问题
I have a code that checks the st_mode of a file:
self.assertEqual(16877, os.stat(my_directory).st_mode)
Only old school unix experts are able to decipher the integer value 16877
fluently.
Is there more readable way to check for exactly this value?
回答1:
If I may extend the question a bit and understand it as “Is there more readable way to check file modes?”, then I'll suggest adding a custom assertion. The target:
self.assertFileMode(my_directory, user="rwx", group="rx", others="rx")
How to do it.
Let's put that assertion in a 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))
Using it
Now, how about we test some file modes?
# 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')
Example output:
AssertionError: user permissions: rw != rwx for /foo/bar
Notes:
- Only permissions given to the method are tested. To test that no permissions exist, pass an empty string.
- Most of the complexity comes from generating a user-friendly message.
- Permissions are sorted alphabetically in the error messages, so they are easier to eyeball-compare.
- To keep it simple, I did not handle testing the sticky bit.
回答2:
You can use defined constats. They can be found in stat
(docs). They are:
stat.S_IRUSR
for owner read permission,
stat.S_IWUSR
for owner write permission,
stat.S_IXUSR
for owner execute permission,
stat.S_IRGRP
for group read permission,
stat.S_IWGRP
for group write permission,
stat.S_IXGRP
for group execute permission,
stat.S_IROTH
for others read permission,
stat.S_IWOTH
for others write permission,
stat.S_IXOTH
for others execute permission,
stat.S_IFDIR
for directory.
They can be combined using bitwise or |
. Then your code can look like:
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)
回答3:
Here's some information about st_mode
from the manual for stat
, quoted below.
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
These are all octal numbers.
oct(16877)
is equivalent to 0o40755
. This means that the following bits are active:
40000
, orS_IFDIR
: directory700
, which means that owner can read, write, and execute50
, which means that the group can read and execute, but not write.5
, which means that the world can read and execute, but not write.
(755
is the mode of the directory.)
Some things you might be able to do:
Link the
stat
manpage in the documentation, or add the codes I pasted. Then, you can useself.assertEqual(0o40755, os.stat(my_directory).st_mode)
instead, which means exactly the same thing, but it becomes easier to debug.
- You could use the
ctypes
module to implement the bitfield yourself. The python wiki has more information. (Search for "Bit field".) You could put 16877 or 0o40755 (and the other outputs) as the value of a constant variable, perhaps global.
DIR_755 = 0o40755
Then, you could do:
self.assertEqual(DIR_755, os.stat(my_directory).st_mode)
to check that the mode is correct.
You could make a dictionary of the codes and values:
st_mode_vals = { "S_IFMT": 0170000, # Or instead, "filetype_bitmask" "S_IFSOCK": 0140000, # Or instead, "socket" ... "S_IXOTH": 0o1 # Or instead, "other_x" }
You would then be able to define them as:
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)
I would personally use #1 (link to documentation), with #3 if there are certain codes that are reused often. For all of them, maybe you can add a comment indicating the value in decimal, too?
edit: I did not see the answer that was added before I posted. I had no idea what the stat
module did, so I didn't think of it. Either way, I think I would still use #1 and maybe #3. What he wrote, however, would definitely replace #4 (the dictionary) as another option, and would indeed be much better.
回答4:
self.assertEqual(16877, os.stat(my_directory).st_mode)
Only old school unix experts are able to decipher the integer value 16877 fluently.
Actually, old-school unix experts would probably be in the same boat, since such things are displayed in octal.
I would:
- switch to octal for specifying the mode
- add the text representation and a comment
Something like this:
# '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)
来源:https://stackoverflow.com/questions/32606954/better-assertequal-for-os-statmyfile-st-mode