diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index de34370..ae4e4f1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -2,7 +2,7 @@ name: check on: workflow_dispatch: push: - branches: "main" + branches: ["main"] tags-ignore: ["**"] pull_request: schedule: @@ -36,8 +36,18 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.12" - - name: Install tox - run: python -m pip install tox + - name: Pick environment to run + run: | + import codecs; import os + py = "${{ matrix.py }}" + py = "test.{}".format(py if py.startswith("pypy") else f"py{py}") + print(f"Picked {py}") + with codecs.open(os.environ["GITHUB_ENV"], mode="a", encoding="utf-8") as file_handler: + file_handler.write("FORCE_COLOR=1\n") + file_handler.write(f"ENV={py}\n") + shell: python + - name: Install hatch + run: python -m pip install hatch - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -45,36 +55,28 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.py }} - - name: Pick environment to run + - name: Setup test environment run: | - import codecs - import os - import platform - import sys - cpy = platform.python_implementation() == "CPython" - base =("{}{}{}" if cpy else "{}{}").format("py" if cpy else "pypy", *sys.version_info[0:2]) - env = "TOXENV={}\n".format(base) - print("Picked:\n{}for{}".format(env, sys.version)) - with codecs.open(os.environ["GITHUB_ENV"], "a", "utf-8") as file_handler: - file_handler.write(env) - shell: python - - name: Setup test suite - run: tox -vv --notest + hatch -v env create ${ENV} + hatch run ${ENV}:pip freeze + shell: bash - name: Run test suite - run: tox --skip-pkg-install + run: hatch -v run ${ENV}:run env: PYTEST_ADDOPTS: "-vv --durations=20" CI_RUN: "yes" + shell: bash - name: Rename coverage report file run: | import os; import sys; - os.rename(f".tox/.coverage.{os.environ['TOXENV']}", f".tox/.coverage.{os.environ['TOXENV']}-{sys.platform}") + os.rename(f"report{os.sep}.coverage.${{ matrix.py }}", f"report{os.sep}.coverage.${{ matrix.py }}-{sys.platform}") shell: python - name: Upload coverage data - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: coverage-data - path: ".tox/.coverage.*" + name: coverage-${{ matrix.os }}-${{ matrix.py }} + path: "report/.coverage.*" + retention-days: 3 coverage: name: Combine coverage @@ -87,29 +89,30 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.12" - - name: Install tox - run: python -m pip install tox + - name: Let us have colors + run: echo "FORCE_COLOR=true" >> "$GITHUB_ENV" + - name: Install hatch + run: python -m pip install hatch - name: Setup coverage tool - run: tox -e coverage --notest - - name: Install package builder - run: python -m pip install build - - name: Build package - run: pyproject-build --wheel . + run: | + hatch -v env create coverage + hatch run coverage:pip freeze - name: Download coverage data - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: coverage-data - path: .tox + path: report + pattern: coverage-* + merge-multiple: true - name: Combine and report coverage - run: tox -e coverage + run: hatch run coverage:run - name: Upload HTML report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: html-report - path: .tox/htmlcov + path: report/html check: - name: ${{ matrix.tox_env }} - ${{ matrix.os }} + name: ${{ matrix.env.name }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -117,13 +120,11 @@ jobs: os: - ubuntu-22.04 - windows-2022 - tox_env: - - dev - - type - - docs - - readme - exclude: - - { os: windows-latest, tox_env: readme } + env: + - {"name": "default", "target": "show"} + - {"name": "type", "target": "run"} + - {"name": "docs", "target": "build"} + - {"name": "readme", "target": "run"} steps: - uses: actions/checkout@v4 with: @@ -132,9 +133,11 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.12" - - name: Install tox - run: python -m pip install tox - - name: Setup test suite - run: tox -vv --notest -e ${{ matrix.tox_env }} - - name: Run test suite - run: tox --skip-pkg-install -e ${{ matrix.tox_env }} + - name: Install hatch + run: python -m pip install hatch + - name: Setup ${{ matrix.env.name }} + run: | + hatch -v env create ${{ matrix.env.name }} + hatch run ${{ matrix.env.name }}:pip freeze + - name: Run ${{ matrix.env.name }} + run: hatch -v run ${{ matrix.env.name }}:${{ matrix.env.target }} diff --git a/.gitignore b/.gitignore index f5f1096..86a75e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ -/.idea/ -/.vscode/ - *.pyc *.egg-info -tmp/ -build/ -dist/ -.tox/ +/dist +/.tox /src/platformdirs/version.py +/report +/docs/build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3856aa..705598f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,11 +21,6 @@ repos: - id: ruff-format - id: ruff args: [ "--fix", "--unsafe-fixes", "--exit-non-zero-on-fix" ] - - repo: https://github.com/tox-dev/tox-ini-fmt - rev: "1.3.1" - hooks: - - id: tox-ini-fmt - args: ["-p", "fix"] - repo: https://github.com/tox-dev/pyproject-fmt rev: "1.7.0" hooks: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..90ba3d8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +This project uses [hatch](https://hatch.pypa.io) development, therefore consult that documentation for more in-depth how +to. To see a list of available environments use `hatch env show`, for example to run the test suite under Python 3.12 +can type in a shell `hatch run test.py3.12:run`. diff --git a/pyproject.toml b/pyproject.toml index 3e051c8..6ae2403 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,9 @@ optional-dependencies.test = [ "pytest-cov>=4.1", "pytest-mock>=3.12", ] +optional-dependencies.type = [ + "mypy>=1.8", +] urls.Documentation = "https://platformdirs.readthedocs.io" urls.Homepage = "https://github.com/platformdirs/platformdirs" urls.Source = "https://github.com/platformdirs/platformdirs" @@ -67,6 +70,77 @@ build.hooks.vcs.version-file = "src/platformdirs/version.py" build.targets.sdist.include = ["/src", "/tests", "/tox.ini"] version.source = "vcs" +[tool.hatch.envs.default] +description = "Development environment" +features = ["test", "docs", "type"] +scripts = { show = ["python -m pip list --format=columns", 'python -c "import sys; print(sys.executable)"'] } + +[tool.hatch.envs.test] +template = "test" +# dev-mode = false # cannot enable this until https://github.com/pypa/hatch/issues/1237 +description = "Run the test suite" +matrix = [{ python = ["3.12", "3.11", "3.10", "3.9", "3.8", "pypy3.9"] }] +features = ["test"] +env-vars = { COVERAGE_FILE = "report/.coverage.{matrix:python}", COVERAGE_PROCESS_START = "pyproject.toml", _COVERAGE_SRC = "src/platformdirs" } +[tool.hatch.envs.test.scripts] +run = [""" + pytest --junitxml report/junit.{matrix:python}.xml --cov src/platformdirs --cov tests \ + --cov-config=pyproject.toml --no-cov-on-fail --cov-report term-missing:skip-covered --cov-context=test \ + --cov-report html:report/html{matrix:python} --cov-report xml:report/coverage{matrix:python}.xml \ + tests +"""] + +[tool.hatch.envs.coverage] +template = "coverage" +description = "combine coverage files and generate diff" +dependencies = ["covdefaults>=2.3", "coverage[toml]>=7.3.2", "diff-cover>=8.0.1"] +env-vars = { COVERAGE_FILE = "report/.coverage" } +[tool.hatch.envs.coverage.scripts] +run = [ + "coverage combine report", + "coverage report --skip-covered --show-missing", + "coverage xml -o report/coverage.xml", + "coverage html -d report/html", + "diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} report/coverage.xml", +] + +[tool.hatch.envs.type] +template = "type" +description = "Run the type checker" +python = "3.12" +dev-mode = false +features = ["type", "test"] +scripts = { run = ["mypy --strict src", "mypy --strict tests"] } + +[tool.hatch.envs.fix] +template = "fix" +description = "Run the pre-commit tool to lint and autofix issues" +python = "3.12" +detached = true +dependencies = ["pre-commit>=3.6"] +scripts = { "run" = ["pre-commit run --all-files --show-diff-on-failure"] } + +[tool.hatch.envs.docs] +template = "docs" +description = "Build documentation using Sphinx" +python = "3.12" +dev-mode = false +features = ["docs"] +[tool.hatch.envs.docs.scripts] +build = [ + """python -c "import glob; import subprocess; subprocess.call(['proselint'] + glob.glob('docs/*.rst'))" """, + """python -c "from shutil import rmtree; rmtree('docs/build', ignore_errors=True)" """, + "sphinx-build -d docs/build/tree docs docs/build --color -b html", + """python -c "from pathlib import Path; p=(Path('docs')/'build'/'index.html').absolute().as_uri(); print('Documentation built under '+p)" """, +] + +[tool.hatch.envs.readme] +template = "readme" +description = "check that the long description is valid" +python = "3.12" +dependencies = ["build[virtualenv]>=1.0.3", "twine>=4.0.2", "check-wheel-contents>=0.6.0"] +scripts = { "run" = ["python -m build -o dist", "twine check dist/*.whl dist/*.tar.gz", "check-wheel-contents dist"] } + [tool.ruff] select = ["ALL"] line-length = 120 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a6d51ad..0000000 --- a/tox.ini +++ /dev/null @@ -1,115 +0,0 @@ -[tox] -requires = - tox>=4.2 -env_list = - fix - py312 - py311 - py310 - py39 - py38 - pypy3 - type - coverage - readme - docs -skip_missing_interpreters = true - -[testenv] -description = run the unit tests with pytest under {basepython} -package = wheel -wheel_build_env = .pkg -extras = - test -pass_env = - ANDROID_DATA - ANDROID_ROOT -set_env = - COVERAGE_FILE = {toxworkdir}/.coverage.{envname} - COVERAGE_PROCESS_START = {toxinidir}/pyproject.toml - _COVERAGE_SRC = {envsitepackagesdir}/platformdirs -commands = - pytest {tty:--color=yes} {posargs: \ - --junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}platformdirs \ - --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} - -[testenv:fix] -description = run static analysis and style check using flake8 -skip_install = true -deps = - pre-commit>=3.5 -pass_env = - HOMEPATH - PROGRAMDATA -commands = - pre-commit run --all-files --show-diff-on-failure - -[testenv:type] -description = run type check on code base -deps = - mypy==1.7.1 -set_env = - {tty:MYPY_FORCE_COLOR = 1} -commands = - mypy --strict src - 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.3.2 - diff-cover>=8.0.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 = - py312 - py311 - py310 - py39 - py38 - pypy3 - -[testenv:readme] -description = check that the long description is valid -skip_install = true -deps = - build[virtualenv]>=1.0.3 - twine>=4.0.2 -pass_env = - * -change_dir = {toxinidir} -commands = - python -m build -o {envtmpdir} . - twine check {envtmpdir}/* - -[testenv:docs] -extras = - docs -commands = - python -c 'import glob; import subprocess; subprocess.call(["proselint"] + glob.glob("docs/*.rst"))' - sphinx-build -d "{envtmpdir}/doctree" docs "{toxworkdir}/docs_out" --color -b html {posargs} - python -c 'import pathlib; print("documentation available under \{0\}".format((pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html").as_uri()))' - -[testenv:dev] -description = generate a DEV environment -package = editable -extras = - test -commands = - python -m pip list --format=columns - python -c 'import sys; print(sys.executable)'