diff --git a/README.rst b/README.rst index b07d74d..9b363c2 100644 --- a/README.rst +++ b/README.rst @@ -117,7 +117,7 @@ To make sure your cache generation scripts don't race, use the `Lock` class: >>> import portalocker >>> with portalocker.Lock('somefile', timeout=1) as fh: -... print >>fh, 'writing some stuff to my cache...' +... print('writing some stuff to my cache...', file=fh) To customize the opening and locking a manual approach is also possible: diff --git a/portalocker/__about__.py b/portalocker/__about__.py index 0762d9d..17bf0ed 100644 --- a/portalocker/__about__.py +++ b/portalocker/__about__.py @@ -1,7 +1,7 @@ __package_name__ = 'portalocker' __author__ = 'Rick van Hattem' __email__ = 'wolph@wol.ph' -__version__ = '2.5.0' +__version__ = '2.5.1' __description__ = '''Wraps the portalocker recipe for easy usage''' __url__ = 'https://github.com/WoLpH/portalocker' diff --git a/portalocker/__init__.py b/portalocker/__init__.py index 03eaee9..c0edcc1 100644 --- a/portalocker/__init__.py +++ b/portalocker/__init__.py @@ -17,7 +17,7 @@ #: Current author's email address __email__ = __about__.__email__ #: Version number -__version__ = '2.5.0' +__version__ = '2.5.1' #: Package description for Pypi __description__ = __about__.__description__ #: Package homepage diff --git a/portalocker/portalocker.py b/portalocker/portalocker.py index 3be4d66..4bae5e3 100644 --- a/portalocker/portalocker.py +++ b/portalocker/portalocker.py @@ -5,6 +5,12 @@ from . import constants from . import exceptions + +# Alias for readability. Due to import recursion issues we cannot do: +# from .constants import LockFlags +LockFlags = constants.LockFlags + + if os.name == 'nt': # pragma: no cover import msvcrt import pywintypes @@ -15,12 +21,12 @@ __overlapped = pywintypes.OVERLAPPED() - def lock(file_: typing.IO, flags: constants.LockFlags): + def lock(file_: typing.IO, flags: LockFlags): mode = 0 - if flags & constants.LockFlags.NON_BLOCKING: + if flags & LockFlags.NON_BLOCKING: mode |= win32con.LOCKFILE_FAIL_IMMEDIATELY - if flags & constants.LockFlags.EXCLUSIVE: + if flags & LockFlags.EXCLUSIVE: mode |= win32con.LOCKFILE_EXCLUSIVE_LOCK # Save the old position so we can go back to that position but @@ -85,13 +91,20 @@ def unlock(file_: typing.IO): import fcntl - def lock(file_: typing.IO, flags: constants.LockFlags): + def lock(file_: typing.IO, flags: LockFlags): locking_exceptions = IOError, try: # pragma: no cover locking_exceptions += BlockingIOError, # type: ignore except NameError: # pragma: no cover pass + # Locking with NON_BLOCKING without EXCLUSIVE or SHARED enabled results + # in an error + if ((flags & LockFlags.NON_BLOCKING) + and not flags & (LockFlags.SHARED | LockFlags.EXCLUSIVE)): + raise RuntimeError('When locking in non-blocking mode the SHARED ' + 'or EXCLUSIVE flag must be specified as well') + try: fcntl.flock(file_.fileno(), flags) except locking_exceptions as exc_value: @@ -101,7 +114,7 @@ def lock(file_: typing.IO, flags: constants.LockFlags): def unlock(file_: typing.IO, ): - fcntl.flock(file_.fileno(), constants.LockFlags.UNBLOCK) + fcntl.flock(file_.fileno(), LockFlags.UNBLOCK) else: # pragma: no cover raise RuntimeError('PortaLocker only defined for nt and posix platforms') diff --git a/portalocker_tests/tests.py b/portalocker_tests/tests.py index bd3ba51..e41cb03 100644 --- a/portalocker_tests/tests.py +++ b/portalocker_tests/tests.py @@ -1,6 +1,7 @@ from __future__ import print_function from __future__ import with_statement +import os import dataclasses import multiprocessing import time @@ -227,6 +228,14 @@ def test_blocking_timeout(tmpfile): lock.acquire(timeout=5) +@pytest.mark.skipif(os.name == 'nt', + reason='Windows uses an entirely different lockmechanism') +def test_nonblocking(tmpfile): + with open(tmpfile, 'w') as fh: + with pytest.raises(RuntimeError): + portalocker.lock(fh, LockFlags.NON_BLOCKING) + + def shared_lock(filename, **kwargs): with portalocker.Lock( filename, diff --git a/setup.cfg b/setup.cfg index 70e8910..9f4fba1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,5 +6,5 @@ universal = 1 [flake8] ignore = - *.py W391,E303 + *.py W391,E303,W503 docs/*.py ALL