FilePerms cannot revoke permission bits: setting a field to a subset of its value is a no-op on int(fp)
FilePerms only ever ORs bits into its integer. Setting a field to something narrower than it was — fp.group = '' after group='r' — updates the attribute (and repr) but leaves the previous bits in int(fp). A caller tightening a file's mode through os.chmod(path, int(fp)) therefore leaves the file exactly as permissive as before, while repr(fp) reports the tightened value.
Reproduction (fresh clone at 967864f, Python 3.13 and 3.14, tempdir):
import os, tempfile
from boltons.fileutils import FilePerms
with tempfile.TemporaryDirectory() as d:
p = os.path.join(d, "s.txt")
open(p, "w").write("secret")
os.chmod(p, 0o644)
fp = FilePerms.from_path(p)
fp.other = ""
fp.group = ""
print(repr(fp)) # FilePerms(user='rw', group='', other='')
print(oct(int(fp))) # 0o644 — expected 0o600
os.chmod(p, int(fp))
print(oct(os.stat(p).st_mode & 0o777)) # 0o644 — the file is still group- and world-readableMechanism — boltons/fileutils.py, FilePerms._FilePermProperty._update_integer (lines 147-153):
def _update_integer(self, fp_obj, value):
mode = 0
key = 'xwr'
for symbol in value:
bit = 2 ** key.index(symbol)
mode |= (bit << (self.offset * 3))
fp_obj._integer |= mode # bits are only ever added; the field's previous bits are never clearedClearing the field's three bits before OR-ing (fp_obj._integer &= ~(0o7 << (self.offset * 3))) makes the integer follow the attributes. It matters most in the one direction people use this class for — revoking access — where the failure is silent and repr says it succeeded.
The existing tests only build up permissions from FilePerms() (int(FilePerms()) == 0, repr after setting fields), so nothing exercises a revoke; a regression test would set a field to a subset and assert on int(fp). The class has behaved this way since it was introduced.
Found by an automated QA pass over the repository (Verdict); the reproduction above was re-run by hand on a fresh clone before filing.
Source: mahmoud/boltons