【问题标题】:Better assertEqual() for os.stat(myfile).st_modeos.stat(myfile).st_mode 更好的 assertEqual()
【发布时间】:2015-09-16 11:10:23
【问题描述】:

我有一个检查文件 st_mode 的代码:

self.assertEqual(16877, os.stat(my_directory).st_mode)

只有老派 unix 专家才能流利地破译整数值16877

有没有更易读的方法来准确检查这个值?

【问题讨论】:

  • 也许,取决于你所说的“可读”。您在问题中的代码在幕后隐藏了很多东西。例如:os.stat(my_directory) 返回一个 os.stat_return() 对象,该对象具有多个属性,包括 st_mode。至于揭开16877 的神秘面纱,您可以将值分配给一个适当命名的变量,但这就是您所能做的,因为os.stat_return() 对象的st_mode 属性将始终是一个数值。
  • 您的系统是否设置并检查此状态?你有验证状态的功能吗?你能在断言中使用它吗?您的系统中是否有此状态的名称(例如,仅所有者)?
  • @PeterWood 是的,我的系统设置了这个状态。是的,它检查这个状态。就是上面那行。没有功能可以验证这一点。但是包含上述行的测试。到现在还没有名字。但好主意。国家的名字会更好。谢谢
  • 不客气。关于检查:我的意思不是在你的测试中,我的意思是你的系统检查,例如在做一些操作之前或之后。系统如何引用它?我知道它是 0o4075516877,但给它一个 ubiquitous name 会对每个人都有帮助。
  • @PeterWood 到目前为止,我将其称为“默认 umask”。

标签: python chmod readability stat


【解决方案1】:

您可以使用已定义的常量。它们可以在stat (docs) 中找到。他们是:

stat.S_IRUSR 获取所有者读取权限,

stat.S_IWUSR 为所有者写权限,

stat.S_IXUSR 为所有者执行权限,

stat.S_IRGRP 为群组读取权限,

stat.S_IWGRP 为组写权限,

stat.S_IXGRP 为组执行权限,

stat.S_IROTH 为他人读取权限,

stat.S_IWOTH为他人写权限,

stat.S_IXOTH为他人执行权限,

stat.S_IFDIR 用于目录。

可以使用按位或| 组合它们。然后你的代码看起来像:

import stat
import os

permissions = (stat.S_IFDIR | 
               stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
               stat.S_IRGRP | stat.S_IXGRP |
               stat.S_IROTH | stat.S_IXOTH)
self.assertEqual(permissions, os.stat(my_directory).st_mode)

【讨论】:

    【解决方案2】:

    Here 的一些关于st_mode 的信息来自stat 的手册,引用如下。

           S_IFMT     0170000   bit mask for the file type bit field
           S_IFSOCK   0140000   socket
           S_IFLNK    0120000   symbolic link
           S_IFREG    0100000   regular file
           S_IFBLK    0060000   block device
           S_IFDIR    0040000   directory
           S_IFCHR    0020000   character device
           S_IFIFO    0010000   FIFO
           S_ISUID      04000   set-user-ID bit
           S_ISGID      02000   set-group-ID bit (see below)
           S_ISVTX      01000   sticky bit (see below)
           S_IRWXU      00700   owner has read, write, and execute permission
           S_IRUSR      00400   owner has read permission
           S_IWUSR      00200   owner has write permission
           S_IXUSR      00100   owner has execute permission
           S_IRWXG      00070   group has read, write, and execute permission
           S_IRGRP      00040   group has read permission
           S_IWGRP      00020   group has write permission
           S_IXGRP      00010   group has execute permission
           S_IRWXO      00007   others (not in group) have read, write, and
                                execute permission
           S_IROTH      00004   others have read permission
           S_IWOTH      00002   others have write permission
           S_IXOTH      00001   others have execute permission
    

    这些都是八进制数。

    oct(16877) 等价于0o40755。这意味着以下位处于活动状态:

    • 40000,或S_IFDIR:目录
    • 700,表示所有者可以读、写和执行
    • 50,表示该组可以读取和执行,但不能写入。
    • 5,表示世界可以读和执行,但不能写。

    755是目录的模式。)

    您可能会做的一些事情:

    1. 在文档中链接stat 联机帮助页,或添加我粘贴的代码。然后,您可以使用

      self.assertEqual(0o40755, os.stat(my_directory).st_mode)
      

      相反,意思完全一样,但调试起来更容易。

    2. 您可以使用ctypes 模块自己实现位域。 python wiki 有更多信息。 (搜索“位域”。)
    3. 您可以将 16877 或 0o40755(以及其他输出)作为常量变量的值,可能是全局变量。

      DIR_755 = 0o40755
      

      那么,你可以这样做:

      self.assertEqual(DIR_755, os.stat(my_directory).st_mode)
      

      检查模式是否正确。

    4. 您可以制作代码和值的字典:

      st_mode_vals = {
          "S_IFMT": 0170000,  # Or instead, "filetype_bitmask"
          "S_IFSOCK": 0140000,  # Or instead, "socket"
          ...
          "S_IXOTH": 0o1  # Or instead, "other_x"
      }
      

      然后您可以将它们定义为:

      DIR_755 = st_mode_vals["directory"] +\
                st_mode_vals["usr_rwx"] +\
                st_mode_vals["grp_r"] + st_mode_vals["grp_x"] +\
                st_mode_vals["oth_r"] + st_mode_vals["oth_x"]
      self.assertEqual(DIR_755, os.stat(my_directory).st_mode)
      

    我个人会使用#1(文档链接),如果某些代码经常被重用,我会使用#3。对于所有这些,也许您也可以添加注释以指示十进制值?

    编辑:我没有看到在发布之前添加的答案。我不知道stat 模块做了什么,所以我没有想到。无论哪种方式,我想我仍然会使用#1,也许是#3。然而,他写的内容肯定会取代 #4(字典)作为另一种选择,而且确实会好得多。

    【讨论】:

    • 感谢您提供的意见和选项。不过,我想分享一些关于它的想法。通常使用库常量比定义你的方法更好。原因如下:首先,为什么要重新定义已经存在的东西?其次,如果有什么变化(假设新版本的 unix 有新的权限常量,或者 os.stat 开始使用自己的格式,而不是 unix 格式),那么对于系统/库常量,代码仍然可以工作,但如果你使用您自己的常量,那么您将不得不在代码中更改它们,或者更糟糕的是,您会错过它并且很难找到错误或安全漏洞。
    • 这是我没有考虑过的。我认为选项#1 是最好的,因为在我看来,与 S_IFDIR 等相比,它对大多数程序员来说是最容易理解的。这也是我在字典中给出替代名称的原因。但是我看到了您在可维护性方面的观点,并承认了这一点。谢谢。
    【解决方案3】:

    如果我可以稍微扩展一下这个问题并将其理解为“是否有更易读的方法来检查文件模式?”,那么我会建议添加一个自定义断言。目标:

    self.assertFileMode(my_directory, user="rwx", group="rx", others="rx")
    

    怎么做。

    让我们把这个断言放在一个 mixin 中:

    import os
    import stat
    
    class FileAssertions(object):
        FILE_PERMS = {
            'user': {'r': stat.S_IRUSR, 'w': stat.S_IWUSR, 'x': stat.S_IXUSR, 's': stat.S_ISUID},
            'group': {'r': stat.S_IRGRP, 'w': stat.S_IWGRP, 'x': stat.S_IXGRP, 's': stat.S_ISGID},
            'others': {'r': stat.S_IROTH, 'w': stat.S_IWOTH, 'x': stat.S_IXOTH},
        }
    
        def assertFileMode(self, path, **kwargs):
            mode = os.stat(path).st_mode
            for key, perm_defs in self.FILE_PERMS.items():
                expected = kwargs.pop(key, None)
                if expected is not None:
                    actual_perms = mode & sum(perm_defs.values())
                    expected_perms = sum(perm_defs[flag] for flag in expected)
    
                    if actual_perms != expected_perms:
                        msg = '{key} permissions: {expected} != {actual} for {path}'.format(
                            key=key, path=path,
                            expected=''.join(sorted(expected)),
                            actual=''.join(sorted(flag for flag, value in perm_defs.items()
                                                  if value & mode != 0))
                        )
                        raise self.failureException(msg)
            if kwargs:
                raise TypeError('assertFileMode: unknown arguments %s' % ', '.join(kwargs))
    

    使用它

    现在,我们来测试一些文件模式如何?

    # We use our mixin
    class MyTestCase(FileAssertions, TestCase):
        def test_some_paths(self):
            # Test all permissions
            self.assertFileMode('/foo/bar', user='rwx', group='rx', others='')
    
            # Only test user permissions
            self.assertFileMode('/foo/bar', user='rwx')
    
            # We support the suid/sgid bits as well
            self.assertFileMode('/foo/bar', user='rwxs', group='rxs', others='rx')
    

    示例输出:

    AssertionError: user permissions: rw != rwx for /foo/bar
    

    注意事项:

    • 仅测试授予该方法的权限。要测试不存在权限,请传递一个空字符串。
    • 大部分复杂性来自生成用户友好的消息。
    • 权限在错误消息中按字母顺序排序,因此更易于目视比较。
    • 为简单起见,我没有处理 sticky bit 的测试。

    【讨论】:

    • 是的,这是一个很好的可重复使用的解决方案。将它作为 pip 可安装库会很好。
    • 谢谢。我相信python包确实有一些空间可以添加一堆有用的——我可以说是语义的——断言,特别是关于文件操作和内容、临时目录……通过适当的文档和彻底的测试来正确地处理它太大了不过目前为自己努力。
    • 顺便说一下,我特此将我的答案中的代码放在公共领域。用它做任何你想做的事。信用赞赏,但不是强制性的。
    【解决方案4】:
    self.assertEqual(16877, os.stat(my_directory).st_mode)
    

    只有老派 unix 专家才能流利地破译整数值 16877。

    实际上,老派的 unix 专家可能会在同一条船上,因为这些东西是用八进制显示的。

    我愿意:

    • 切换到八进制来指定模式
    • 添加文本表示和注释

    类似这样的:

    # 'my_directory' should be a directory with full permissions for user and
    # read/execute permissions for group and other
    # drwxr-xr-x
    self.assertEqual(0o40755, os.stat(my_directory).st_mode)
    

    【讨论】:

      猜你喜欢
      • 2020-07-15
      • 1970-01-01
      • 1970-01-01
      • 2019-03-21
      • 1970-01-01
      • 1970-01-01
      • 2013-01-05
      • 2022-01-03
      • 2015-10-22
      相关资源
      最近更新 更多