45

How can I in python (3) create a file what others users can write as well. I've so far this but it changes the

os.chmod("/home/pi/test/relaxbank1.txt", 777)
with open("/home/pi/test/relaxbank1.txt", "w") as fh:
    fh.write(p1)  

what I get

---sr-S--t 1 root root 12 Apr 20 13:21 relaxbank1.txt

expected (after doing in commandline $ sudo chmod 777 relaxbank1.txt )

-rwxrwxrwx 1 root root 12 Apr 20 13:21 relaxbank1.txt

Richard de Ree
  • 2,329
  • 4
  • 30
  • 47
  • 2
    What happens if you put the `chmod` after the `with` block? – Kevin Apr 20 '16 at 13:38
  • 2
    The permission number is octal, try `0777`! – Klaus D. Apr 20 '16 at 13:45
  • os.chmod("/home/pi/test/relaxbank1.txt", 0777) ^ SyntaxError: invalid token 0777 gives an error – Richard de Ree Apr 20 '16 at 15:32
  • Klaus D. still lives in the last century. The correct synthax is 0o777. But you could also write 511, 0x1ff or 0b111111111, as all these literals resolve to the same integer. An integer object is the pure representation of a value, having no special format as long as you don't write it. – Bachsau Nov 19 '18 at 20:22

5 Answers5

108

If you don't want to use os.chmod and prefer to have the file created with appropriate permissions, then you may use os.open to create the appropriate file descriptor and then open the descriptor:

import os

# The default umask is 0o22 which turns off write permission of group and others
os.umask(0)

descriptor = os.open(
    path='filepath',
    flags=(
        os.O_WRONLY  # access mode: write only
        | os.O_CREAT  # create if not exists
        | os.O_TRUNC  # truncate the file to zero
    ),
    mode=0o777
)

with open(descriptor, 'w') as fh:
    fh.write('some text')
    # the descriptor is automatically closed when fh is closed

Using a custom opener will make things easier and less error-prone. open will generate the appropriate flags for our opener according to the requested mode (w):

import os

os.umask(0)


def opener(path, flags):
    return os.open(path, flags, 0o777)


with open('filepath', 'w', opener=opener) as fh:
    fh.write('some text')

Python 2 Note:

The built-in open() in Python 2.x doesn't support opening by file descriptor. Use os.fdopen instead; otherwise you'll get:

TypeError: coercing to Unicode: need string or buffer, int found.
AXO
  • 8,198
  • 6
  • 62
  • 63
  • 33
    This is a better approach than the accepted answer. When you need to create a file with more restrictive permissions than the default umask, updating permissions after the file is created is a security bug. – Andrew Palmer Dec 04 '17 at 14:13
  • 1
    ERROR: "TypeError: coercing to Unicode: need string or buffer, int found" – Peter Krauss Dec 03 '18 at 18:59
  • 3
    This solution is more elegant and aesthetic. – Michael A. Feb 03 '20 at 19:49
  • 1
    This is the better option, and I changed this to the accepted answer. Thank you for your effort to share this with us. – Richard de Ree Jun 10 '21 at 08:42
  • This assumes that the process is not multi-threaded. This is not any safer in a mtli-threaded application, since there is no guarantee that the umask will be active when the file access occurs. – Christopher Jul 09 '21 at 13:09
  • 2
    There is one other "trap" in this code sample; one might assume `os.O_CREAT | os.O_WRONLY` are the flags that `open('filepath', 'w')` would normally set, but an important flag `O_TRUNC` is missing here. If you use this code to (sometimes) write a file that already exists, and the new content you are writing is shorter than the original content, then this code sample will leave garbage at the end of the file, in a way that I believe would be surprising to most non-experts, like me, and is inconsistent with the default python behavior. – Tao Aug 11 '22 at 14:16
  • @Tao: You are totally right. I've updated my answer. The new suggestion is to use `opener` when possible to avoid such issues. – AXO Sep 07 '22 at 15:34
31

The problem is your call to open() recreates the call. Either you need to move the chmod() to after you close the file, OR change the file mode to w+.

Option1:

with open("/home/pi/test/relaxbank1.txt", "w+") as fh:
    fh.write(p1)
os.chmod("/home/pi/test/relaxbank1.txt", 0o777)

Option2:

os.chmod("/home/pi/test/relaxbank1.txt", 0o777)
with open("/home/pi/test/relaxbank1.txt", "w+") as fh:
    fh.write(p1)

Comment: Option1 is slightly better as it handles the condition where the file may not already exist (in which case the os.chmod() will throw an exception).

user590028
  • 11,364
  • 3
  • 40
  • 57
  • The suggested "0777" gives an error: os.chmod("/home/pi/test richard/relaxbank1.txt",0777) Error message: SyntaxError: invalid token However "777" don't give an error but change the permission in -r----x--t 1 root root 12 Apr 20 15:37 relaxbank1.txt ^ – Richard de Ree Apr 20 '16 at 15:50
  • 1
    My bad -- correct octal notation is `0o777` (zero, letter 'o', 777) – user590028 Apr 20 '16 at 15:50
  • 2
    what does the `0o` in the `0o777` do/mean? – Jeff Oct 18 '18 at 17:28
  • 1
    ah, I see, for those who are wondering about the leading `0o` in the `0o777` it is to make the number octal see [this post](https://stackoverflow.com/questions/15607903/python-module-os-chmodfile-664-does-not-change-the-permission-to-rw-rw-r-bu) – Jeff Oct 18 '18 at 17:35
  • 7
    This answer may be **dangerous** for future readers. It is only secure because the OP is asking to change the permissions to be **less restrictive**. Future readers be-ware this is not secure if you want to make the permissions *more restrictive*. That's because there is time between the file being created and the permissions being changed. So a malicious agent may be able to open the file before the permissions change and keep it open. See here for a secure solution: https://stackoverflow.com/a/45368120/453851 – Philip Couling Jan 09 '20 at 14:12
11

Similar to AXO's idea where it's preferable to have the file created with the correct permissions, the opener argument of open() seems quite handy for this purpose.

import os

def opener(path, flags):
    return os.open(path, flags, 0o777)

os.umask(0)  # Without this, the created file will have 0o777 - 0o022 (default umask) = 0o755 permissions
with open("myfile.txt", "w", opener=opener) as f:
    f.write("myline")

From the documentation for opener:

A custom opener can be used by passing a callable as opener. The underlying file descriptor for the file object is then obtained by calling opener with (file, flags). opener must return an open file descriptor (passing os.open as opener results in functionality similar to passing None).

Ivan Gozali
  • 2,089
  • 1
  • 27
  • 25
4

This is a robust method

#!/usr/bin/env python3
import stat
import os
path = 'outfile.txt'
with open(path, 'w') as fh:
    fh.write('blabla\n')
st = os.stat(path)
os.chmod(path, st.st_mode | stat.S_IWOTH)

See how:

See also: Write file with specific permissions in Python

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
0

In addition to providing write permission to others: OTH, you can also add write permissions for group: GRP. Note what hierarchy you want to assign what permissions to while writing a file.

st = os.stat(path)
os.chmod(path, st.st_mode | stat.S_IWOTH | stat.S_IWGRP)