Skip to content

Commit

Permalink
feat(stickybits): fix sticky bits and add inheritable option
Browse files Browse the repository at this point in the history
  • Loading branch information
shinny-taojiachun committed Sep 2, 2024
1 parent c438609 commit 5062871
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fail-fast: false
matrix:
python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
platform: [ubuntu-latest, macos-latest]
platform: [ubuntu-latest, macos-13] # intel mac only
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
Expand Down
17 changes: 12 additions & 5 deletions src/shinny_filelock/_flockd.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@


@contextmanager
def flocked(path, blocking=True, create_file=False, shared=False):
def flocked(path, blocking=True, create_file=False, shared=False, inheritable=False):
"""
:param path: file path
:param blocking: blocking lock, default True
:param create_file: create file if not exists, default False
:param shared: shared lock, default False
:param inheritable: inherit lock after exec, default False
"""
fd = -1
fd_flags = os.O_RDONLY | os.O_NOCTTY
fd_flags_with_create = fd_flags | os.O_CREAT
try:
fd_flags = os.O_RDONLY | os.O_NOCTTY
if create_file:
fd_flags |= os.O_CREAT
fd = os.open(path, fd_flags)
# when stick bits set, os.O_CREAT will raise PermissionError
try:
fd = os.open(path, fd_flags_with_create if create_file else fd_flags)
except PermissionError:
# maybe file exists and sits in a directory with sticky bits
fd = os.open(path, fd_flags)
if fd == -1:
raise ValueError()
# default fd in non-inheritable. See PEP 446: https://peps.python.org/pep-0446/
os.set_inheritable(fd, inheritable)
flags = fcntl.LOCK_SH if shared else fcntl.LOCK_EX
if not blocking:
flags |= fcntl.LOCK_NB
Expand Down
21 changes: 14 additions & 7 deletions tests/test_flock.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# the inclusion of the tests module is not meant to offer best practices for
# testing in general, but rather to support the `find_packages` example in
# setup.py that excludes installing the "tests" package
import subprocess
import unittest

from shinny_filelock import flocked

from ._timeout import timeout

local_path = "/tmp/shinny-filelock-test.lock"


class TestSimple(unittest.TestCase):

def setUp(self):
self.addCleanup(subprocess.check_call, ["sudo", "rm", "-f", local_path])

def test_non_blocking_lock(self):
local_path = "/tmp/test.lock"
# local_path = "/tmp/test.lock"
with flocked(local_path, blocking=False, create_file=True):
try:
with flocked(local_path, blocking=False, create_file=True):
Expand All @@ -20,7 +23,6 @@ def test_non_blocking_lock(self):
self.assertEqual(e.__class__, BlockingIOError)

def test_blocking_lock(self):
local_path = "/tmp/test-block.lock"
with flocked(local_path, blocking=True, create_file=True):
try:
with timeout(2):
Expand All @@ -30,7 +32,6 @@ def test_blocking_lock(self):
self.assertEqual(e.__class__, TimeoutError)

def test_blocking_with_non_blocking_lock(self):
local_path = "/tmp/test-mix.lock"
with flocked(local_path, blocking=True, create_file=True):
try:
with flocked(local_path, blocking=False, create_file=True):
Expand All @@ -39,13 +40,19 @@ def test_blocking_with_non_blocking_lock(self):
self.assertEqual(e.__class__, BlockingIOError)

def test_file_not_exists(self):
local_path = "/tmp/test-not-exists.lock"
try:
with flocked(local_path, blocking=True):
pass
except Exception as e:
self.assertEqual(e.__class__, FileNotFoundError)

def test_lock_with_file_created_by_another_user(self):
# use sudo to create a file with sticky bits
with open(local_path, "w"):
subprocess.check_call(["sudo", "chown", "2222:3333", local_path])
with flocked(local_path, blocking=True, create_file=True):
pass


if __name__ == '__main__':
unittest.main()
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ commands =
[flake8]
exclude = .tox,*.egg,build,data
select = E,W,F
max-line-length = 120

0 comments on commit 5062871

Please sign in to comment.