Python module os.chmod(file, 664) does not change the permission to rw-rw-r-- but -w--wx----

PythonFilePermissionsChmod

Python Problem Overview


Recently I am using Python module os, when I tried to change the permission of a file, I did not get the expected result. For example, I intended to change the permission to rw-rw-r--,

os.chmod("/tmp/test_file", 664)

The ownership permission is actually -w--wx--- (230)

--w--wx--- 1 ag ag 0 Mar 25 05:45 test_file

However, if I change 664 to 0664 in the code, the result is just what I need, e.g.

os.chmod("/tmp/test_file", 0664)

The result is:

-rw-rw-r-- 1 ag ag 0 Mar 25 05:55 test_file

Could anybody help explaining why does that leading 0 is so important to get the correct result?

Python Solutions


Solution 1 - Python

Found this on a different forum

> If you're wondering why that leading zero is important, it's because > permissions are set as an octal integer, and Python automagically > treats any integer with a leading zero as octal. So os.chmod("file", > 484) (in decimal) would give the same result.

What you are doing is passing 664 which in octal is 1230

In your case you would need

os.chmod("/tmp/test_file", 436)

[Update] Note, for Python 3 you have prefix with 0o (zero oh). E.G, 0o666

Solution 2 - Python

So for people who want semantics similar to:

$ chmod 755 somefile

Use:

$ python -c "import os; os.chmod('somefile', 0o755)"

If your Python is older than 2.6:

$ python -c "import os; os.chmod('somefile', 0755)"

Solution 3 - Python

Use permission symbols (stat.S_I*) instead of raw octal numbers

Your problem would have been avoided if you had used the more semantically named permission symbols rather than raw magic numbers, e.g. for 664:

#!/usr/bin/env python3

import os
import stat

os.chmod(
    'myfile',
    stat.S_IRUSR |
    stat.S_IWUSR |
    stat.S_IRGRP |
    stat.S_IWGRP |
    stat.S_IROTH
)

This is documented at https://docs.python.org/3/library/os.html#os.chmod and the names are the same as the POSIX C API which is also present at man 2 stat and man 2 chmod:

S_IRUSR  (00400)  read by owner
S_IWUSR  (00200)  write by owner
S_IXUSR  (00100)  execute/search by owner
S_IRGRP  (00040)  read by group
S_IWGRP  (00020)  write by group
S_IXGRP  (00010)  execute/search by group
S_IROTH  (00004)  read by others
S_IWOTH  (00002)  write by others
S_IXOTH  (00001)  execute/search by others

Another advantage is the greater portability as mentioned in the docs:

> Note: Although Windows supports chmod(), you can only set the file’s read-only flag with it (via the stat.S_IWRITE and stat.S_IREAD constants or a corresponding integer value). All other bits are ignored.

chmod +x is demonstrated at: https://stackoverflow.com/questions/12791997/how-do-you-do-a-simple-chmod-x-from-within-python/55591471#55591471

Tested in Ubuntu 16.04, Python 3.5.2.

Solution 4 - Python

leading 0 means this is octal constant, not the decimal one. and you need an octal to change file mode.

permissions are a bit mask, for example, rwxrwx--- is 111111000 in binary, and it's very easy to group bits by 3 to convert to the octal, than calculate the decimal representation.

0644 (octal) is 0.110.100.100 in binary (i've added dots for readability), or, as you may calculate, 420 in decimal.

Solution 5 - Python

If you have desired permissions saved to string then do

s = '660'
os.chmod(file_path, int(s, base=8))

Solution 6 - Python

@mc.dev's answer was the best answer here I ended up leveraging that to make the below function wrapper for reuse. Thanks for the share.

def chmod_digit(file_path, perms):
    """
    Helper function to chmod like you would in unix without having to preface 0o or converting to octal yourself.
    Credits: https://stackoverflow.com/a/60052847/1621381
    """
    os.chmod(file_path, int(str(perms), base=8))

Solution 7 - Python

Using the stat.* bit masks does seem to me the most portable and explicit way of doing this. But on the other hand, I often forget how best to handle that. So, here's an example of masking out the 'group' and 'other' permissions and leaving 'owner' permissions untouched. Using bitmasks and subtraction is a useful pattern.

import os
import stat
def chmodme(pn):
    """Removes 'group' and 'other' perms. Doesn't touch 'owner' perms."""
    mode = os.stat(pn).st_mode
    mode -= (mode & (stat.S_IRWXG | stat.S_IRWXO))
    os.chmod(pn, mode)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAplusGView Question on Stackoverflow
Solution 1 - PythonRedBaronView Answer on Stackoverflow
Solution 2 - PythonDimaView Answer on Stackoverflow
Solution 3 - PythonCiro Santilli Путлер Капут 六四事View Answer on Stackoverflow
Solution 4 - PythonlenikView Answer on Stackoverflow
Solution 5 - Pythonmc.devView Answer on Stackoverflow
Solution 6 - PythonMik RView Answer on Stackoverflow
Solution 7 - PythonJason DrewView Answer on Stackoverflow