[build-system]
build-backend = 'uv_build'
requires = ['uv_build>=0.11,<0.13']

[project]
name = 'portalocker'
version = '4.1.0'
dynamic = []
description = 'Cross-platform file locking, with Redis, PID-file and bounded-semaphore locks'
readme = 'README.rst'
requires-python = '>=3.10'
license = 'BSD-3-Clause'
license-files = ['LICENSE']
authors = [{ name = 'Rick van Hattem', email = 'wolph@wol.ph' }]
keywords = ['locking', 'locks', 'with', 'statement', 'windows', 'linux', 'unix']
classifiers = [
    'Development Status :: 6 - Mature',
    'Intended Audience :: Developers',
    'Intended Audience :: Information Technology',
    'Intended Audience :: System Administrators',
    'Natural Language :: English',
    'Operating System :: MacOS',
    'Operating System :: MacOS :: MacOS X',
    'Operating System :: Microsoft',
    'Operating System :: Microsoft :: Windows',
    'Operating System :: POSIX',
    'Operating System :: POSIX :: BSD',
    'Operating System :: POSIX :: BSD :: FreeBSD',
    'Operating System :: POSIX :: BSD :: NetBSD',
    'Operating System :: POSIX :: BSD :: OpenBSD',
    'Operating System :: POSIX :: Linux',
    'Operating System :: POSIX :: SunOS/Solaris',
    'Operating System :: Unix',
    'Programming Language :: Python',
    'Programming Language :: Python :: 3',
    'Programming Language :: Python :: 3 :: Only',
    'Programming Language :: Python :: 3.10',
    'Programming Language :: Python :: 3.11',
    'Programming Language :: Python :: 3.12',
    'Programming Language :: Python :: 3.13',
    'Programming Language :: Python :: 3.14',
    'Programming Language :: Python :: Implementation',
    'Programming Language :: Python :: Implementation :: CPython',
    'Programming Language :: Python :: Implementation :: PyPy',
    'Topic :: Software Development :: Libraries',
    'Topic :: Software Development :: Libraries :: Python Modules',
    'Topic :: System',
    'Topic :: System :: Distributed Computing',
    'Topic :: System :: Filesystems',
    'Topic :: System :: Monitoring',
    'Typing :: Typed',
]
dependencies = []

[project.urls]
homepage = 'https://github.com/wolph/portalocker/'
documentation = 'https://portalocker.readthedocs.io/en/latest/'
repository = 'https://github.com/wolph/portalocker/'

[project.optional-dependencies]
# redis-py has shipped inline type hints (py.typed) since 5.0, which
# replaced the external types-redis stubs this package used to depend on.
redis = ['redis>=5.0']
win32 = ["pywin32>=226; platform_system == 'Windows'"]
tests = [
    'portalocker[redis]',
    'fakeredis>=2.31.0',
    'pytest>=5.4.1',
    'pytest-cov>=2.8.1',
    'pytest-timeout>=2.1.0',
    'pytest-rerunfailures>=15.1',
    'coverage-conditional-plugin>=0.9',
    'types-pywin32>=310.0.0.20250429',
]
docs = [
    'sphinx>=7',
    'furo',
]

[dependency-groups]
dev = [
    'portalocker[tests,docs]',
    'ruff',
    'mypy',
    'basedpyright',
    'pyrefly',
    'ty',
    'codespell',
    'tox>=4.21',
    'tox-uv',
]

[tool.uv.build-backend]
module-name = 'portalocker'
module-root = ''
source-include = ['CHANGELOG.rst', 'portalocker_tests/**']

[tool.codespell]
skip = '*.css,*.js,*.lock,./docs/_build,./htmlcov,./.git,./.tox,./.venv'

[tool.repo-review]
ignore = [
    'PY004', # no /docs with markdown - we use RST
    'PY005', # tests folder is portalocker_tests (public import path for downstream)
    'PC110', # no black, we use ruff
    'PC111', # no blacken-docs, we use ruff
    'PC140', # manual mypy instead of pre-commit
    'PC160', # manual codespell instead of pre-commit
    'PC170', # no pygrep
    'PC180', # no css/js to format
    'PC190', # manual ruff instead of pre-commit
    'PC901', # no pre-commit CI; lefthook is used instead
    'PY006', # no pre-commit config; lefthook is used instead
    'MY103', # mypy strict = true is set in [tool.mypy]
]

[tool.pytest.ini_options]
minversion = '6.0'
addopts = [
    '--doctest-modules',
    '--doctest-glob=*.rst',
    '--cov=portalocker',
    '--cov-report=term-missing',
    '--cov-fail-under=100',
    '--timeout=20',
    '--strict-config',
    '--strict-markers',
    '-ra',
    '-p', 'no:cacheprovider',
]
log_level = 'INFO'
xfail_strict = true
testpaths = ['portalocker', 'portalocker_tests', 'docs', 'README.rst']
doctest_optionflags = ['ELLIPSIS']
norecursedirs = ['_build', '_theme']
filterwarnings = [
    'error',
    'ignore::DeprecationWarning',
    'ignore::PendingDeprecationWarning',
    'ignore::ImportWarning',
    'ignore::ResourceWarning',
    # CPython's multiprocessing resource tracker warns (and, escalated to an
    # error inside a finalizer, fails tests as an unraisable exception) when
    # garbage collection makes it clean up a semaphore reentrantly. The
    # timing is interpreter-internal and unrelated to the code under test.
    'ignore:ResourceTracker called reentrantly:UserWarning',
]

[tool.coverage.run]
branch = true
relative_files = true
source = ['portalocker', 'portalocker_tests']
plugins = ['coverage_conditional_plugin']

[tool.coverage.report]
fail_under = 100
ignore_errors = true
exclude_also = [
    'pragma: no cover',
    'def __repr__',
    'if self.debug:',
    'if settings.DEBUG',
    'if typing.TYPE_CHECKING:',
    'if TYPE_CHECKING:',
    '@(typing\.)?overload',
    'raise AssertionError',
    'raise NotImplementedError',
    'except ImportError:',
    'if 0:',
    'if __name__ == .__main__.:', # CLI entry-point guard, not exercised by imports
    'typing.Protocol',
]

# The `not-nt` / `not-posix` / `nt-no-pywin32` pragmas above are resolved
# per-OS by coverage-conditional-plugin (see rules below) rather than the
# static `exclude_also` list, so a POSIX-only branch stays *measured* on POSIX
# (and is only excluded on the platforms where it cannot run) instead of being
# hidden everywhere. `os_name` is `os.name`; `is_installed` probes the optional
# pywin32 extra so the Win32-only dispatch paths are excluded on the
# pywin32-less Windows cells where they are unreachable.
[tool.coverage.coverage_conditional_plugin.rules]
not-nt = "os_name != 'nt'"
not-posix = "os_name != 'posix'"
nt-no-pywin32 = "os_name == 'nt' and not is_installed('win32file')"

[tool.mypy]
python_version = '3.10'
strict = true
warn_unreachable = true
enable_error_code = ['ignore-without-code', 'truthy-bool', 'redundant-expr']
packages = ['portalocker', 'portalocker_tests']

[[tool.mypy.overrides]]
module = 'portalocker_tests.*'
disallow_untyped_defs = false

[tool.pyright]
pythonVersion = '3.10'
typeCheckingMode = 'standard'
strict = ['portalocker']
include = ['portalocker', 'portalocker_tests']
exclude = ['.venv', '.tox', 'build', 'dist', 'docs']

[tool.pyrefly]
project-includes = ['portalocker', 'portalocker_tests']
project-excludes = ['.venv', '.tox', 'build', 'dist']
python-version = '3.10'

[tool.ty.environment]
python-version = '3.10'

[tool.ty.src]
include = ['portalocker', 'portalocker_tests']

[tool.uv]
exclude-newer = "14 days"
