From 421f767024264a84496bcc9b214e0d4feccf0e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Wed, 13 Aug 2025 06:53:08 -0700 Subject: [PATCH] Add support for 3.14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BernΓ‘t GΓ‘bor --- .github/workflows/check.yaml | 106 ++++++++++------------- .github/workflows/release.yaml | 24 ++---- .pre-commit-config.yaml | 11 ++- .readthedocs.yml | 17 ++-- pyproject.toml | 74 ++++++++++++----- src/filelock/asyncio.py | 3 +- tox.ini | 111 ------------------------- tox.toml | 148 +++++++++++++++++++++++++++++++++ 8 files changed, 266 insertions(+), 228 deletions(-) delete mode 100644 tox.ini create mode 100644 tox.toml diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 5f67408c..daed41ee 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -1,4 +1,4 @@ -name: check +name: "πŸ› οΈ check" on: workflow_dispatch: push: @@ -14,65 +14,59 @@ concurrency: jobs: test: - name: test ${{ matrix.py }} - ${{ matrix.os }} + name: "πŸ§ͺ Test ${{ matrix.py }} - ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: py: - - "pypy3.10" # ahead to start it earlier because takes longer + - "3.14" - "3.13" - "3.12" - "3.11" - "3.10" - "3.9" + - "pypy3.11" os: - - ubuntu-latest - - windows-latest - - macos-latest + - ubuntu-24.04 + - windows-2025 + - macos-15 + exclude: + - { os: windows-2025, py: pypy3.11 } steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Install the latest version of uv + - name: "πŸ”„ Install the latest version of uv" uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - cache-dependency-glob: "pyproject.toml" - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Add .local/bin to Windows PATH - if: runner.os == 'Windows' - shell: bash - run: echo "$USERPROFILE/.local/bin" >> $GITHUB_PATH - - name: Install tox + - name: "πŸ§ͺ Install tox" run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv - - name: Install Python + - name: "🐍 Install Python" run: uv python install --python-preference only-managed ${{ matrix.py }} - - name: Setup test suite + - name: "βš™οΈ Setup test suite" run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.py }} env: UV_PYTHON_PREFERENCE: only-managed - - name: Run test suite - if: ${{ !startsWith(matrix.py, 'pypy')}} - run: tox run --skip-pkg-install -e ${{ matrix.py }} - env: - PYTEST_ADDOPTS: "-vv --durations=20" - DIFF_AGAINST: HEAD - UV_PYTHON_PREFERENCE: only-managed - - name: Run test suite without coverage - if: ${{ startsWith(matrix.py, 'pypy')}} - run: tox run --skip-pkg-install -e ${{ matrix.py }} -- + - name: "βœ… Run test suite" + run: | + if [[ "${{ matrix.py }}" == pypy* ]]; then + tox run --skip-pkg-install -e ${{ matrix.py }} -- + else + tox run --skip-pkg-install -e ${{ matrix.py }} + fi + shell: bash env: PYTEST_ADDOPTS: "-vv --durations=20" UV_PYTHON_PREFERENCE: only-managed - - name: Rename coverage report file + DIFF_AGAINST: HEAD + - name: "πŸ“ Rename coverage report file" if: ${{ !startsWith(matrix.py, 'pypy')}} run: | import os; import sys os.rename(f".tox/.coverage.${{ matrix.py }}", f".tox/.coverage.${{ matrix.py }}-{sys.platform}") shell: python - - name: Upload coverage data + - name: "πŸ“¦ Upload coverage data" if: ${{ !startsWith(matrix.py, 'pypy')}} uses: actions/upload-artifact@v4 with: @@ -82,84 +76,68 @@ jobs: retention-days: 3 coverage: - name: Combine coverage - runs-on: ubuntu-latest + name: "πŸ“Š Combine coverage" + runs-on: ubuntu-24.04 needs: test steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Install the latest version of uv + - name: "πŸ”„ Install the latest version of uv" uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - cache-dependency-glob: "pyproject.toml" - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Add .local/bin to Windows PATH - if: runner.os == 'Windows' - shell: bash - run: echo "$USERPROFILE/.local/bin" >> $GITHUB_PATH - - name: Install hatch + - name: "πŸ§ͺ Install tox" run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv - - name: Build package to generate version + - name: "πŸ“¦ Build package to generate version" run: uv build --python 3.13 --python-preference only-managed --wheel . --out-dir dist - - name: Setup coverage tool + - name: "βš™οΈ Setup coverage tool" run: tox -e coverage --notest env: UV_PYTHON_PREFERENCE: only-managed - - name: Download coverage data + - name: "⬇️ Download coverage data" uses: actions/download-artifact@v5 with: path: .tox pattern: .coverage.* merge-multiple: true - - name: Combine and report coverage + - name: "πŸ“Š Combine and report coverage" run: tox -e coverage --skip-pkg-install env: UV_PYTHON_PREFERENCE: only-managed - - name: Upload HTML report + - name: "πŸ“€ Upload HTML report" uses: actions/upload-artifact@v4 with: name: html-report path: .tox/htmlcov check: - name: ${{ matrix.tox_env }} - ${{ matrix.os }} + name: "πŸ”Ž ${{ matrix.tox_env }} - ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: - - ubuntu-latest - - windows-latest + - ubuntu-24.04 + - windows-2025 tox_env: - dev - type - docs - - readme + - pkg_meta exclude: - - { os: windows-latest, tox_env: readme } + - { os: windows-2025, tox_env: pkg_meta } steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Install the latest version of uv + - name: "πŸ”„ Install the latest version of uv" uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - cache-dependency-glob: "pyproject.toml" - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Add .local/bin to Windows PATH - if: runner.os == 'Windows' - shell: bash - run: echo "$USERPROFILE/.local/bin" >> $GITHUB_PATH - - name: Install tox + - name: "πŸ§ͺ Install tox" run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv - - name: Setup test suite + - name: "βš™οΈ Setup test suite" run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.tox_env }} env: UV_PYTHON_PREFERENCE: only-managed - - name: Run test suite + - name: "βœ… Run test suite" run: tox run --skip-pkg-install -e ${{ matrix.tox_env }} env: UV_PYTHON_PREFERENCE: only-managed diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d3a17d54..a5adf45a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: Release to PyPI +name: πŸš€Release to PyPI on: push: tags: ["*"] @@ -8,20 +8,16 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Install the latest version of uv + - name: "πŸ”„ Install the latest version of uv" uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - cache-dependency-glob: "pyproject.toml" - github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Build package + - name: "πŸ“¦ Build package" run: uv build --python 3.13 --python-preference only-managed --sdist --wheel . --out-dir dist - - name: Store the distribution packages + - name: "πŸ“€ Store the distribution packages" uses: actions/upload-artifact@v4 with: name: ${{ env.dists-artifact-name }} @@ -30,19 +26,17 @@ jobs: release: needs: - build - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 environment: - name: release + name: "πŸš€ Release to PyPI" url: https://pypi.org/project/filelock/${{ github.ref_name }} permissions: id-token: write steps: - - name: Download all the dists + - name: "⬇️ Download all the dists" uses: actions/download-artifact@v5 with: name: ${{ env.dists-artifact-name }} path: dist/ - - name: Publish to PyPI + - name: "πŸš€ Publish to PyPI" uses: pypa/gh-action-pypi-publish@v1.12.4 - with: - attestations: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0da89b13..0a80e156 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,11 +14,10 @@ repos: hooks: - id: codespell additional_dependencies: ["tomli>=2.2.1"] - - repo: https://github.com/tox-dev/tox-ini-fmt - rev: "1.6.0" + - repo: https://github.com/tox-dev/tox-toml-fmt + rev: "v1.0.0" hooks: - - id: tox-ini-fmt - args: ["-p", "fix"] + - id: tox-toml-fmt - repo: https://github.com/tox-dev/pyproject-fmt rev: "v2.6.0" hooks: @@ -34,8 +33,8 @@ repos: hooks: - id: prettier additional_dependencies: - - prettier@3.4.2 - - "@prettier/plugin-xml@3.4.1" + - prettier@3.6.2 + - "@prettier/plugin-xml@3.4.2" - repo: meta hooks: - id: check-hooks-apply diff --git a/.readthedocs.yml b/.readthedocs.yml index ab301130..af60a61c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,15 +1,10 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-lts-latest tools: python: "3" -python: - install: - - method: pip - path: . - extra_requirements: - - docs -sphinx: - builder: html - configuration: docs/conf.py - fail_on_warning: true + commands: + - pip install uv + - uv venv + - uv pip install tox-uv + - .venv/bin/tox run -e docs -- diff --git a/pyproject.toml b/pyproject.toml index 64a7ef16..d0cefd83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] build-backend = "hatchling.build" requires = [ - "hatch-vcs>=0.4", + "hatch-vcs>=0.5", "hatchling>=1.27", ] @@ -40,30 +40,55 @@ classifiers = [ dynamic = [ "version", ] -optional-dependencies.docs = [ - "furo>=2024.8.6", - "sphinx>=8.1.3", - "sphinx-autodoc-typehints>=3", -] -optional-dependencies.testing = [ - "covdefaults>=2.3", - "coverage>=7.6.10", - "diff-cover>=9.2.1", - "pytest>=8.3.4", - "pytest-asyncio>=0.25.2", - "pytest-cov>=6", - "pytest-mock>=3.14", - "pytest-timeout>=2.3.1", - "virtualenv>=20.28.1", -] -optional-dependencies.typing = [ - "typing-extensions>=4.12.2; python_version<'3.11'", -] urls.Documentation = "https://py-filelock.readthedocs.io" urls.Homepage = "https://github.com/tox-dev/py-filelock" urls.Source = "https://github.com/tox-dev/py-filelock" urls.Tracker = "https://github.com/tox-dev/py-filelock/issues" +[dependency-groups] +dev = [ + { include-group = "coverage" }, + { include-group = "docs" }, + { include-group = "fix" }, + { include-group = "pkg-meta" }, + { include-group = "test" }, + { include-group = "type" }, +] + +test = [ + "covdefaults>=2.3", + "diff-cover>=9.6", + "pytest>=8.4.1", + "pytest-asyncio>=1.1", + "pytest-cov>=6.2.1", + "pytest-mock>=3.14.1", + "pytest-timeout>=2.4", + "virtualenv>=20.33.1", +] +type = [ + "mypy>=1.17.1", + "typing-extensions>=4.14.1; python_version<'3.11'", + { include-group = "test" }, +] +docs = [ + "furo>=2025.7.19", + "sphinx>=8.2.3", + "sphinx-autodoc-typehints>=3.2", +] +fix = [ + "pre-commit-uv>=4.1.4", +] +pkg-meta = [ + "check-wheel-contents>=0.6.3", + "twine>=6.1", + "uv>=0.8.5", +] +coverage = [ + "covdefaults>=2.3", + "coverage[toml]>=7.10.2", + "diff-cover>=9.6", +] + [tool.hatch] build.hooks.vcs.version-file = "src/filelock/version.py" build.targets.sdist.include = [ @@ -119,6 +144,15 @@ max_supported_python = "3.13" [tool.pytest.ini_options] asyncio_default_fixture_loop_scope = "session" +testpaths = [ + "tests", +] +verbosity_assertions = 2 +filterwarnings = [ + "error", + "ignore:unclosed database in None: # type: ignore[override] _LOGGER.debug("Lock %s released on %s", lock_id, lock_filename) async def _run_internal_method(self, method: Callable[[], Any]) -> None: - if asyncio.iscoroutinefunction(method): + if iscoroutinefunction(method): await method() elif self.run_in_executor: loop = self.loop or asyncio.get_running_loop() diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 960e01e0..00000000 --- a/tox.ini +++ /dev/null @@ -1,111 +0,0 @@ -[tox] -requires = - tox>=4.23.2 - tox-uv>=1.17 -env_list = - fix - 3.13 - 3.12 - 3.11 - 3.10 - 3.9 - type - coverage - docs - readme -skip_missing_interpreters = true - -[testenv] -description = run tests with {basepython} -package = wheel -wheel_build_env = .pkg -extras = - testing -pass_env = - PYTEST_ADDOPTS -set_env = - COVERAGE_FILE = {toxworkdir}{/}.coverage.{envname} -commands = - pytest {tty:--color=yes} {posargs: \ - --junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}filelock --cov {toxinidir}{/}tests \ - --cov-config=pyproject.toml --no-cov-on-fail --cov-report term-missing:skip-covered --cov-context=test \ - --cov-report html:{envtmpdir}{/}htmlcov --cov-report xml:{toxworkdir}{/}coverage.{envname}.xml \ - tests - diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}{/}coverage.{envname}.xml} - -[testenv:fix] -description = format the code base to adhere to our styles, and complain about what we cannot do automatically -base_python = python3.10 -skip_install = true -deps = - pre-commit>=4.0.1 -commands = - pre-commit run --all-files --show-diff-on-failure - python -c 'import pathlib; print("hint: run \{\} install to add checks as pre-commit hook".format(pathlib.Path(r"{envdir}") / "bin" / "pre-commit"))' - -[testenv:type] -description = run type check on code base -deps = - mypy==1.14.1 -set_env = - {tty:MYPY_FORCE_COLOR = 1} -commands = - mypy --strict src/filelock - mypy --strict tests - -[testenv:coverage] -description = combine coverage files and generate diff (against DIFF_AGAINST defaulting to origin/main) -skip_install = true -deps = - covdefaults>=2.3 - coverage[toml]>=7.6.10 - diff-cover>=9.2.1 -extras = -parallel_show_output = true -pass_env = - DIFF_AGAINST -set_env = - COVERAGE_FILE = {toxworkdir}/.coverage -commands = - coverage combine - coverage report --skip-covered --show-missing - coverage xml -o {toxworkdir}/coverage.xml - coverage html -d {toxworkdir}/htmlcov - diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}/coverage.xml -depends = - 3.13 - 3.12 - 3.11 - 3.10 - 3.9 - 3.8 - -[testenv:docs] -description = build documentation -extras = - docs -commands = - sphinx-build -d "{envtmpdir}{/}doctree" docs "{toxworkdir}{/}docs_out" --color -b html -W {posargs} - python -c 'print(r"documentation available under file://{toxworkdir}{/}docs_out{/}index.html")' - -[testenv:readme] -description = check that the long description is valid -skip_install = true -deps = - check-wheel-contents>=0.6.1 - twine>=6.0.1 - uv>=0.5.18 -commands = - uv build --sdist --wheel --out-dir {envtmpdir} . - twine check {envtmpdir}{/}* - check-wheel-contents --no-config {envtmpdir} - -[testenv:dev] -description = generate a DEV environment -package = editable -extras = - docs - testing -commands = - uv pip tree - python -c 'import sys; print(sys.executable)' diff --git a/tox.toml b/tox.toml new file mode 100644 index 00000000..ad5d3a35 --- /dev/null +++ b/tox.toml @@ -0,0 +1,148 @@ +requires = [ "tox>=4.28.4" ] +env_list = [ "fix", "3.14t", "3.14", "3.13", "3.12", "3.11", "3.10", "3.9", "coverage", "type", "docs", "pkg_meta" ] +skip_missing_interpreters = true + +[env_run_base] +description = "run the tests with pytest under {env_name}" +package = "wheel" +wheel_build_env = ".pkg" +dependency_groups = [ "test" ] +pass_env = [ "PYTEST_*", "SSL_CERT_FILE" ] +set_env.COVERAGE_FILE = { replace = "env", name = "COVERAGE_FILE", default = "{work_dir}{/}.coverage.{env_name}" } +commands = [ + [ + "pytest", + "--durations", + "10", + "--junitxml", + "{env_tmp_dir}{/}junit.xml", + { replace = "posargs", extend = true, default = [ + "--no-cov-on-fail", + "--cov", + "{env_site_packages_dir}{/}filelock", + "--cov", + "{tox_root}{/}tests", + "--cov-config", + "{tox_root}{/}pyproject.toml", + "--cov-context", + "test", + "--cov-report", + "term-missing:skip-covered", + "--cov-report", + "html:{env_tmp_dir}{/}htmlcov", + "--cov-report", + "xml:{env_tmp_dir}{/}coverage.xml", + ] }, + + "tests", + ], + { replace = "posargs", extend = true, default = [ + [ + "diff-cover", + "--compare-branch", + { replace = "env", name = "DIFF_AGAINST", default = "origin/main" }, + "{env_tmp_dir}{/}coverage.xml", + ], + ] }, +] + +[env.coverage] +description = "combine coverage files and generate diff (against DIFF_AGAINST defaulting to origin/main)" +skip_install = true +dependency_groups = [ "coverage" ] +pass_env = [ + { replace = "ref", of = [ + "env_run_base", + "pass_env", + ], extend = true }, + "DIFF_AGAINST", +] +set_env.COVERAGE_FILE = { replace = "env", name = "COVERAGE_FILE", default = "{work_dir}{/}.coverage" } +commands = [ + [ "coverage", "combine" ], + [ "coverage", "report", "--skip-covered", "--show-missing" ], + [ "coverage", "xml", "-o", "{env_tmp_dir}{/}coverage.xml" ], + [ "coverage", "html", "-d", "{work_dir}{/}htmlcov" ], + [ + "diff-cover", + "--compare-branch", + { replace = "env", name = "DIFF_AGAINST", default = "origin/main" }, + "{env_tmp_dir}{/}coverage.xml", + ], +] +parallel_show_output = true +depends = [ "3.14t", "3.14", "3.13", "3.12", "3.11", "3.10", "3.9" ] + +[env.fix] +description = "format the code base to adhere to our styles, and complain about what we cannot do automatically" +skip_install = true +dependency_groups = [ "fix" ] +pass_env = [ + { replace = "ref", of = [ + "env_run_base", + "pass_env", + ], extend = true }, + "PROGRAMDATA", + "DISABLE_PRE_COMMIT_UV_PATCH", +] +commands = [ [ "pre-commit", "run", "--all-files", "--show-diff-on-failure", { replace = "posargs", extend = true } ] ] + +[env.type] +description = "run type check on code base" +dependency_groups = [ "type" ] +commands = [ [ "mypy", "src{/}filelock" ], [ "mypy", "tests" ] ] + +[env.docs] +description = "build documentation" +dependency_groups = [ "docs" ] +commands = [ + [ + "sphinx-build", + "-d", + "{env_tmp_dir}{/}docs_tree", + "docs", + "{env:READTHEDOCS_OUTPUT:{work_dir}{/}docs_out}/html", + "--color", + "-b", + "html", + { replace = "posargs", default = [ ], extend = true }, + "-W", + ], + [ + "python", + "-c", + 'print(r"documentation available under file://{work_dir}{/}docs_out{/}index.html")', + ], +] + +[env.pkg_meta] +description = "check that the long description is valid" +skip_install = true +dependency_groups = [ "pkg_meta" ] +commands = [ + [ + "uv", + "build", + "--sdist", + "--wheel", + "--out-dir", + "{env_tmp_dir}", + ".", + ], + [ + "twine", + "check", + "{env_tmp_dir}{/}*", + ], + [ + "check-wheel-contents", + "--no-config", + "{env_tmp_dir}", + ], +] + +[env.dev] +description = "dev environment with all deps at {envdir}" +package = "editable" +dependency_groups = [ "dev" ] +commands = [ [ "uv", "pip", "list", "--format=columns" ], [ "python", "-c", 'print(r"{env_python}")' ] ]