Issue
I try to set file access rights in Python in os.chmod
in Windows.
I can succeed removing write access, but not read access:
>>> import os
>>> import stat
>>> from pathlib import Path
>>> toto = Path("toto.txt")
>>> toto.write_text("Hello")
5
>>> toto.read_text()
'Hello'
>>> os.chmod(toto, toto.stat().st_mode & ~stat.S_IWUSR)
>>> toto.write_text("Hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\python37-32\lib\pathlib.py", line 1208, in write_text
with self.open(mode='w', encoding=encoding, errors=errors) as f:
File "c:\python37-32\lib\pathlib.py", line 1176, in open
opener=self._opener)
File "c:\python37-32\lib\pathlib.py", line 1030, in _opener
return self._accessor.open(self, flags, mode)
PermissionError: [Errno 13] Permission denied: 'toto.txt'
[Errno 13] Permission denied: 'toto.txt'
>>> os.chmod(toto, stat.S_IRWXU)
>>> toto.write_text("Hello")
5
>>> os.chmod(toto, toto.stat().st_mode & ~stat.S_IRUSR)
>>> toto.read_text()
'Hello'
The last line should have raised an error because the file should have no rights to be read.
How to solve the issue?
Solution
The best way to set access rights in Windows is to use icacls
.
This code could help with that:
"""
Example of script to modify access rights with "icacls" in Windows
"""
from contextlib import contextmanager
from enum import Enum
from subprocess import check_output
from pathlib import Path
from typing import Generator, List
class AccessRight(Enum):
"""Access Rights for files/folders"""
DELETE = "D"
FULL = "F" # Edit Permissions + Create + Delete + Read + Write
NO_ACCESS = "N"
MODIFY = "M" # Create + Delete + Read + Write
READ_EXECUTE = "RX"
READ_ONLY = "R"
WRITE_ONLY = "W"
def change_access_rights(file: Path, access: AccessRight) -> None:
"""Change Access Rights of a given file"""
def cmd(access_right: AccessRight, mode="grant:r") -> List[str]:
return [
"icacls",
str(file),
"/inheritance:r",
f"/{mode}",
f"Everyone:{access_right.value}",
]
if access == AccessRight.NO_ACCESS:
check_output(cmd(AccessRight.FULL, mode="deny"))
else:
check_output(cmd(access))
@contextmanager
def set_access_right(file: Path, access: AccessRight) -> Generator[Path, None, None]:
"""Context Manager to temporarily set a given access rights to a file and reset"""
try:
change_access_rights(file, access)
yield file
finally:
change_access_rights(file, AccessRight.FULL)
# We create a file (if it does not exist) with empty content
toto = Path("toto.txt")
toto.touch()
# We temporarily set access rights of the
with set_access_right(toto, AccessRight.WRITE_ONLY) as path:
path.write_text("My name is Toto")
try:
content = path.read_text()
print(f":( Should not be able to read content of file but read: {content}")
except PermissionError:
print("Cannot read toto: YEAH!")
# We check that access rights have been restored
print(path.read_text())
change_access_rights(toto, AccessRight.NO_ACCESS)
try:
toto.write_text("My name is Toto")
print(f":( Should not be able to write in file")
except PermissionError:
print("NO ACCESS to toto: YEAH!")
# We check that access rights have been restored (and delete the file to stay clean)
change_access_rights(toto, AccessRight.FULL)
toto.write_text("The end...")
print(toto.read_text())
change_access_rights(toto, AccessRight.DELETE)
toto.unlink()
Answered By - Jean-Francois T. Answer Checked By - Candace Johnson (WPSolving Volunteer)