diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb7572c..1845b9e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,15 +4,11 @@ repos: hooks: - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.2.0" + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 hooks: - - id: ruff - args: [--fix, --exit-non-zero-on-fix] - - repo: https://github.com/psf/black - rev: 24.1.1 - hooks: - - id: black + - id: codespell + args: ["--write-changes"] - repo: https://github.com/tox-dev/tox-ini-fmt rev: "1.3.1" hooks: @@ -22,7 +18,13 @@ repos: rev: "1.7.0" hooks: - id: pyproject-fmt - additional_dependencies: ["tox>=4.11.4"] + additional_dependencies: ["tox>=4.12.1"] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.2.1" + hooks: + - id: ruff-format + - id: ruff + args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"] - repo: meta hooks: - id: check-hooks-apply diff --git a/docs/changelog.rst b/docs/changelog.rst index e398553..9fd4d79 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,7 +2,7 @@ Changelog ========= v3.12.0 (2023-04-18) -------------------- -- Make the thread local behaviour something the caller can enable/disable via a flag during the lock creation, it's on +- Make the thread local behavior something the caller can enable/disable via a flag during the lock creation, it's on by default. - Better error handling on Windows. diff --git a/docs/conf.py b/docs/conf.py index 60996b3..dcdfc8b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,6 @@ # noqa: INP001 """Configuration for Sphinx.""" + from __future__ import annotations from datetime import datetime, timezone @@ -50,10 +51,11 @@ def setup(app: Sphinx) -> None: Setup app. :param app: the app + """ class PatchedPythonDomain(PythonDomain): - def resolve_xref( # noqa: PLR0913 + def resolve_xref( # noqa: PLR0913, PLR0917 self, env: BuildEnvironment, fromdocname: str, diff --git a/pyproject.toml b/pyproject.toml index 4e79624..a9a028c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,33 +65,43 @@ build.hooks.vcs.version-file = "src/filelock/version.py" build.targets.sdist.include = ["/src", "/tests", "/tox.ini"] version.source = "vcs" -[tool.black] -line-length = 120 - [tool.ruff] -select = ["ALL"] line-length = 120 target-version = "py38" -isort = {known-first-party = ["filelock"], required-imports = ["from __future__ import annotations"]} -ignore = [ - "ANN101", # Missing type annotation for `self` in method - "D301", # Use `r"""` if any backslashes in a docstring - "D205", # 1 blank line required between summary line and description - "D401", # First line of docstring should be in imperative mood - "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible - "D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible - "S104", # Possible binding to all interface +lint.isort = { known-first-party = ["filelock"], required-imports = ["from __future__ import annotations"] } +lint.select = ["ALL"] +lint.ignore = [ + "ANN101", # Missing type annotation for `self` in method + "D301", # Use `r"""` if any backslashes in a docstring + "D205", # 1 blank line required between summary line and description + "D401", # First line of docstring should be in imperative mood + "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible + "D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible + "S104", # Possible binding to all interface + "COM812", # Conflict with formatter + "ISC001", # Conflict with formatter + "CPY", # No copyright statements ] -[tool.ruff.per-file-ignores] +lint.preview = true +format.preview = true +format.docstring-code-format = true +format.docstring-code-line-length = 100 +[tool.ruff.lint.per-file-ignores] "tests/**/*.py" = [ - "S101", # asserts allowed in tests... - "FBT", # don"t care about booleans as positional arguments in tests - "INP001", # no implicit namespace - "D", # don"t care about documentation in tests - "S603", # `subprocess` call: check for execution of untrusted input - "PLR2004", # Magic value used in comparison, consider replacing with a constant variable + "S101", # asserts allowed in tests... + "FBT", # don"t care about booleans as positional arguments in tests + "INP001", # no implicit namespace + "D", # don"t care about documentation in tests + "S603", # `subprocess` call: check for execution of untrusted input + "PLR2004", # Magic value used in comparison, consider replacing with a constant variable ] +[tool.codespell] +builtin = "clear,usage,en-GB_to_en-US" +count = true +quiet-level = 3 +ignore-words-list = "master" + [tool.coverage] html.show_contexts = true html.skip_covered = false diff --git a/src/filelock/__init__.py b/src/filelock/__init__.py index 27ae403..006299d 100644 --- a/src/filelock/__init__.py +++ b/src/filelock/__init__.py @@ -41,12 +41,12 @@ __all__ = [ - "__version__", + "AcquireReturnProxy", + "BaseFileLock", "FileLock", "SoftFileLock", "Timeout", "UnixFileLock", "WindowsFileLock", - "BaseFileLock", - "AcquireReturnProxy", + "__version__", ] diff --git a/src/filelock/_api.py b/src/filelock/_api.py index 2e9cdba..49eaf1e 100644 --- a/src/filelock/_api.py +++ b/src/filelock/_api.py @@ -30,7 +30,7 @@ # is not called twice when entering the with statement. If we would simply return *self*, the lock would be acquired # again in the *__enter__* method of the BaseFileLock, but not released again automatically. issue #37 (memory leak) class AcquireReturnProxy: - """A context aware object that will release the lock file when exiting.""" + """A context-aware object that will release the lock file when exiting.""" def __init__(self, lock: BaseFileLock) -> None: self.lock = lock @@ -115,13 +115,14 @@ def __init__( # noqa: PLR0913 :param lock_file: path to the file :param timeout: default timeout when acquiring the lock, in seconds. It will be used as fallback value in \ the acquire method, if no timeout value (``None``) is given. If you want to disable the timeout, set it \ - to a negative value. A timeout of 0 means, that there is exactly one attempt to acquire the file lock. + to a negative value. A timeout of 0 means that there is exactly one attempt to acquire the file lock. :param mode: file permissions for the lockfile :param thread_local: Whether this object's internal context should be thread local or not. If this is set to \ ``False`` then the lock will be reentrant across threads. :param is_singleton: If this is set to ``True`` then only one instance of this class will be created \ per lock file. This is useful if you want to use the lock object for reentrant locking without needing \ to pass the same object around. + """ self._is_thread_local = thread_local self._is_singleton = is_singleton @@ -164,6 +165,7 @@ def timeout(self, value: float | str) -> None: Change the default timeout value. :param value: the new value, in seconds + """ self._context.timeout = float(value) @@ -272,10 +274,11 @@ def acquire( def release(self, force: bool = False) -> None: # noqa: FBT001, FBT002 """ - Releases the file lock. Please note, that the lock is only completely released, if the lock counter is 0. Also - note, that the lock file itself is not automatically deleted. + Releases the file lock. Please note, that the lock is only completely released, if the lock counter is 0. + Also note, that the lock file itself is not automatically deleted. :param force: If true, the lock counter is ignored and the lock is released in every case/ + """ if self.is_locked: self._context.lock_counter -= 1 @@ -293,6 +296,7 @@ def __enter__(self) -> Self: Acquire the lock. :return: the lock object + """ self.acquire() return self @@ -309,6 +313,7 @@ def __exit__( :param exc_type: the exception type if raised :param exc_value: the exception value if raised :param traceback: the exception traceback if raised + """ self.release() @@ -318,6 +323,6 @@ def __del__(self) -> None: __all__ = [ - "BaseFileLock", "AcquireReturnProxy", + "BaseFileLock", ] diff --git a/src/filelock/_unix.py b/src/filelock/_unix.py index 2f0d891..4ee6033 100644 --- a/src/filelock/_unix.py +++ b/src/filelock/_unix.py @@ -60,6 +60,6 @@ def _release(self) -> None: __all__ = [ - "has_fcntl", "UnixFileLock", + "has_fcntl", ] diff --git a/src/filelock/_util.py b/src/filelock/_util.py index 543c139..c671e85 100644 --- a/src/filelock/_util.py +++ b/src/filelock/_util.py @@ -10,10 +10,13 @@ def raise_on_not_writable_file(filename: str) -> None: """ Raise an exception if attempting to open the file for writing would fail. - This is done so files that will never be writable can be separated from - files that are writable but currently locked + + This is done so files that will never be writable can be separated from files that are writable but currently + locked. + :param filename: file to check :raises OSError: as if the file was opened for writing. + """ try: # use stat to do exists + can write to check without race condition file_stat = os.stat(filename) # noqa: PTH116 @@ -35,13 +38,15 @@ def raise_on_not_writable_file(filename: str) -> None: def ensure_directory_exists(filename: Path | str) -> None: """ - Ensure the directory containing the file exists (create it if necessary) + Ensure the directory containing the file exists (create it if necessary). + :param filename: file. + """ Path(filename).parent.mkdir(parents=True, exist_ok=True) __all__ = [ - "raise_on_not_writable_file", "ensure_directory_exists", + "raise_on_not_writable_file", ] diff --git a/tests/test_error.py b/tests/test_error.py index 1d884a7..8290065 100644 --- a/tests/test_error.py +++ b/tests/test_error.py @@ -1,6 +1,6 @@ from __future__ import annotations -import pickle +import pickle # noqa: S403 from filelock import Timeout diff --git a/tests/test_filelock.py b/tests/test_filelock.py index 5cda668..674d81a 100644 --- a/tests/test_filelock.py +++ b/tests/test_filelock.py @@ -112,13 +112,12 @@ def test_ro_file(lock_type: type[BaseFileLock], tmp_file_ro: Path) -> None: ( pytest.param(PermissionError, "Permission denied:", ".", id="current_directory") if sys.platform == "win32" + # Should be IsADirectoryError on MacOS and Linux else ( - # Should be IsADirectoryError on MacOS and Linux pytest.param(IsADirectoryError, "Is a directory", ".", id="current_directory") - if sys.platform in ["darwin", "linux"] - else + if sys.platform in {"darwin", "linux"} # Should be some type of OSError at least on other operating systems - pytest.param(OSError, None, ".", id="current_directory") + else pytest.param(OSError, None, ".", id="current_directory") ) ), ] diff --git a/whitelist.txt b/whitelist.txt deleted file mode 100644 index 05ed87f..0000000 --- a/whitelist.txt +++ /dev/null @@ -1,39 +0,0 @@ -addnodes -autoclass -autodoc -autosectionlabel -caplog -contnode -docutils -eacces -eisdir -enosys -extlinks -fchmod -filelock -filemode -fromdocname -fspath -getframeinfo -intersphinx -intervall -isabstract -iwgrp -iwoth -iwusr -levelno -lk -msvcrt -nblck -nitpicky -notset -perf -pygments -rdwr -ro -skipif -stacklevel -trunc -typehints -unlck -win32