├── .github ├── dependabot.yaml ├── semantic_release │ ├── package-lock.json │ └── package.json └── workflows │ ├── lint.yaml │ ├── matchers │ ├── flake8.json │ ├── mypy.json │ └── python.json │ ├── publish.yaml │ ├── release.yaml │ └── tests.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .releaserc.json ├── LICENSE ├── README.md ├── docs ├── cli.md ├── cli_usage.md ├── contributing.md ├── examples.md ├── gen_pages.py ├── logo.png ├── notes.md └── upgrading.md ├── hatch_pip_compile ├── __about__.py ├── __init__.py ├── __main__.py ├── base.py ├── cli.py ├── exceptions.py ├── hooks.py ├── installer.py ├── lock.py ├── plugin.py ├── py.typed └── resolver.py ├── mkdocs.yaml ├── pyproject.toml ├── requirements.txt ├── requirements ├── requirements-docs.txt ├── requirements-lint.txt ├── requirements-matrix.py3.10.txt ├── requirements-matrix.py3.11.txt ├── requirements-matrix.py3.12.txt ├── requirements-matrix.py3.8.txt ├── requirements-matrix.py3.9.txt ├── requirements-test.txt ├── requirements-versions.1.10.x.txt ├── requirements-versions.1.11.x.txt ├── requirements-versions.1.12.x.txt ├── requirements-versions.1.7.x.txt ├── requirements-versions.1.8.x.txt └── requirements-versions.1.9.x.txt └── tests ├── __init__.py ├── conftest.py ├── data ├── README.md ├── hatch_pip_compile_test.py ├── pyproject.toml ├── requirements.txt └── requirements │ ├── requirements-lint.txt │ └── requirements-test.txt ├── test_cli.py ├── test_installer.py ├── test_integration_cli.py ├── test_lock.py └── test_plugin.py /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | - package-ecosystem: pip 8 | directory: /.github/workflows 9 | schedule: 10 | interval: monthly 11 | - package-ecosystem: pip 12 | directory: /docs 13 | schedule: 14 | interval: monthly 15 | - package-ecosystem: pip 16 | directory: / 17 | schedule: 18 | interval: monthly 19 | versioning-strategy: lockfile-only 20 | allow: 21 | - dependency-type: all 22 | -------------------------------------------------------------------------------- /.github/semantic_release/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@semantic-release/exec": "^6.0.3", 4 | "@semantic-release/git": "^10.0.1", 5 | "@semantic-release/github": "^8.0.7", 6 | "semantic-release": "^21.0.1", 7 | "semantic-release-gitmoji": "^1.6.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request: 5 | branches: ["**"] 6 | 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | env: 11 | PIP_COMPILE_DISABLE: true 12 | HATCH_DEBUG: true 13 | steps: 14 | - name: Set up Github Workspace 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Set up Python Environment 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.11" 22 | - name: Install Hatch 23 | run: | 24 | python -m pip install --upgrade pip wheel 25 | python -m pip install -q hatch pre-commit 26 | python -m pip install -q "${{ github.workspace }}" 27 | hatch --version 28 | - name: Lint 29 | id: lint 30 | continue-on-error: true 31 | run: | 32 | echo "::add-matcher::.github/workflows/matchers/flake8.json" 33 | hatch run lint:style 34 | echo "::remove-matcher owner=flake8::" 35 | - name: Type Checking 36 | id: check 37 | continue-on-error: true 38 | run: | 39 | echo "::add-matcher::.github/workflows/matchers/mypy.json" 40 | hatch run lint:typing 41 | echo "::remove-matcher owner=mypy::" 42 | - name: Raise Errors For Failures 43 | if: | 44 | steps.lint.outcome != 'success' || 45 | steps.check.outcome != 'success' 46 | run: | 47 | echo "Lint: ${{ steps.lint.outcome }}" 48 | echo "Check: ${{ steps.check.outcome }}" 49 | exit 1 50 | -------------------------------------------------------------------------------- /.github/workflows/matchers/flake8.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "flake8", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*?):(\\d+):(\\d+): (.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "message": 4 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/matchers/mypy.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "mypy", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.+):(\\d+):\\s(error|warning|note):\\s(.+)$", 8 | "file": 1, 9 | "line": 2, 10 | "severity": 3, 11 | "message": 4 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/matchers/python.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "python", 5 | "pattern": [ 6 | { 7 | "regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$", 8 | "file": 1, 9 | "line": 2 10 | }, 11 | { 12 | "regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$", 13 | "message": 2 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publishing 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | pypi-publish: 10 | name: PyPI 11 | if: github.repository_owner == 'juftin' 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out the repository 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 2 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.11" 22 | - name: Install Hatch 23 | run: | 24 | python -m pip install --upgrade pip wheel 25 | python -m pip install -q hatch pre-commit 26 | python -m pip install -q "${{ github.workspace }}" 27 | hatch --version 28 | - name: Build package 29 | run: | 30 | hatch build 31 | - name: Publish package on PyPI 32 | uses: pypa/gh-action-pypi-publish@release/v1 33 | with: 34 | user: __token__ 35 | password: ${{ secrets.PYPI_TOKEN }} 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | name: github-release 11 | if: github.repository_owner == 'juftin' 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | issues: write 16 | pull-requests: write 17 | steps: 18 | - name: Check out the repository 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 2 22 | ref: main 23 | - name: Setup Node.js 24 | uses: actions/setup-node@v4 25 | - name: Set up Python 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: "3.11" 29 | - name: Install Hatch 30 | run: | 31 | python -m pip install --upgrade pip wheel 32 | python -m pip install -q hatch pre-commit 33 | python -m pip install -q "${{ github.workspace }}" 34 | hatch --version 35 | - name: Release 36 | run: hatch run gen:release 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 39 | GIT_AUTHOR_NAME: github-actions[bot] 40 | GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com 41 | GIT_COMMITTER_NAME: github-actions[bot] 42 | GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com 43 | 44 | github-pages-publish: 45 | runs-on: ubuntu-latest 46 | needs: release 47 | if: github.ref == 'refs/heads/main' && github.repository_owner == 'juftin' 48 | permissions: 49 | pages: write 50 | id-token: write 51 | environment: 52 | name: github-pages 53 | url: ${{ steps.deployment.outputs.page_url }} 54 | steps: 55 | - name: Checkout Latest Changes 56 | uses: actions/checkout@v4 57 | with: 58 | ref: ${{ github.ref }} 59 | fetch-depth: 0 60 | - name: Set up Python Environment 61 | uses: actions/setup-python@v5 62 | with: 63 | python-version: "3.11" 64 | - name: Install Hatch 65 | run: | 66 | python -m pip install --upgrade pip wheel 67 | python -m pip install -q hatch pre-commit 68 | python -m pip install -q "${{ github.workspace }}" 69 | hatch --version 70 | - name: Create Virtual Environment 71 | run: hatch env create docs 72 | - name: Build Site 73 | run: hatch run docs:build 74 | - name: Setup GitHub Pages 75 | uses: actions/configure-pages@v4 76 | - name: Upload Artifact 77 | uses: actions/upload-pages-artifact@v3 78 | with: 79 | path: site/ 80 | - name: Deploy to GitHub Pages 81 | id: deployment 82 | uses: actions/deploy-pages@v4 83 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - hatch_pip_compile/** 9 | - pyproject.toml 10 | - .github/workflows/tests.yaml 11 | - tests/** 12 | pull_request: 13 | branches: ["**"] 14 | paths: 15 | - hatch_pip_compile/** 16 | - pyproject.toml 17 | - .github/workflows/tests.yaml 18 | - tests/** 19 | schedule: 20 | - cron: 0 12 1 * * 21 | jobs: 22 | test-suite: 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | include: 27 | - { 28 | name: Python-3.12, 29 | python: "3.12", 30 | command: "matrix:cov", 31 | matrix: "+py=3.12", 32 | hatch_version: "1.0", 33 | os: ubuntu-latest, 34 | } 35 | - { 36 | name: Python-3.11, 37 | python: "3.11", 38 | command: "matrix:cov", 39 | matrix: "+py=3.11", 40 | hatch_version: "1.0", 41 | os: ubuntu-latest, 42 | } 43 | - { 44 | name: Python-3.10, 45 | python: "3.10", 46 | command: "matrix:cov", 47 | matrix: "+py=3.10", 48 | hatch_version: "1.0", 49 | os: ubuntu-latest, 50 | } 51 | - { 52 | name: Python-3.9, 53 | python: "3.9", 54 | command: "matrix:cov", 55 | matrix: "+py=3.9", 56 | hatch_version: "1.0", 57 | os: ubuntu-latest, 58 | } 59 | - { 60 | name: Python-3.8, 61 | python: "3.8", 62 | command: "matrix:cov", 63 | matrix: "+py=3.8", 64 | hatch_version: "1.0", 65 | os: ubuntu-latest, 66 | } 67 | - { 68 | name: Hatch-1.7.x, 69 | python: "3.11", 70 | command: "versions:cov", 71 | matrix: "+version=1.7.x", 72 | hatch_version: "1.7.0", 73 | os: ubuntu-latest, 74 | } 75 | - { 76 | name: Hatch-1.8.x, 77 | python: "3.11", 78 | command: "versions:cov", 79 | matrix: "+version=1.8.x", 80 | hatch_version: "1.8.1", 81 | os: ubuntu-latest, 82 | } 83 | - { 84 | name: Hatch-1.9.x, 85 | python: "3.11", 86 | command: "versions:cov", 87 | matrix: "+version=1.9.x", 88 | hatch_version: "1.9.7", 89 | os: ubuntu-latest, 90 | } 91 | - { 92 | name: Hatch-1.10.x, 93 | python: "3.11", 94 | command: "versions:cov", 95 | matrix: "+version=1.10.x", 96 | hatch_version: "1.10.0", 97 | os: ubuntu-latest, 98 | } 99 | - { 100 | name: Hatch-1.11.x, 101 | python: "3.11", 102 | command: "versions:cov", 103 | matrix: "+version=1.11.x", 104 | hatch_version: "1.11.1", 105 | os: ubuntu-latest, 106 | } 107 | - { 108 | name: Hatch-1.12.x, 109 | python: "3.11", 110 | command: "versions:cov", 111 | matrix: "+version=1.12.x", 112 | hatch_version: "1.12.0", 113 | os: ubuntu-latest, 114 | } 115 | - { 116 | name: Hatch-Windows, 117 | python: "3.11", 118 | command: "matrix:cov", 119 | matrix: "+py=3.11", 120 | hatch_version: "1.0", 121 | os: windows-latest, 122 | } 123 | runs-on: ${{ matrix.os }} 124 | concurrency: 125 | group: ${{ github.workflow }}-${{ matrix.name }}-${{ github.ref }} 126 | cancel-in-progress: true 127 | env: 128 | PIP_COMPILE_DISABLE: true 129 | HATCH_DEBUG: true 130 | steps: 131 | - name: Set up Github Workspace 132 | uses: actions/checkout@v4 133 | with: 134 | fetch-depth: 0 135 | - name: Set up Python Environment ${{ matrix.python }} 136 | uses: actions/setup-python@v5 137 | with: 138 | python-version: ${{ matrix.python }} 139 | - name: Install Default Python 140 | if: matrix.python != '3.11' 141 | uses: actions/setup-python@v5 142 | with: 143 | python-version: "3.11" 144 | - name: Install Hatch 145 | run: | 146 | python -m pip install --upgrade pip wheel 147 | python -m pip install -q "hatch~=${{ matrix.hatch_version }}" 148 | python -m pip install -q pre-commit 149 | python -m pip install -q "${{ github.workspace }}" 150 | hatch --version 151 | - name: Test Suite 152 | run: | 153 | echo "::add-matcher::.github/workflows/matchers/python.json" 154 | hatch run ${{ matrix.matrix }} ${{ matrix.command }} 155 | echo "::remove-matcher owner=python::" 156 | - name: Upload coverage reports to Codecov 157 | if: matrix.os == 'ubuntu-latest' && matrix.python == '3.11' && matrix.command == 'matrix:cov' 158 | uses: codecov/codecov-action@v3 159 | env: 160 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 161 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | docs/source/api 74 | docs/source/README.md 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # documentation 123 | /site 124 | docs/source/_autosummary 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Mac Files 132 | .DS_Store 133 | 134 | # IDE Files 135 | .idea/ 136 | 137 | # Node.js 138 | node_modules/ 139 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_stages: [commit] 2 | fail_fast: false 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v4.5.0 7 | hooks: 8 | - id: trailing-whitespace 9 | - id: end-of-file-fixer 10 | - id: check-yaml 11 | exclude: mkdocs.yaml 12 | - id: check-ast 13 | - id: check-docstring-first 14 | - id: check-merge-conflict 15 | - id: mixed-line-ending 16 | 17 | - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks 18 | rev: v2.11.0 19 | hooks: 20 | - id: pretty-format-toml 21 | args: [--autofix] 22 | 23 | - repo: https://github.com/pre-commit/mirrors-prettier 24 | rev: v3.0.3 25 | hooks: 26 | - id: prettier 27 | args: [--print-width=88, --tab-width=4] 28 | exclude: | 29 | (?x)( 30 | .github/semantic_release/release_notes.hbs | 31 | docs/cli.md 32 | ) 33 | additional_dependencies: 34 | - prettier 35 | 36 | - repo: local 37 | hooks: 38 | - id: format 39 | name: format 40 | description: Runs Code Auto-Formatters 41 | entry: hatch run lint:fmt 42 | language: system 43 | pass_filenames: false 44 | - id: lint 45 | name: lint 46 | description: Runs Code Linters 47 | entry: hatch run lint:all 48 | language: system 49 | pass_filenames: false 50 | require_serial: false 51 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master", 5 | "next", 6 | "next-major", 7 | "+([0-9])?(.{+([0-9]),x}).x", 8 | { 9 | "name": "beta", 10 | "prerelease": true 11 | }, 12 | { 13 | "name": "alpha", 14 | "prerelease": true 15 | } 16 | ], 17 | "plugins": [ 18 | "semantic-release-gitmoji", 19 | [ 20 | "@semantic-release/exec", 21 | { 22 | "prepareCmd": "hatch version ${nextRelease.version} && hatch build" 23 | } 24 | ], 25 | [ 26 | "@semantic-release/git", 27 | { 28 | "assets": ["pyproject.toml", "*/__about__.py"], 29 | "message": "🔖 hatch-pip-compile ${nextRelease.version}\n\n${nextRelease.notes}\n[skip ci]" 30 | } 31 | ], 32 | [ 33 | "@semantic-release/github", 34 | { 35 | "assets": [ 36 | { 37 | "path": "dist/*.whl" 38 | }, 39 | { 40 | "path": "dist/*.tar.gz" 41 | } 42 | ] 43 | } 44 | ] 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2023 Justin Flannery 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

hatch-pip-compile

2 | 3 |
4 | 5 | hatch-pip-compile 6 | 7 |
8 | 9 |

10 | hatch plugin to use pip-compile (or uv) to manage project dependencies and lockfiles. 11 |

12 | 13 |

14 | PyPI 15 | PyPI - Python Version 16 | GitHub License 17 | docs 18 | Testing Status 19 | 20 | Hatch project 21 | Pip Tools project 22 | uv 23 | Ruff 24 | pre-commit 25 | semantic-release 26 | Gitmoji 27 |

28 | 29 | ## Usage 30 | 31 | The `hatch-pip-compile` plugin will automatically run `pip-compile` whenever your 32 | environment needs to be updated. Behind the scenes, this plugin creates a lockfile 33 | at `requirements.txt` (non-default lockfiles are located at 34 | `requirements/requirements-{env_name}.txt`). Once the dependencies are resolved 35 | the plugin will install the lockfile into your virtual environment and keep it 36 | up-to-date. 37 | 38 | ## Installation 39 | 40 | Declare `hatch-pip-compile` as a dependency in your `pyproject.toml` file under the 41 | `[tool.hatch.env]` table and hatch will automatically install it. You must also have 42 | your environment type set to `pip-compile` (see [Configuration](#configuration)). 43 | 44 | - **_pyproject.toml_** 45 | 46 | ```toml 47 | [tool.hatch.env] 48 | requires = [ 49 | "hatch-pip-compile" 50 | ] 51 | 52 | [tool.hatch.envs.default] 53 | type = "pip-compile" 54 | ``` 55 | 56 | - **_hatch.toml_** 57 | 58 | ```toml 59 | [env] 60 | requires = [ 61 | "hatch-pip-compile" 62 | ] 63 | 64 | [envs.default] 65 | type = "pip-compile" 66 | ``` 67 | 68 | ## Configuration 69 | 70 | Set your environment type to `pip-compile` to use this plugin for the respective environment: 71 | 72 | - **_pyproject.toml_** 73 | 74 | ```toml 75 | [tool.hatch.envs.default] 76 | type = "pip-compile" 77 | ``` 78 | 79 | - **_hatch.toml_** 80 | 81 | ```toml 82 | [envs.default] 83 | type = "pip-compile" 84 | ``` 85 | 86 | ### Common Scenarios 87 | 88 | - [lock-filename](docs/examples.md#lock-filename) - changing the default lockfile path 89 | - [pip-compile-constraint](docs/examples.md#pip-compile-constraint) - syncing dependency versions across environments 90 | - [Upgrading Dependencies](docs/examples.md#upgrading-dependencies) - how to upgrade dependencies 91 | - [Using Hashes](docs/examples.md#pip-compile-hashes) - how to include hashes in your lockfile 92 | - [Using uv instead of pip-compile](docs/examples.md#pip-compile-resolver) - how to use `uv` instead of `pip-compile` 93 | 94 | ### Configuration Options 95 | 96 | The plugin gives you options to configure how lockfiles are generated and how they are installed 97 | into your environment. 98 | 99 | The following example shows how to specify the `pip-compile-hashes` option 100 | on your environment in your `pyproject.toml` file: 101 | 102 | ```toml 103 | [tool.hatch.envs.default] 104 | type = "pip-compile" 105 | pip-compile-hashes = true 106 | ``` 107 | 108 | #### Generating Lockfiles 109 | 110 | | name | type | description | 111 | | ----------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | 112 | | [lock-filename](docs/examples.md#lock-filename) | `str` | The filename of the ultimate lockfile. `default` env is `requirements.txt`, non-default is `requirements/requirements-{env_name}.txt` | 113 | | [pip-compile-constraint](docs/examples.md#pip-compile-constraint) | `str` | An environment to use as a constraint file, ensuring that all shared dependencies are pinned to the same versions. | 114 | | [pip-compile-hashes](docs/examples.md#pip-compile-hashes) | `bool` | Whether to generate hashes in the lockfile. Defaults to `false`. | 115 | | [pip-compile-resolver](docs/examples.md#pip-compile-resolver) | `str` | Whether to use `pip-compile` or `uv` to resolve dependencies into the project. Defaults to `pip-compile` | 116 | | [pip-compile-args](docs/examples.md#pip-compile-args) | `list[str]` | Additional command-line arguments to pass to `pip-compile-resolver` | 117 | | [pip-compile-verbose](docs/examples.md#pip-compile-verbose) | `bool` | Set to `true` to run `pip-compile` in verbose mode instead of quiet mode, set to `false` to silence warnings | 118 | 119 | #### Installing Lockfiles 120 | 121 | | name | type | description | 122 | | --------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------- | 123 | | [pip-compile-installer](docs/examples.md#pip-compile-installer) | `str` | Whether to use `pip`, `pip-sync`, or `uv` to install dependencies into the project. Defaults to `pip` | 124 | | [pip-compile-install-args](docs/examples.md#pip-compile-install-args) | `list[str]` | Additional command-line arguments to pass to `pip-compile-installer` | 125 | 126 | 127 | 128 | --- 129 | 130 | --- 131 | 132 | #### Check Out the [Docs] 133 | 134 | - [Examples 📚](docs/examples.md) 135 | - [Upgrading 🚀](docs/upgrading.md) 136 | - [Command Line Usage 📦](docs/cli_usage.md) 137 | - [Notes 📝](docs/notes.md) 138 | 139 | #### Looking to contribute? See the [Contributing Guide] 140 | 141 | #### See the [Changelog] 142 | 143 | 144 | 145 | [Docs]: https://juftin.github.io/hatch-pip-compile/ 146 | [Contributing Guide]: https://juftin.github.io/hatch-pip-compile/contributing 147 | [Changelog]: https://github.com/juftin/hatch-pip-compile/releases 148 | -------------------------------------------------------------------------------- /docs/cli.md: -------------------------------------------------------------------------------- 1 | # Command Line Interface 2 | 3 | It's recommend to use [pipx] to install the CLI, but 4 | you can also install it with [pip]: 5 | 6 | ```shell 7 | pipx install hatch-pip-compile 8 | ``` 9 | 10 | ::: mkdocs-click 11 | :module: hatch_pip_compile.cli 12 | :command: cli 13 | :prog_name: hatch-pip-compile 14 | :style: table 15 | :list_subcommands: True 16 | 17 | ## How it works 18 | 19 | The `hatch-pip-compile` CLI is a wrapper around `hatch` that simply 20 | sets the `PIP_COMPILE_UPGRADE` / `PIP_COMPILE_UPGRADE_PACKAGE` environment 21 | variables before running a `hatch` command in a given environment. 22 | 23 | These environment variables are used by the `hatch-pip-compile` plugin 24 | to run the `pip-compile` command with the `--upgrade` / `--upgrade-package` 25 | flags. 26 | 27 | ## Examples 28 | 29 | ### Upgrade the `default` environment 30 | 31 | The below command will upgrade all packages in the `default` environment. 32 | 33 | ```shell 34 | hatch-pip-compile --upgrade 35 | ``` 36 | 37 | ### Upgrade a non-default environment 38 | 39 | The below command will upgrade all packages in the `docs` environment. 40 | 41 | ```shell 42 | hatch-pip-compile docs --upgrade 43 | ``` 44 | 45 | ### Upgrade a specific package 46 | 47 | The below command will upgrade the `requests` package in the `default` 48 | environment. 49 | 50 | ```shell 51 | hatch-pip-compile --upgrade-package requests 52 | ``` 53 | 54 | ### Upgrade all `pip-compile` environments 55 | 56 | The below command will upgrade all packages in all `pip-compile` environments. 57 | 58 | ```shell 59 | hatch-pip-compile --upgrade --all 60 | ``` 61 | 62 | [pipx]: https://github.com/pypa/pipx 63 | [pip]: https://pip.pypa.io 64 | -------------------------------------------------------------------------------- /docs/cli_usage.md: -------------------------------------------------------------------------------- 1 | # Using the `hatch-pip-compile` CLI 2 | 3 | For convenience this package also makes a CLI available to handle the setting / 4 | unsetting of the `PIP_COMPILE_UPGRADE` / `PIP_COMPILE_UPGRADE_PACKAGE` environment variables 5 | and invoking the `hatch env run` command for you automatically. To use the CLI you'll need to 6 | install it outside your `pyproject.toml` / `hatch.toml` file. 7 | 8 | I recommend using [pipx] to 9 | install the CLI, but you can also install it directly with [pip]: 10 | 11 | ```shell 12 | pipx install hatch-pip-compile 13 | ``` 14 | 15 | Once installed, you can run the CLI with the `hatch-pip-compile` command. 16 | 17 | ## Examples 18 | 19 | ### Upgrade the `default` environment 20 | 21 | The below command will upgrade all packages in the `default` environment. 22 | 23 | ```shell 24 | hatch-pip-compile --upgrade 25 | ``` 26 | 27 | ### Upgrade a non-default environment 28 | 29 | The below command will upgrade all packages in the `docs` environment. 30 | 31 | ```shell 32 | hatch-pip-compile docs --upgrade 33 | ``` 34 | 35 | ### Upgrade a specific package 36 | 37 | The below command will upgrade the `requests` package in the `default` 38 | environment. 39 | 40 | ```shell 41 | hatch-pip-compile --upgrade-package requests 42 | ``` 43 | 44 | ### Upgrade all `pip-compile` environments 45 | 46 | The below command will upgrade all packages in all `pip-compile` environments. 47 | 48 | ```shell 49 | hatch-pip-compile --upgrade --all 50 | ``` 51 | 52 | [pipx]: https://github.com/pypa/pipx 53 | [pip]: https://pip.pypa.io 54 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Environment Setup 4 | 5 | > TIP: **pipx** 6 | > 7 | > This documentaion uses [pipx] to 8 | > install and manage non-project command line tools like `hatch` and 9 | > `pre-commit`. If you don't already have `pipx` installed, make sure to 10 | > see their [documentation](https://pypa.github.io/pipx/installation/). 11 | > If you prefer not to use `pipx`, you can use `pip` instead. 12 | 13 | 1. Install [hatch](https://github.com/pypa/hatch) 14 | 15 | ```shell 16 | pipx install hatch 17 | ``` 18 | 19 | > NOTE: **pre-commit** 20 | > 21 | > Hatch will attempt to set up pre-commit hooks for you using 22 | > [pre-commit]. If you don't already, make sure to install 23 | > pre-commit as well: `pipx install pre-commit` 24 | 25 | 2. Build the Virtual Environment 26 | 27 | ```shell 28 | hatch env create 29 | ``` 30 | 31 | 3. If you need to, you can link a hatch virtual environment to your IDE. 32 | They can be located by name with the `env find` command: 33 | 34 | ```shell 35 | hatch env find default 36 | ``` 37 | 38 | 4. Activate the Virtual Environment 39 | 40 | ```shell 41 | hatch shell 42 | ``` 43 | 44 | ## Using Hatch 45 | 46 | ### Hatch Cheat Sheet 47 | 48 | | Command Description | Command | Notes | 49 | | -------------------------- | -------------------------- | ---------------------------------------------- | 50 | | Run Formatting | `hatch run lint:fmt` | Runs `ruff` code formatter | 51 | | Run Linting | `hatch run lint:all` | Runs `ruff` and `mypy` linters / type checkers | 52 | | Run Type Checking | `hatch run lint:typing` | Runs `mypy` type checker | 53 | | Serve the Documentation | `hatch run docs:serve` | Serve the documentation using MkDocs | 54 | | Run the `pre-commit` Hooks | `hatch run lint:precommit` | Runs the `pre-commit` hooks on all files | 55 | 56 | ### Hatch Explanation 57 | 58 | Hatch is a Python package manager. It's most basic use is as a standardized build-system. 59 | However, hatch also has some extra features which this project takes advantage of. 60 | These features include virtual environment management and the organization of common 61 | scripts like linting and testing. All the operations in hatch take place in one 62 | of its managed virtual environments. 63 | 64 | Hatch has a variety of environments, to see them simply ask hatch: 65 | 66 | ```bash exec="on" result="markdown" source="tabbed-left" tabs="hatch CLI|Output" 67 | hatch env show 68 | ``` 69 | 70 | That above command will tell you that there are four environments that 71 | you can use: 72 | 73 | - `default` 74 | - `docs` 75 | - `gen` 76 | - `lint` 77 | 78 | Each of these environments has a set of commands that you can run. 79 | To see the commands for a specific environment, run: 80 | 81 | ```bash exec="on" result="markdown" source="tabbed-left" tabs="hatch CLI|Output" 82 | hatch env show default 83 | ``` 84 | 85 | Here we can see that the `default` environment has the following commands: 86 | 87 | - `cov` 88 | - `cov-report` 89 | - `test` 90 | - `test-cov` 91 | 92 | The one that we're interested in is `cov`, which will run the tests 93 | for the project. 94 | 95 | ```bash 96 | hatch run cov 97 | ``` 98 | 99 | Since `cov` is in the default environment, we can run it without 100 | specifying the environment. However, to run the `serve` command in the 101 | `docs` environment, we need to specify the environment: 102 | 103 | ```bash 104 | hatch run docs:serve 105 | ``` 106 | 107 | You can see what scripts are available using the `env show` command 108 | 109 | ```bash exec="on" result="markdown" source="tabbed-left" tabs="hatch CLI|Output" 110 | hatch env show docs 111 | ``` 112 | 113 | ## Committing Code 114 | 115 | This project uses [pre-commit] to run a set of 116 | checks on the code before it is committed. The pre-commit hooks are 117 | installed by hatch automatically when you run it for the first time. 118 | 119 | This project uses [semantic-versioning] standards, managed by [semantic-release]. 120 | Releases for this project are handled entirely by CI/CD via pull requests being 121 | merged into the `main` branch. Contributions follow the [gitmoji] standards 122 | with [conventional commits]. 123 | 124 | While you can denote other changes on your commit messages with [gitmoji], the following 125 | commit message emoji prefixes are the only ones to trigger new releases: 126 | 127 | | Emoji | Shortcode | Description | Semver | 128 | | ----- | ------------- | --------------------------- | ------ | 129 | | 💥 | \:boom\: | Introduce breaking changes. | Major | 130 | | ✨ | \:sparkles\: | Introduce new features. | Minor | 131 | | 🐛 | \:bug\: | Fix a bug. | Patch | 132 | | 🚑 | \:ambulance\: | Critical hotfix. | Patch | 133 | | 🔒 | \:lock\: | Fix security issues. | Patch | 134 | 135 | Most features can be squash merged into a single commit on a pull-request. 136 | When merging multiple commits, they will be summarized into a single release. 137 | 138 | If you're working on a new feature, your commit message might look like: 139 | 140 | ```text 141 | ✨ New Feature Description 142 | ``` 143 | 144 | Bug fix commits would look like this: 145 | 146 | ```text 147 | 🐛 Bug Fix Description 148 | ``` 149 | 150 | If you're working on a feature that introduces breaking changes, your 151 | commit message might look like: 152 | 153 | ```text 154 | 💥 Breaking Change Description 155 | ``` 156 | 157 | Other commits that don't trigger a release might look like: 158 | 159 | ```text 160 | 📝 Documentation Update Description 161 | 👷 CI/CD Update Description 162 | 🧪 Testing Changes Description 163 | 🚚 Moving/Renaming Description 164 | ⬆️ Dependency Upgrade Description 165 | 🎨 Non-Material Code Tidying Description 166 | ``` 167 | 168 | ### Pre-Releases 169 | 170 | [semantic-release] supports pre-releases. To trigger a pre-release, you 171 | would merge your pull request into an `alpha` or `beta` branch. 172 | 173 | ### Specific Release Versions 174 | 175 | In some cases you need more advanced control around what kind of release you 176 | need to create. If you need to release a specific version, you can do so by creating a 177 | new branch with the version number as the branch name. For example, if the 178 | current version is `2.3.2`, but you need to release a fix as `1.2.5`, you 179 | would create a branch named `1.2.x` and merge your changes into that branch. 180 | 181 | See the [semantic-release documentation] for more information about 182 | branch based releases and other advanced release cases. 183 | 184 | [pipx]: https://pipx.pypa.io 185 | [pre-commit]: https://pre-commit.com 186 | [gitmoji]: https://gitmoji.dev 187 | [conventional commits]: https://www.conventionalcommits.org 188 | [semantic-release]: https://github.com/semantic-release/semantic-release 189 | [semantic-versioning]: https://semver.org 190 | [semantic-release documentation]: https://semantic-release.gitbook.io/semantic-release/usage/configuration#branches 191 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## lock-filename 4 | 5 | The path (including the directory) to the ultimate lockfile. Defaults to `requirements.txt` in the project root 6 | for the `default` environment, and `requirements/requirements-{env_name}.txt` for non-default environments. 7 | 8 | Changing the lock file path: 9 | 10 | - **_pyproject.toml_** 11 | 12 | ```toml 13 | [tool.hatch.envs.] 14 | type = "pip-compile" 15 | lock-filename = "locks/{env_name}.lock" 16 | ``` 17 | 18 | - **_hatch.toml_** 19 | 20 | ```toml 21 | [envs.] 22 | type = "pip-compile" 23 | lock-filename = "locks/{env_name}.lock" 24 | ``` 25 | 26 | Changing the lock filename to a path in the project root: 27 | 28 | - **_pyproject.toml_** 29 | 30 | ```toml 31 | [tool.hatch.envs.lint] 32 | type = "pip-compile" 33 | lock-filename = "linting-requirements.txt" 34 | ``` 35 | 36 | - **_hatch.toml_** 37 | 38 | ```toml 39 | [envs.lint] 40 | type = "pip-compile" 41 | lock-filename = "linting-requirements.txt" 42 | ``` 43 | 44 | ## pip-compile-constraint 45 | 46 | An environment to use as a constraint, ensuring that all shared dependencies are 47 | pinned to the same versions. For example, if you have a `default` environment and 48 | a `test` environment, you can set the `pip-compile-constraint` option to `default` 49 | on the `test` environment to ensure that all shared dependencies are pinned to the 50 | same versions. `pip-compile-constraint` can also be set to an empty string to disable 51 | the feature. 52 | 53 | - **_pyproject.toml_** 54 | 55 | ```toml 56 | [tool.hatch.envs.default] 57 | type = "pip-compile" 58 | 59 | [tool.hatch.envs.test] 60 | dependencies = [ 61 | "pytest" 62 | ] 63 | type = "pip-compile" 64 | pip-compile-constraint = "default" 65 | ``` 66 | 67 | - **_hatch.toml_** 68 | 69 | ```toml 70 | [envs.default] 71 | type = "pip-compile" 72 | 73 | [envs.test] 74 | dependencies = [ 75 | "pytest" 76 | ] 77 | type = "pip-compile" 78 | pip-compile-constraint = "default" 79 | ``` 80 | 81 | By default, all environments inherit from the `default` environment via 82 | [inheritance]. A common use case is to set the `pip-compile-constraint` 83 | and `type` options on the `default` environment and inherit them on 84 | all other environments. It's important to note that when `detached = true`, 85 | inheritance is disabled and the `type` and `pip-compile-constraint` options 86 | must be set explicitly. 87 | 88 | - **_pyproject.toml_** 89 | 90 | ```toml 91 | [tool.hatch.envs.default] 92 | type = "pip-compile" 93 | pip-compile-constraint = "default" 94 | 95 | [tool.hatch.envs.test] 96 | dependencies = [ 97 | "pytest" 98 | ] 99 | ``` 100 | 101 | - **_hatch.toml_** 102 | 103 | ```toml 104 | [envs.default] 105 | type = "pip-compile" 106 | pip-compile-constraint = "default" 107 | 108 | [envs.test] 109 | dependencies = [ 110 | "pytest" 111 | ] 112 | ``` 113 | 114 | ## pip-compile-hashes 115 | 116 | Whether to generate hashes in the lockfile. Defaults to `false`. 117 | 118 | - **_pyproject.toml_** 119 | 120 | ```toml 121 | [tool.hatch.envs.] 122 | type = "pip-compile" 123 | pip-compile-hashes = true 124 | ``` 125 | 126 | - **_hatch.toml_** 127 | 128 | ```toml 129 | [envs.] 130 | type = "pip-compile" 131 | pip-compile-hashes = true 132 | ``` 133 | 134 | ## pip-compile-resolver 135 | 136 | Which resolver to use to generate the lockfile. Defaults to `pip-compile`. 137 | 138 | [uv] is a drop in replacement for `pip-compile` with a much faster resolver written in rust. 139 | If you'd like to use `uv` instead of `pip-compile` you can set the `pip-compile-resolver` option. 140 | 141 | > NOTE: **pip-compile-installer** 142 | > 143 | > [uv] can also be used as the default installer instead of `pip`. See 144 | > the [pip-compile-installer](#pip-compile-installer) option for more 145 | > information. 146 | 147 | - **_pyproject.toml_** 148 | 149 | ```toml 150 | [tool.hatch.envs.] 151 | type = "pip-compile" 152 | pip-compile-resolver = "uv" 153 | ``` 154 | 155 | - **_hatch.toml_** 156 | 157 | ```toml 158 | [envs.] 159 | type = "pip-compile" 160 | pip-compile-resolver = "uv" 161 | ``` 162 | 163 | ## pip-compile-args 164 | 165 | Extra arguments to pass to `pip-compile-resolver`. Custom PyPI indexes can be specified here. 166 | 167 | - **_pyproject.toml_** 168 | 169 | ```toml 170 | [tool.hatch.envs.] 171 | type = "pip-compile" 172 | pip-compile-args = [ 173 | "--index-url", 174 | "https://pypi.org/simple", 175 | ] 176 | ``` 177 | 178 | - **_hatch.toml_** 179 | 180 | ```toml 181 | [envs.] 182 | type = "pip-compile" 183 | pip-compile-args = [ 184 | "--index-url", 185 | "https://pypi.org/simple", 186 | ] 187 | ``` 188 | 189 | ## pip-compile-verbose 190 | 191 | Set to `true` to run `pip-compile` in verbose mode instead of quiet mode. 192 | 193 | Optionally, if you would like to silence any warnings set the `pip-compile-verbose` option 194 | to `false`. 195 | 196 | - **_pyproject.toml_** 197 | 198 | ```toml 199 | [tool.hatch.envs.] 200 | type = "pip-compile" 201 | pip-compile-verbose = true 202 | ``` 203 | 204 | - **_hatch.toml_** 205 | 206 | ```toml 207 | [envs.] 208 | type = "pip-compile" 209 | pip-compile-verbose = true 210 | ``` 211 | 212 | ## pip-compile-installer 213 | 214 | Whether to use [pip], [pip-sync], or [uv] to install dependencies into the project. Defaults 215 | to `pip`. When you choose the `pip` option the plugin will run `pip install -r {lockfile}` 216 | under the hood to install the dependencies. When you choose the `pip-sync` option 217 | `pip-sync {lockfile}` is invoked by the plugin. [uv] is a drop in replacement for 218 | `pip`, it has the same behavior as `pip` installer, `uv pip install -r {lockfile}`. 219 | 220 | The key difference between these options is that `pip-sync` will uninstall any packages that are 221 | not in the lockfile and remove them from your environment. `pip-sync` is useful if you want to 222 | ensure that your environment is exactly the same as the lockfile. If the environment should 223 | be used across different Python versions and platforms `pip` is the safer option to use. 224 | 225 | - **_pyproject.toml_** 226 | 227 | ```toml 228 | [tool.hatch.envs.] 229 | type = "pip-compile" 230 | pip-compile-installer = "pip-sync" 231 | ``` 232 | 233 | - **_hatch.toml_** 234 | 235 | ```toml 236 | [envs.] 237 | type = "pip-compile" 238 | pip-compile-installer = "pip-sync" 239 | ``` 240 | 241 | ## pip-compile-install-args 242 | 243 | Extra arguments to pass to `pip-compile-installer`. For example, if you'd like to use `pip` as the 244 | installer but want to pass the `--no-deps` flag to `pip install` you can do so with this option: 245 | 246 | - **_pyproject.toml_** 247 | 248 | ```toml 249 | [tool.hatch.envs.] 250 | type = "pip-compile" 251 | pip-compile-installer = "pip" 252 | pip-compile-install-args = [ 253 | "--no-deps" 254 | ] 255 | ``` 256 | 257 | - **_hatch.toml_** 258 | 259 | ```toml 260 | [envs.] 261 | type = "pip-compile" 262 | pip-compile-installer = "pip" 263 | pip-compile-install-args = [ 264 | "--no-deps" 265 | ] 266 | ``` 267 | 268 | ## Alternate Install Locations 269 | 270 | If you'd like to install dependencies into a different location, you must configure 271 | this at the `hatch` level in your `config.toml` file: 272 | 273 | ```toml 274 | [dirs.env] 275 | virtual = ".venv" 276 | pip-compile = ".venv" 277 | ``` 278 | 279 | Alternatively, you can set this from the commandline: 280 | 281 | ```shell 282 | hatch config set dirs.env.pip-compile ".venv" 283 | ``` 284 | 285 | [pip-sync]: https://github.com/jazzband/pip-tools 286 | [pip]: https://pip.pypa.io 287 | [inheritance]: hhttps://hatch.pypa.io/latest/config/environment/overview/#inheritance 288 | [uv]: https://github.com/astral-sh/uv 289 | -------------------------------------------------------------------------------- /docs/gen_pages.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generate the code reference pages and navigation. 3 | """ 4 | 5 | import logging 6 | from pathlib import Path 7 | 8 | import mkdocs_gen_files 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | project_dir = Path(__file__).resolve().parent.parent 13 | source_code = project_dir.joinpath("hatch_pip_compile") 14 | 15 | for path in sorted(source_code.rglob("*.py")): 16 | module_path = path.relative_to(project_dir).with_suffix("") 17 | doc_path = path.relative_to(source_code).with_suffix(".md") 18 | full_doc_path = Path("reference", doc_path) 19 | 20 | parts = tuple(module_path.parts) 21 | if parts[-1] == "__init__": 22 | parts = parts[:-1] 23 | doc_path = doc_path.with_name("index.md") 24 | full_doc_path = full_doc_path.with_name("index.md") 25 | elif parts[-1] == "__main__": 26 | continue 27 | 28 | with mkdocs_gen_files.open(full_doc_path, "w") as fd: 29 | fd.write(f"# `{parts[-1]}`\n\n::: {'.'.join(parts)}") 30 | 31 | mkdocs_gen_files.set_edit_path(full_doc_path, path) 32 | 33 | readme_content = Path("README.md").read_text(encoding="utf-8") 34 | readme_content = readme_content.replace("](docs/", "](") 35 | # Exclude parts that are between two exact `` lines 36 | readme_content = "\n".join(readme_content.split("\n\n")[::2]) 37 | with mkdocs_gen_files.open("index.md", "w") as index_file: 38 | index_file.write(readme_content) 39 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juftin/hatch-pip-compile/9337724557a2a8efe663d76a98faf96d3176d063/docs/logo.png -------------------------------------------------------------------------------- /docs/notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## Dev Dependencies 4 | 5 | Using the default hatch configuration, dev dependencies listed in your 6 | `default` environment (like `pytest`) will be included on the default lockfile 7 | (`requirements.txt`). If you want to remove your dev dependencies 8 | from the lockfile you must remove them from the `default` environment 9 | on your `pyproject.toml` / `hatch.toml` file. 10 | 11 | ## Disabling Changes to the Lockfile 12 | 13 | In some scenarios, like in CI/CD, you may want to prevent the plugin from 14 | making changes to the lockfile. If you set the `PIP_COMPILE_DISABLE` 15 | environment variable to any non-empty value, the plugin will raise an error 16 | if it detects that the lockfile needs to be updated. 17 | 18 | ```shell 19 | PIP_COMPILE_DISABLE=1 hatch env run python --version 20 | ``` 21 | 22 | ## Manual Installation 23 | 24 | If you want to manually install this plugin instead of adding it to the 25 | `[tool.hatch.env]` table, you can do so with [pipx]: 26 | 27 | ```bash 28 | pipx install hatch 29 | pipx inject hatch hatch-pip-compile 30 | ``` 31 | 32 | `pipx` also supports upgrading the plugin when any new versions are released: 33 | 34 | ```shell 35 | pipx runpip hatch install --upgrade hatch-pip-compile 36 | ``` 37 | 38 | Alternatively, you can install the plugin directly with [pip]: 39 | 40 | ```bash 41 | pip install hatch hatch-pip-compile 42 | ``` 43 | 44 | [pipx]: https://github.com/pypa/pipx 45 | [pip]: https://pip.pypa.io 46 | -------------------------------------------------------------------------------- /docs/upgrading.md: -------------------------------------------------------------------------------- 1 | # Upgrading Dependencies 2 | 3 | Upgrading all dependencies can be as simple as deleting your lockfile and 4 | recreating it by reactivating the environment: 5 | 6 | ```shell 7 | rm requirements.txt 8 | hatch env run --env default -- python --version 9 | ``` 10 | 11 | If you're a user of the `--upgrade` / `--upgrade-package` options on `pip-compile`, 12 | these features can be enabled on this plugin by using the environment variables 13 | `PIP_COMPILE_UPGRADE` and `PIP_COMPILE_UPGRADE_PACKAGE`. When either of these 14 | environment variables are set `hatch` will force the lockfile to be regenerated 15 | whenever the environment is activated. 16 | 17 | > NOTE: **command line interface** 18 | > 19 | > `hatch-pip-compile` also makes a CLI available to handle the 20 | > the `PIP_COMPILE_UPGRADE` / `PIP_COMPILE_UPGRADE_PACKAGE` workflow 21 | > automatically. See the [hatch-pip-compile CLI](cli_usage.md#using-the-hatch-pip-compile-cli) 22 | > section for more information. 23 | 24 | To run with `upgrade` functionality on the `default` environment: 25 | 26 | ```shell 27 | PIP_COMPILE_UPGRADE=1 hatch env run --env default -- python --version 28 | ``` 29 | 30 | To run with `upgrade-package` functionality on the `docs` environment: 31 | 32 | ```shell 33 | PIP_COMPILE_UPGRADE_PACKAGE="mkdocs,mkdocs-material" hatch env run --env docs -- python --version 34 | ``` 35 | 36 | The above commands call `python --version` on a particular environment, 37 | but the same behavior applies to any script that activates the environment. 38 | -------------------------------------------------------------------------------- /hatch_pip_compile/__about__.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch-pip-compile info file 3 | """ 4 | 5 | __author__ = "Justin Flannery" 6 | __email__ = "juftin@juftin.com" 7 | __application__ = "hatch-pip-compile" 8 | __version__ = "1.11.5" 9 | -------------------------------------------------------------------------------- /hatch_pip_compile/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch-pip-compile 3 | """ 4 | 5 | from hatch_pip_compile.__about__ import __application__, __author__, __email__, __version__ 6 | 7 | __all__ = [ 8 | "__application__", 9 | "__version__", 10 | "__author__", 11 | "__email__", 12 | ] 13 | -------------------------------------------------------------------------------- /hatch_pip_compile/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch_pip_compile module hook 3 | """ 4 | 5 | from hatch_pip_compile.cli import cli 6 | 7 | if __name__ == "__main__": 8 | cli() 9 | -------------------------------------------------------------------------------- /hatch_pip_compile/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Base classes for hatch-pip-compile 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | from typing import TYPE_CHECKING, ClassVar 8 | 9 | from hatchling.dep.core import dependencies_in_sync 10 | from packaging.requirements import Requirement 11 | 12 | if TYPE_CHECKING: 13 | from hatch_pip_compile.plugin import PipCompileEnvironment 14 | 15 | 16 | class HatchPipCompileBase: 17 | """ 18 | Base Class for hatch-pip-compile tools 19 | """ 20 | 21 | pypi_dependencies: ClassVar[list[str]] = [] 22 | 23 | def __init__(self, environment: PipCompileEnvironment) -> None: 24 | """ 25 | Inject the environment into the base class 26 | """ 27 | self.environment = environment 28 | self.pypi_dependencies_installed = False 29 | 30 | def install_pypi_dependencies(self) -> None: 31 | """ 32 | Install the resolver from PyPI 33 | """ 34 | if not self.pypi_dependencies: 35 | return 36 | elif self.pypi_dependencies_installed: 37 | return 38 | with self.environment.safe_activation(): 39 | in_sync = dependencies_in_sync( 40 | requirements=[Requirement(item) for item in self.pypi_dependencies], 41 | sys_path=self.environment.virtual_env.sys_path, 42 | environment=self.environment.virtual_env.environment, 43 | ) 44 | if not in_sync: 45 | self.environment.plugin_check_command( 46 | self.environment.construct_pip_install_command(self.pypi_dependencies) 47 | ) 48 | self.pypi_dependencies_installed = True 49 | -------------------------------------------------------------------------------- /hatch_pip_compile/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch-pip-compile CLI 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | import dataclasses 8 | import json 9 | import os 10 | import subprocess 11 | from typing import Any, Sequence 12 | 13 | import click 14 | import rich.traceback 15 | 16 | from hatch_pip_compile.__about__ import __application__, __version__ 17 | 18 | 19 | @dataclasses.dataclass 20 | class HatchCommandRunner: 21 | """ 22 | Hatch Command Runner 23 | """ 24 | 25 | environments: Sequence[str] = dataclasses.field(default_factory=list) 26 | upgrade: bool = False 27 | upgrade_all: bool = False 28 | upgrade_packages: Sequence[str] = dataclasses.field(default_factory=list) 29 | 30 | former_env_vars: dict[str, Any] = dataclasses.field(init=False, default_factory=dict) 31 | console: rich.console.Console = dataclasses.field(init=False) 32 | supported_environments: set[str] = dataclasses.field(init=False) 33 | 34 | def __post_init__(self): 35 | """ 36 | Initialize the internal state 37 | """ 38 | self.console = rich.console.Console() 39 | rich.traceback.install(show_locals=True, console=self.console) 40 | self.supported_environments = self._get_supported_environments() 41 | if all( 42 | [not self.environments, "default" in self.supported_environments, not self.upgrade_all] 43 | ): 44 | self.environments = ["default"] 45 | elif not self.environments and not self.upgrade_all: 46 | msg = "Either `--all` or an environment name must be specified" 47 | raise click.BadParameter(msg) 48 | elif self.upgrade_all: 49 | self.environments = list(self.supported_environments) 50 | unsupported_environments = set(self.environments).difference(self.supported_environments) 51 | if unsupported_environments: 52 | msg = ( 53 | f"The following environments are not supported or unknown: " 54 | f"{', '.join(unsupported_environments)}. " 55 | f"Supported environments are: {', '.join(sorted(self.supported_environments))}" 56 | ) 57 | raise click.BadParameter(msg) 58 | 59 | def __enter__(self) -> HatchCommandRunner: 60 | """ 61 | Set the environment variables 62 | """ 63 | env_vars = {"__PIP_COMPILE_FORCE__": "1"} 64 | if self.upgrade: 65 | env_vars["PIP_COMPILE_UPGRADE"] = "1" 66 | self.console.print( 67 | "[bold green]hatch-pip-compile[/bold green]: Upgrading all dependencies" 68 | ) 69 | elif self.upgrade_packages: 70 | env_vars["PIP_COMPILE_UPGRADE_PACKAGE"] = ",".join(self.upgrade_packages) 71 | message = ( 72 | "[bold green]hatch-pip-compile[/bold green]: " 73 | f"Upgrading packages: {', '.join(self.upgrade_packages)}" 74 | ) 75 | self.console.print(message) 76 | self.former_env_vars = { 77 | key: os.environ.get(key) for key in env_vars.keys() if os.environ.get(key) is not None 78 | } 79 | os.environ.update(env_vars) 80 | return self 81 | 82 | def __exit__(self, *args, **kwargs): 83 | """ 84 | Restore the environment variables 85 | """ 86 | os.environ.update(self.former_env_vars) 87 | 88 | def hatch_cli(self): 89 | """ 90 | Run the `hatch` CLI 91 | """ 92 | self.console.print( 93 | "[bold green]hatch-pip-compile[/bold green]: Targeting environments: " 94 | f"{', '.join(sorted(self.environments))}" 95 | ) 96 | for environment in sorted(self.environments): 97 | environment_command = [ 98 | "hatch", 99 | "env", 100 | "run", 101 | "--env", 102 | environment, 103 | "--", 104 | "python", 105 | "--version", 106 | ] 107 | self.console.print( 108 | f"[bold green]hatch-pip-compile[/bold green]: Running " 109 | f"`[bold blue]{' '.join(environment_command)}`[/bold blue]" 110 | ) 111 | result = subprocess.run( 112 | args=environment_command, 113 | capture_output=True, 114 | check=False, 115 | ) 116 | if result.returncode != 0: # pragma: no cover 117 | self.console.print( 118 | "[bold yellow]hatch command[/bold yellow]: " 119 | f"[bold blue]`{' '.join(environment_command)}`[/bold blue]" 120 | ) 121 | self.console.print(result.stdout.decode("utf-8")) 122 | self.console.print( 123 | "[bold red]hatch-pip-compile[/bold red]: Error running hatch command" 124 | ) 125 | raise click.exceptions.Exit(1) 126 | 127 | @classmethod 128 | def _get_supported_environments(cls) -> set[str]: 129 | """ 130 | Get the names of the environments from `hatch env show --json` 131 | 132 | Returns 133 | ------- 134 | List[str] 135 | The name of the environments 136 | """ 137 | result = subprocess.run( 138 | args=["hatch", "env", "show", "--json"], 139 | capture_output=True, 140 | check=True, 141 | ) 142 | environment_dict: dict[str, Any] = json.loads(result.stdout) 143 | return { 144 | key for key, value in environment_dict.items() if value.get("type") == "pip-compile" 145 | } 146 | 147 | 148 | @click.command("hatch-pip-compile") 149 | @click.version_option(version=__version__, prog_name=__application__) 150 | @click.argument("environment", default=None, type=click.STRING, required=False, nargs=-1) 151 | @click.option( 152 | "-U", 153 | "--upgrade/--no-upgrade", 154 | is_flag=True, 155 | default=False, 156 | help="Try to upgrade all dependencies to their latest versions", 157 | ) 158 | @click.option( 159 | "-P", 160 | "--upgrade-package", 161 | "upgrade_packages", 162 | nargs=1, 163 | multiple=True, 164 | help="Specify a particular package to upgrade; may be used more than once", 165 | ) 166 | @click.option( 167 | "--all", 168 | "upgrade_all", 169 | is_flag=True, 170 | default=False, 171 | help="Upgrade all environments", 172 | ) 173 | def cli( 174 | environment: Sequence[str], 175 | upgrade: bool, 176 | upgrade_packages: Sequence[str], 177 | upgrade_all: bool, 178 | ): 179 | """ 180 | Upgrade your `hatch-pip-compile` managed dependencies 181 | from the command line. 182 | """ 183 | with HatchCommandRunner( 184 | environments=environment, 185 | upgrade=upgrade, 186 | upgrade_packages=upgrade_packages, 187 | upgrade_all=upgrade_all, 188 | ) as hatch_runner: 189 | hatch_runner.hatch_cli() 190 | 191 | 192 | if __name__ == "__main__": 193 | cli() 194 | -------------------------------------------------------------------------------- /hatch_pip_compile/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch-pip-compile exceptions 3 | """ 4 | 5 | 6 | class HatchPipCompileError(Exception): 7 | """ 8 | Base exception for hatch-pip-compile 9 | """ 10 | 11 | 12 | class LockFileNotFoundError(HatchPipCompileError, FileNotFoundError): 13 | """ 14 | A lock file was not found 15 | """ 16 | 17 | 18 | class LockFileError(HatchPipCompileError, ValueError): 19 | """ 20 | A lock file content Error 21 | """ 22 | -------------------------------------------------------------------------------- /hatch_pip_compile/hooks.py: -------------------------------------------------------------------------------- 1 | """ 2 | Hatch Plugin Registration 3 | """ 4 | 5 | from typing import Type 6 | 7 | from hatchling.plugin import hookimpl 8 | 9 | from hatch_pip_compile.plugin import PipCompileEnvironment 10 | 11 | 12 | @hookimpl 13 | def hatch_register_environment() -> Type[PipCompileEnvironment]: 14 | """ 15 | Register the PipCompileEnvironment plugin with Hatch 16 | """ 17 | return PipCompileEnvironment 18 | -------------------------------------------------------------------------------- /hatch_pip_compile/installer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Package + Dependency Installers 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | from abc import ABC, abstractmethod 8 | from typing import ClassVar 9 | 10 | from hatch.env.utils import add_verbosity_flag 11 | 12 | from hatch_pip_compile.base import HatchPipCompileBase 13 | 14 | 15 | class PluginInstaller(HatchPipCompileBase, ABC): 16 | """ 17 | Package Installer for the plugin 18 | 19 | This abstract base class is used to define the interface for 20 | how the plugin should install packages and dependencies. 21 | """ 22 | 23 | @abstractmethod 24 | def install_dependencies(self) -> None: 25 | """ 26 | Install the dependencies 27 | """ 28 | 29 | def sync_dependencies(self) -> None: 30 | """ 31 | Sync the dependencies - same as `install_dependencies` 32 | """ 33 | self.install_pypi_dependencies() 34 | self.install_dependencies() 35 | 36 | def construct_pip_install_command(self, args: list[str]) -> list[str]: 37 | """ 38 | Construct a `pip install` command with the given arguments 39 | """ 40 | return self.environment.construct_pip_install_command(args) 41 | 42 | def install_project(self) -> None: 43 | """ 44 | Install the project (`--no-deps`) 45 | """ 46 | self.install_pypi_dependencies() 47 | with self.environment.safe_activation(): 48 | self.environment.plugin_check_command( 49 | self.construct_pip_install_command(args=["--no-deps", str(self.environment.root)]) 50 | ) 51 | 52 | def install_project_dev_mode(self) -> None: 53 | """ 54 | Install the project in editable mode (`--no-deps`) 55 | """ 56 | self.install_pypi_dependencies() 57 | with self.environment.safe_activation(): 58 | self.environment.plugin_check_command( 59 | self.construct_pip_install_command( 60 | args=["--no-deps", "--editable", str(self.environment.root)] 61 | ) 62 | ) 63 | 64 | 65 | class PipInstaller(PluginInstaller): 66 | """ 67 | Plugin Installer for `pip` 68 | """ 69 | 70 | def install_dependencies(self) -> None: 71 | """ 72 | Install the dependencies with `pip` 73 | """ 74 | self.install_pypi_dependencies() 75 | with self.environment.safe_activation(): 76 | if not self.environment.piptools_lock_file.exists(): 77 | return 78 | extra_args = self.environment.config.get("pip-compile-install-args", []) 79 | args = [*extra_args, "--requirement", str(self.environment.piptools_lock_file)] 80 | install_command = self.construct_pip_install_command(args=args) 81 | self.environment.plugin_check_command(install_command) 82 | 83 | 84 | class UvInstaller(PipInstaller): 85 | """ 86 | Plugin Installer for `uv` 87 | """ 88 | 89 | pypi_dependencies: ClassVar[list[str]] = ["uv"] 90 | 91 | def construct_pip_install_command(self, args: list[str]) -> list[str]: 92 | """ 93 | Construct a `pip install` command with the given arguments 94 | """ 95 | command = [ 96 | "python", 97 | "-m", 98 | "uv", 99 | "pip", 100 | "install", 101 | ] 102 | add_verbosity_flag(command, self.environment.verbosity, adjustment=-1) 103 | command.extend(args) 104 | return command 105 | 106 | 107 | class PipSyncInstaller(PluginInstaller): 108 | """ 109 | Plugin Installer for `pip-sync` 110 | """ 111 | 112 | pypi_dependencies: ClassVar[list[str]] = ["pip-tools"] 113 | 114 | def install_dependencies(self) -> None: 115 | """ 116 | Install the dependencies with `pip-sync` 117 | 118 | In the event that there are no dependencies, pip-sync will 119 | uninstall everything in the environment before deleting the 120 | lockfile. 121 | """ 122 | self.install_pypi_dependencies() 123 | cmd = [ 124 | self.environment.virtual_env.python_info.executable, 125 | "-m", 126 | "piptools", 127 | "sync", 128 | "--verbose" 129 | if self.environment.config.get("pip-compile-verbose", None) is True 130 | else "--quiet", 131 | "--python-executable", 132 | str(self.environment.virtual_env.python_info.executable), 133 | ] 134 | if not self.environment.dependencies: 135 | self.environment.piptools_lock_file.write_text("") 136 | extra_args = self.environment.config.get("pip-compile-install-args", []) 137 | cmd.extend(extra_args) 138 | cmd.append(str(self.environment.piptools_lock_file)) 139 | self.environment.plugin_check_command(cmd) 140 | if not self.environment.dependencies: 141 | self.environment.piptools_lock_file.unlink() 142 | 143 | def _full_install(self) -> None: 144 | """ 145 | Run the full install process 146 | 147 | 1) Run pip-compile (if necessary) 148 | 2) Run pip-sync 149 | 3) (re)install project 150 | """ 151 | with self.environment.safe_activation(): 152 | self.environment.run_pip_compile() 153 | self.install_dependencies() 154 | if not self.environment.skip_install: 155 | if self.environment.dev_mode: 156 | super().install_project_dev_mode() 157 | else: 158 | super().install_project() 159 | 160 | def sync_dependencies(self): 161 | """ 162 | Sync dependencies 163 | """ 164 | self._full_install() 165 | 166 | def install_project(self): 167 | """ 168 | Install the project the first time 169 | 170 | The same implementation as `_full_install` 171 | due to the way `pip-sync` uninstalls our root package 172 | """ 173 | self._full_install() 174 | 175 | def install_project_dev_mode(self): 176 | """ 177 | Install the project the first time in dev mode 178 | 179 | The same implementation as `_full_install` 180 | due to the way `pip-sync` uninstalls our root package 181 | """ 182 | self._full_install() 183 | -------------------------------------------------------------------------------- /hatch_pip_compile/lock.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch-pip-compile header operations 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | import hashlib 8 | import logging 9 | import pathlib 10 | import re 11 | from textwrap import dedent 12 | from typing import Iterable 13 | 14 | from packaging.requirements import Requirement 15 | from packaging.version import Version 16 | from piptools._compat.pip_compat import PipSession, parse_requirements 17 | 18 | from hatch_pip_compile.base import HatchPipCompileBase 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | 23 | class PipCompileLock(HatchPipCompileBase): 24 | """ 25 | Pip Compile Lock File Operations 26 | """ 27 | 28 | def process_lock(self, lockfile: pathlib.Path) -> None: 29 | """ 30 | Post process lockfile 31 | """ 32 | version = f"{self.current_python_version.major}.{self.current_python_version.minor}" 33 | raw_prefix = f""" 34 | # 35 | # This file is autogenerated by hatch-pip-compile with Python {version} 36 | # 37 | """ 38 | prefix = dedent(raw_prefix).strip() 39 | joined_dependencies = "\n".join([f"# - {dep}" for dep in self.environment.dependencies]) 40 | lockfile_text = lockfile.read_text() 41 | cleaned_input_file = self.replace_temporary_lockfile(lockfile_text=lockfile_text) 42 | if self.environment.piptools_constraints_file is not None: 43 | lockfile_contents = self.environment.piptools_constraints_file.read_bytes() 44 | cross_platform_contents = lockfile_contents.replace(b"\r\n", b"\n") 45 | constraint_sha = hashlib.sha256(cross_platform_contents).hexdigest() 46 | constraints_path = self.environment.piptools_constraints_file.relative_to( 47 | self.environment.root 48 | ).as_posix() 49 | constraints_line = f"# [constraints] {constraints_path} (SHA256: {constraint_sha})" 50 | joined_dependencies = "\n".join([constraints_line, "#", joined_dependencies]) 51 | cleaned_input_file = re.sub( 52 | r"-c \S*", 53 | lambda _: f"-c {constraints_path}", 54 | cleaned_input_file, 55 | ) 56 | prefix += "\n" + joined_dependencies + "\n#" 57 | new_text = prefix + "\n\n" + cleaned_input_file 58 | lockfile.write_text(new_text) 59 | 60 | def read_header_requirements(self) -> list[Requirement]: 61 | """ 62 | Read requirements from lock file header 63 | """ 64 | lock_file_text = self.environment.piptools_lock_file.read_text() 65 | parsed_requirements = [] 66 | for line in lock_file_text.splitlines(): 67 | if line.startswith("# - "): 68 | requirement = Requirement(line[4:]) 69 | parsed_requirements.append(requirement) 70 | elif not line.startswith("#"): 71 | break 72 | return parsed_requirements 73 | 74 | @property 75 | def current_python_version(self) -> Version: 76 | """ 77 | Get python version 78 | 79 | In the case of running as a hatch plugin, the `virtualenv` will be set, 80 | otherwise it will be None and the Python version will be read differently. 81 | """ 82 | if self.environment.virtual_env is not None: 83 | return Version(self.environment.virtual_env.environment["python_version"]) 84 | else: 85 | msg = "VirtualEnv is not set" 86 | raise NotImplementedError(msg) 87 | 88 | @property 89 | def lock_file_version(self) -> Version | None: 90 | """ 91 | Get lock file version 92 | """ 93 | lock_file_text = self.environment.piptools_lock_file.read_text() 94 | match = re.search( 95 | r"# This file is autogenerated by hatch-pip-compile with Python (.*)", lock_file_text 96 | ) 97 | if match is None: 98 | logger.error( 99 | "[hatch-pip-compile] Non hatch-pip-compile lock file detected (%s)", 100 | self.environment.piptools_lock_file.name, 101 | ) 102 | return None 103 | return Version(match.group(1)) 104 | 105 | def compare_python_versions(self, verbose: bool | None = None) -> bool: 106 | """ 107 | Compare python versions 108 | 109 | Parameters 110 | ---------- 111 | verbose : Optional[bool] 112 | Print warning if python versions are different, by default None 113 | which will print the warning. Used as a plugin flag. 114 | """ 115 | lock_version = self.lock_file_version 116 | if lock_version is None: 117 | return False 118 | current_version = self.current_python_version 119 | match = (current_version.major == lock_version.major) and ( 120 | current_version.minor == lock_version.minor 121 | ) 122 | if match is False and verbose is not False: 123 | logger.error( 124 | "[hatch-pip-compile] Your Python version is different " 125 | "from the lock file, your results may vary." 126 | ) 127 | return lock_version == current_version 128 | 129 | def compare_requirements(self, requirements: Iterable[Requirement]) -> bool: 130 | """ 131 | Compare requirements 132 | 133 | Parameters 134 | ---------- 135 | requirements : Iterable[Requirement] 136 | List of requirements to compare against the lock file 137 | """ 138 | lock_requirements = self.read_header_requirements() 139 | return set(requirements) == set(lock_requirements) 140 | 141 | def compare_constraint_sha(self, sha: str) -> bool: 142 | """ 143 | Compare SHA to the SHA on the lockfile 144 | """ 145 | lock_file_text = self.environment.piptools_lock_file.read_text() 146 | match = re.search(r"# \[constraints\] \S* \(SHA256: (.*)\)", lock_file_text) 147 | if match is None: 148 | return False 149 | return match.group(1).strip() == sha.strip() 150 | 151 | def get_file_content_hash(self) -> str: 152 | """ 153 | Get hash of lock file 154 | """ 155 | lockfile_contents = self.environment.piptools_lock_file.read_bytes() 156 | cross_platform_contents = lockfile_contents.replace(b"\r\n", b"\n") 157 | return hashlib.sha256(cross_platform_contents).hexdigest() 158 | 159 | def read_lock_requirements(self) -> list[Requirement]: 160 | """ 161 | Read all requirements from lock file 162 | """ 163 | if not self.environment.dependencies: 164 | return [] 165 | install_requirements = parse_requirements( 166 | str(self.environment.piptools_lock_file), 167 | session=PipSession(), 168 | ) 169 | return [ireq.req for ireq in install_requirements] # type: ignore[misc] 170 | 171 | def replace_temporary_lockfile(self, lockfile_text: str) -> str: 172 | """ 173 | Replace the temporary lockfile with the new lockfile 174 | """ 175 | cleaned_input_file = re.sub( 176 | rf"-r \S*[\\/]{self.environment.name}\.in", 177 | f"hatch.envs.{self.environment.name}", 178 | lockfile_text, 179 | ) 180 | return cleaned_input_file 181 | -------------------------------------------------------------------------------- /hatch_pip_compile/plugin.py: -------------------------------------------------------------------------------- 1 | """ 2 | hatch-pip-compile plugin 3 | """ 4 | 5 | import functools 6 | import hashlib 7 | import logging 8 | import os 9 | import pathlib 10 | import shutil 11 | import tempfile 12 | from subprocess import CompletedProcess 13 | from typing import Any, ClassVar, Dict, List, Optional, Type, Union 14 | 15 | import hatch.cli 16 | from hatch.env.virtual import VirtualEnvironment 17 | from hatch.utils.platform import Platform 18 | from hatchling.dep.core import dependencies_in_sync 19 | 20 | from hatch_pip_compile.exceptions import HatchPipCompileError 21 | from hatch_pip_compile.installer import PipInstaller, PipSyncInstaller, PluginInstaller, UvInstaller 22 | from hatch_pip_compile.lock import PipCompileLock 23 | from hatch_pip_compile.resolver import BaseResolver, PipCompileResolver, UvResolver 24 | 25 | logger = logging.getLogger(__name__) 26 | 27 | 28 | class PipCompileEnvironment(VirtualEnvironment): 29 | """ 30 | Virtual Environment supported by pip-compile 31 | """ 32 | 33 | PLUGIN_NAME: ClassVar[str] = "pip-compile" 34 | default_env_name: ClassVar[str] = "default" 35 | dependency_resolvers: ClassVar[Dict[str, Type[BaseResolver]]] = { 36 | "pip-compile": PipCompileResolver, 37 | "uv": UvResolver, 38 | } 39 | dependency_installers: ClassVar[Dict[str, Type[PluginInstaller]]] = { 40 | "pip": PipInstaller, 41 | "pip-sync": PipSyncInstaller, 42 | "uv": UvInstaller, 43 | } 44 | 45 | def __repr__(self): 46 | """ 47 | Get representation of PipCompileEnvironment 48 | """ 49 | return f"<{self.__class__.__name__} - {self.name}>" 50 | 51 | def __init__(self, *args, **kwargs) -> None: 52 | """ 53 | Initialize PipCompileEnvironment with extra attributes 54 | """ 55 | super().__init__(*args, **kwargs) 56 | lock_filename_config = self.config.get("lock-filename") 57 | if lock_filename_config is None: 58 | if self.name == self.default_env_name: 59 | lock_filename = "requirements.txt" 60 | else: 61 | lock_filename = f"requirements/requirements-{self.name}.txt" 62 | else: 63 | with self.metadata.context.apply_context(self.context): 64 | lock_filename = self.metadata.context.format(lock_filename_config) 65 | self.piptools_lock_file = self.root / lock_filename 66 | self.piptools_lock = PipCompileLock(environment=self) 67 | install_method = self.config.get("pip-compile-installer", "pip") 68 | resolve_method = self.config.get("pip-compile-resolver", "pip-compile") 69 | if install_method not in self.dependency_installers.keys(): 70 | msg = ( 71 | f"Invalid pip-compile-installer: {install_method} - " 72 | f"must be one of {', '.join(self.dependency_installers.keys())}" 73 | ) 74 | raise HatchPipCompileError(msg) 75 | if resolve_method not in self.dependency_resolvers.keys(): 76 | msg = ( 77 | f"Invalid pip-compile-resolver: {resolve_method} - " 78 | f"must be one of {', '.join(self.dependency_resolvers.keys())}" 79 | ) 80 | raise HatchPipCompileError(msg) 81 | resolver_class = self.dependency_resolvers[resolve_method] 82 | installer_class = self.dependency_installers[install_method] 83 | self.resolver: BaseResolver = resolver_class(environment=self) 84 | self.installer: PluginInstaller = installer_class(environment=self) 85 | 86 | @staticmethod 87 | def get_option_types() -> Dict[str, Any]: 88 | """ 89 | Get option types 90 | """ 91 | return { # pragma: no cover 92 | "lock-filename": str, 93 | "pip-compile-hashes": bool, 94 | "pip-compile-args": list, 95 | "pip-compile-constraint": str, 96 | "pip-compile-installer": str, 97 | "pip-compile-install-args": list, 98 | "pip-compile-resolver": str, 99 | } 100 | 101 | def dependency_hash(self) -> str: 102 | """ 103 | Get the dependency hash 104 | """ 105 | self.run_pip_compile() 106 | hatch_hash = super().dependency_hash() 107 | if not self.dependencies: 108 | return hatch_hash 109 | else: 110 | lockfile_hash = self.piptools_lock.get_file_content_hash() 111 | return hashlib.sha256(f"{hatch_hash}-{lockfile_hash}".encode()).hexdigest() 112 | 113 | def run_pip_compile(self) -> None: 114 | """ 115 | Run pip-compile if necessary 116 | """ 117 | self.prepare_environment() 118 | if not self.lockfile_up_to_date: 119 | with self.safe_activation(): 120 | self.resolver.install_pypi_dependencies() 121 | if self.piptools_lock_file.exists(): 122 | _ = self.piptools_lock.compare_python_versions( 123 | verbose=self.config.get("pip-compile-verbose", None) 124 | ) 125 | self.pip_compile_cli() 126 | 127 | def pip_compile_cli(self) -> None: 128 | """ 129 | Run pip-compile 130 | """ 131 | if not self.dependencies: 132 | self.piptools_lock_file.unlink(missing_ok=True) 133 | self.lockfile_up_to_date = True 134 | return 135 | no_compile = bool(os.getenv("PIP_COMPILE_DISABLE")) 136 | if no_compile: 137 | msg = "hatch-pip-compile is disabled but attempted to run a lockfile update." 138 | raise HatchPipCompileError(msg) 139 | with tempfile.TemporaryDirectory() as tmpdir: 140 | tmp_path = pathlib.Path(tmpdir) 141 | input_file = tmp_path / f"{self.name}.in" 142 | output_file = tmp_path / "lock.txt" 143 | input_file.write_text("\n".join([*self.dependencies, ""])) 144 | if self.piptools_lock_file.exists(): 145 | shutil.copy(self.piptools_lock_file, output_file) 146 | self.piptools_lock_file.parent.mkdir(exist_ok=True, parents=True) 147 | cmd = self.resolver.get_pip_compile_args( 148 | input_file=input_file, 149 | output_file=output_file, 150 | ) 151 | self.plugin_check_command(cmd) 152 | self.piptools_lock.process_lock(lockfile=output_file) 153 | shutil.move(output_file, self.piptools_lock_file) 154 | self.lockfile_up_to_date = True 155 | 156 | def install_project(self) -> None: 157 | """ 158 | Install the project (`--no-deps`) 159 | """ 160 | self.installer.install_project() 161 | 162 | def install_project_dev_mode(self) -> None: 163 | """ 164 | Install the project in editable mode (`--no-deps`) 165 | """ 166 | self.installer.install_project_dev_mode() 167 | 168 | @functools.cached_property 169 | def lockfile_up_to_date(self) -> bool: 170 | """ 171 | Check if the lockfile is up-to-date 172 | 173 | Behavior 174 | -------- 175 | 1) If there are no dependencies and no lock file, exit early and return True. 176 | 2) If the constraint file / environment is out of date, sync it and return False. 177 | 3) If there are no dependencies and a lock file, return False. 178 | 4) If there are dependencies and no lock file, return False. 179 | 5) If a force upgrade is requested, return False. 180 | 6) If there are dependencies and a lock file... 181 | a) If there is a constraint file... 182 | i) If the file is valid but the SHA is different, return False. 183 | b) If the lock file dependencies aren't current, return False. 184 | c) If the lock file dependencies are current but the lockfile 185 | has a different sha than its constraints file, return False. 186 | 7) Otherwise, return True. 187 | """ 188 | upgrade = os.getenv("PIP_COMPILE_UPGRADE") or False 189 | upgrade_packages = os.getenv("PIP_COMPILE_UPGRADE_PACKAGE") or False 190 | pip_compile_force = bool(os.getenv("__PIP_COMPILE_FORCE__")) 191 | force_upgrade = any( 192 | [ 193 | upgrade is not False, 194 | upgrade_packages is not False, 195 | pip_compile_force is not False, 196 | ] 197 | ) 198 | if not self.dependencies and not self.piptools_lock_file.exists(): 199 | return True 200 | if self.piptools_constraints_file: 201 | valid_constraint = self.validate_constraints_file( 202 | constraints_file=self.piptools_constraints_file, environment=self.constraint_env 203 | ) 204 | if not valid_constraint: 205 | return False 206 | if not self.dependencies and self.piptools_lock_file.exists(): 207 | return False 208 | elif force_upgrade: 209 | return False 210 | elif self.dependencies and not self.piptools_lock_file.exists(): 211 | return False 212 | elif self.dependencies and self.piptools_lock_file.exists(): 213 | if self.piptools_constraints_file: 214 | current_sha = self.constraint_env.piptools_lock.get_file_content_hash() 215 | sha_match = self.piptools_lock.compare_constraint_sha(sha=current_sha) 216 | if sha_match is False: 217 | return False 218 | expected_dependencies = self.piptools_lock.compare_requirements( 219 | requirements=self.dependencies_complex 220 | ) 221 | if not expected_dependencies: 222 | return False 223 | return True 224 | 225 | def dependencies_in_sync(self): 226 | """ 227 | Whether the dependencies are in sync 228 | """ 229 | if not self.lockfile_up_to_date: 230 | return False 231 | else: 232 | with self.safe_activation(): 233 | return dependencies_in_sync( 234 | self.piptools_lock.read_lock_requirements(), 235 | sys_path=self.virtual_env.sys_path, 236 | environment=self.virtual_env.environment, 237 | ) 238 | 239 | def sync_dependencies(self) -> None: 240 | """ 241 | Sync dependencies 242 | """ 243 | self.run_pip_compile() 244 | self.installer.sync_dependencies() 245 | 246 | @property 247 | def piptools_constraints_file(self) -> Optional[pathlib.Path]: 248 | """ 249 | Get the constraint file path 250 | """ 251 | if self.constraint_env.name == self.name: 252 | return None 253 | else: 254 | return self.constraint_env.piptools_lock_file 255 | 256 | def get_piptools_environment(self, environment_name: str) -> "PipCompileEnvironment": 257 | """ 258 | Get a `PipCompileEnvironment` instance for an environment 259 | other than the current instance. This is useful 260 | for recursively checking other environments for lock file 261 | validity and defining inheritance. 262 | """ 263 | if environment_name not in self.pipools_environment_dict.keys(): 264 | error_message = ( 265 | f"[hatch-pip-compile] The environment {environment_name} does not exist." 266 | ) 267 | raise HatchPipCompileError(error_message) 268 | if isinstance(self.app, hatch.cli.Application): 269 | env = self.app.get_environment(env_name=environment_name) 270 | else: 271 | env = PipCompileEnvironment( 272 | root=self.root, 273 | metadata=self.metadata, 274 | name=environment_name, 275 | config=self.pipools_environment_dict.get(environment_name, {}), 276 | matrix_variables=self.matrix_variables, 277 | data_directory=self.data_directory, 278 | isolated_data_directory=self.isolated_data_directory, 279 | platform=Platform(), 280 | verbosity=self.verbosity, 281 | app=self.app, 282 | ) 283 | return env 284 | 285 | @functools.cached_property 286 | def constraint_env(self) -> "PipCompileEnvironment": 287 | """ 288 | Get the constraint environment 289 | """ 290 | constraint_env = self.config.get("pip-compile-constraint") 291 | if not constraint_env: 292 | return self 293 | elif self.name == constraint_env: 294 | return self 295 | environment = self.get_piptools_environment(environment_name=constraint_env) 296 | if environment.config.get("type") != self.PLUGIN_NAME: 297 | logger.error("The constraint environment is not a hatch-pip-compile environment.") 298 | return self 299 | elif not environment.dependencies: 300 | return self 301 | else: 302 | return environment 303 | 304 | def validate_constraints_file( 305 | self, constraints_file: pathlib.Path, environment: "PipCompileEnvironment" 306 | ) -> bool: 307 | """ 308 | Validate the constraints file 309 | 310 | Parameters 311 | ---------- 312 | constraints_file : pathlib.Path 313 | The lock file 314 | environment : PipCompileEnvironment 315 | The environment to validate against 316 | 317 | Returns 318 | ------- 319 | bool 320 | Whether the constraints file is valid 321 | """ 322 | if not constraints_file.exists(): 323 | self.constraint_env.run_pip_compile() 324 | return False 325 | else: 326 | up_to_date = environment.piptools_lock.compare_requirements( 327 | requirements=environment.dependencies_complex 328 | ) 329 | if not up_to_date: 330 | self.constraint_env.run_pip_compile() 331 | return False 332 | return True 333 | 334 | @property 335 | def pipools_environment_dict(self) -> Dict[str, Any]: 336 | """ 337 | Get the environment dictionary 338 | """ 339 | return self.metadata.hatch.config.get("envs", {}) 340 | 341 | def plugin_check_command( 342 | self, command: Union[str, List[str]], *, shell: bool = False, **kwargs: Any 343 | ) -> CompletedProcess: 344 | """ 345 | Run a command from the virtualenv 346 | """ 347 | with self.safe_activation(): 348 | return self.virtual_env.platform.check_command( 349 | command=command, 350 | shell=shell, 351 | **kwargs, 352 | ) 353 | 354 | def virtualenv_exists(self) -> bool: 355 | """ 356 | Check if the virtualenv exists 357 | """ 358 | try: 359 | _ = self.virtual_env.executables_directory 360 | return True 361 | except OSError: 362 | return False 363 | 364 | def prepare_environment(self) -> None: 365 | """ 366 | Prepare the environment 367 | 368 | Ideally, we could access the `self.app` attribute, 369 | but this ultimately leads to a recursion error when 370 | `self.app.prepare_environment()` is called. 371 | """ 372 | if not self.virtualenv_exists(): 373 | self.create() 374 | if not self.dependencies_in_sync(): 375 | self.sync_dependencies() 376 | if not self.skip_install: 377 | if self.dev_mode: 378 | self.install_project_dev_mode() 379 | else: 380 | self.install_project() 381 | -------------------------------------------------------------------------------- /hatch_pip_compile/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juftin/hatch-pip-compile/9337724557a2a8efe663d76a98faf96d3176d063/hatch_pip_compile/py.typed -------------------------------------------------------------------------------- /hatch_pip_compile/resolver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dependency Resolvers 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | import os 8 | from abc import ABC, abstractmethod 9 | from typing import ClassVar 10 | 11 | from hatch_pip_compile.base import HatchPipCompileBase 12 | 13 | 14 | class BaseResolver(HatchPipCompileBase, ABC): 15 | """ 16 | Base Resolver for the plugin 17 | """ 18 | 19 | resolver_options: ClassVar[list[str]] = [] 20 | 21 | @property 22 | @abstractmethod 23 | def resolver_executable(self) -> list[str]: 24 | """ 25 | Resolver Executable 26 | """ 27 | 28 | def get_pip_compile_args(self, input_file: os.PathLike, output_file: os.PathLike) -> list[str]: 29 | """ 30 | Get the pip compile arguments 31 | """ 32 | upgrade = bool(os.getenv("PIP_COMPILE_UPGRADE")) 33 | upgrade_packages = os.getenv("PIP_COMPILE_UPGRADE_PACKAGE") or None 34 | upgrade_args = [] 35 | upgrade_package_args = [] 36 | if upgrade: 37 | upgrade_args.append("--upgrade") 38 | if upgrade_packages: 39 | upgrade_packages_sep = upgrade_packages.split(",") 40 | for package in upgrade_packages_sep: 41 | upgrade_package_args.append(f"--upgrade-package={package.strip()}") 42 | cmd = [ 43 | *self.resolver_executable, 44 | "--verbose" 45 | if self.environment.config.get("pip-compile-verbose", None) is True 46 | else "--quiet", 47 | "--no-header", 48 | *self.resolver_options, 49 | ] 50 | if self.environment.config.get("pip-compile-hashes", False) is True: 51 | cmd.append("--generate-hashes") 52 | if self.environment.piptools_constraints_file is not None: 53 | cmd.extend(["--constraint", str(self.environment.piptools_constraints_file)]) 54 | cmd.extend(self.environment.config.get("pip-compile-args", [])) 55 | cmd.extend(upgrade_args) 56 | cmd.extend(upgrade_package_args) 57 | cmd.extend(["--output-file", str(output_file), str(input_file)]) 58 | return cmd 59 | 60 | 61 | class PipCompileResolver(BaseResolver): 62 | """ 63 | Pip Compile Resolver 64 | """ 65 | 66 | pypi_dependencies: ClassVar[list[str]] = ["pip-tools"] 67 | resolver_options: ClassVar[list[str]] = ["--resolver=backtracking", "--strip-extras"] 68 | 69 | @property 70 | def resolver_executable(self) -> list[str]: 71 | """ 72 | Resolver Executable 73 | """ 74 | return [ 75 | self.environment.virtual_env.python_info.executable, 76 | "-m", 77 | "piptools", 78 | "compile", 79 | ] 80 | 81 | 82 | class UvResolver(BaseResolver): 83 | """ 84 | Uv Resolver 85 | """ 86 | 87 | pypi_dependencies: ClassVar[list[str]] = ["uv"] 88 | 89 | @property 90 | def resolver_executable(self) -> list[str]: 91 | """ 92 | Resolver Executable 93 | """ 94 | return [ 95 | self.environment.virtual_env.python_info.executable, 96 | "-m", 97 | "uv", 98 | "pip", 99 | "compile", 100 | ] 101 | -------------------------------------------------------------------------------- /mkdocs.yaml: -------------------------------------------------------------------------------- 1 | # $schema: https://squidfunk.github.io/mkdocs-material/schema.json 2 | 3 | site_name: hatch-pip-compile 4 | nav: 5 | - Home 🏠: index.md 6 | - Examples 📚: examples.md 7 | - Upgrading 🚀: upgrading.md 8 | - Command Line Usage 📦: cli_usage.md 9 | - Notes 📝: notes.md 10 | - Command Line Docs ⌨️: cli.md 11 | - API Documentation 🤖: reference/ 12 | - Contributing 🤝: contributing.md 13 | theme: 14 | favicon: https://juftin.com/favicon.ico 15 | logo: https://raw.githubusercontent.com/juftin/juftin/main/static/juftin.png 16 | name: material 17 | features: 18 | - navigation.tracking 19 | - content.code.annotate 20 | - content.code.copy 21 | palette: 22 | - media: "(prefers-color-scheme: light)" 23 | scheme: default 24 | accent: purple 25 | toggle: 26 | icon: material/weather-sunny 27 | name: Switch to dark mode 28 | - media: "(prefers-color-scheme: dark)" 29 | scheme: slate 30 | primary: black 31 | toggle: 32 | icon: material/weather-night 33 | name: Switch to light mode 34 | repo_url: https://github.com/juftin/hatch-pip-compile 35 | repo_name: hatch-pip-compile 36 | edit_uri: blob/main/docs/ 37 | site_author: juftin 38 | remote_branch: gh-pages 39 | extra: 40 | generator: false 41 | exclude_docs: | 42 | gen_pages.py 43 | markdown_extensions: 44 | - toc: 45 | permalink: "#" 46 | - pymdownx.snippets: 47 | base_path: 48 | - !relative $config_dir 49 | - pymdownx.magiclink 50 | - attr_list 51 | - md_in_html 52 | - pymdownx.highlight: 53 | anchor_linenums: true 54 | - pymdownx.inlinehilite 55 | - pymdownx.superfences 56 | - markdown.extensions.attr_list 57 | - pymdownx.keys 58 | - pymdownx.tasklist 59 | - pymdownx.tilde 60 | - callouts 61 | - pymdownx.details 62 | - pymdownx.emoji 63 | - pymdownx.tabbed: 64 | alternate_style: true 65 | - mkdocs-click 66 | plugins: 67 | - search 68 | - markdown-exec 69 | - gen-files: 70 | scripts: 71 | - docs/gen_pages.py 72 | - literate-nav: 73 | nav_file: SUMMARY.md 74 | - section-index 75 | - mkdocstrings: 76 | handlers: 77 | python: 78 | import: 79 | - https://docs.python.org/3/objects.inv 80 | options: 81 | docstring_style: numpy 82 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "hatchling.build" 3 | requires = ["hatchling"] 4 | 5 | [project] 6 | authors = [ 7 | {name = "Justin Flannery", email = "juftin@juftin.com"} 8 | ] 9 | classifiers = [ 10 | "Development Status :: 3 - Alpha", 11 | "Framework :: Hatch", 12 | "Operating System :: OS Independent", 13 | "Programming Language :: Python", 14 | "Programming Language :: Python :: 3.8", 15 | "Programming Language :: Python :: 3.9", 16 | "Programming Language :: Python :: 3.10", 17 | "Programming Language :: Python :: 3.11", 18 | "Programming Language :: Python :: 3.12", 19 | "Programming Language :: Python :: Implementation :: CPython", 20 | "Programming Language :: Python :: Implementation :: PyPy" 21 | ] 22 | dependencies = [ 23 | "hatch>=1.7.0,<2", 24 | "pip-tools>=6", 25 | "click", 26 | "rich" 27 | ] 28 | description = "hatch plugin to use pip-compile to manage project dependencies" 29 | dynamic = ["version"] 30 | license = "MIT" 31 | name = "hatch-pip-compile" 32 | readme = "README.md" 33 | requires-python = ">=3.8" 34 | 35 | [project.entry-points.hatch] 36 | hatch-pip-compile = "hatch_pip_compile.hooks" 37 | 38 | [project.scripts] 39 | hatch-pip-compile = "hatch_pip_compile.cli:cli" 40 | 41 | [project.urls] 42 | Documentation = "https://juftin.github.io/hatch-pip-compile" 43 | Issues = "https://juftin.github.io/hatch-pip-compile/issues" 44 | Source = "https://github.com/juftin/hatch-pip-compile" 45 | 46 | [tool.coverage.paths] 47 | hatch_pip_compile = ["hatch_pip_compile", "*/hatch-pip-compile/hatch_pip_compile"] 48 | tests = ["tests", "*/hatch-pip-compile/tests"] 49 | 50 | [tool.coverage.report] 51 | exclude_lines = [ 52 | "no cov", 53 | "if __name__ == .__main__.:", 54 | "if TYPE_CHECKING:" 55 | ] 56 | show_missing = true 57 | 58 | [tool.coverage.run] 59 | branch = true 60 | disable_warnings = [ 61 | "module-not-measured" 62 | ] 63 | omit = [ 64 | "hatch_pip_compile/__about__.py", 65 | "hatch_pip_compile/hooks.py", 66 | "hatch_pip_compile/__main__.py" 67 | ] 68 | parallel = true 69 | source_pkgs = ["hatch_pip_compile", "tests"] 70 | 71 | [tool.hatch.env] 72 | requires = ["hatch-mkdocs"] 73 | 74 | [tool.hatch.env.collectors.mkdocs.docs] 75 | path = "mkdocs.yaml" 76 | 77 | [tool.hatch.envs.default] 78 | pip-compile-constraint = "default" 79 | post-install-commands = [ 80 | "- pre-commit install" 81 | ] 82 | python = "3.11" 83 | type = "pip-compile" 84 | 85 | [tool.hatch.envs.default.scripts] 86 | cov = "hatch run test:cov {args:}" 87 | test = "hatch run test:test {args:}" 88 | 89 | [tool.hatch.envs.docs] 90 | detached = false 91 | type = "pip-compile" 92 | 93 | [tool.hatch.envs.gen] 94 | detached = true 95 | 96 | [tool.hatch.envs.gen.scripts] 97 | lock-all = "hatch env run --env default -- python -m hatch_pip_compile --all {args:}" 98 | release = [ 99 | "npm install --prefix .github/semantic_release/", 100 | "npx --prefix .github/semantic_release/ semantic-release {args:}" 101 | ] 102 | upgrade-all = [ 103 | "lock-all --upgrade" 104 | ] 105 | 106 | [tool.hatch.envs.lint] 107 | dependencies = [ 108 | "mypy>=1.6.1", 109 | "ruff~=0.1.4" 110 | ] 111 | detached = true 112 | type = "pip-compile" 113 | 114 | [tool.hatch.envs.lint.scripts] 115 | all = [ 116 | "style", 117 | "typing" 118 | ] 119 | fmt = [ 120 | "ruff format {args:.}", 121 | "ruff --fix {args:.}", 122 | "style" 123 | ] 124 | precommit = [ 125 | "pre-commit run --all-files" 126 | ] 127 | style = [ 128 | "ruff {args:.}", 129 | "ruff format --check --diff {args:.}" 130 | ] 131 | typing = "mypy --install-types --non-interactive {args:hatch_pip_compile}" 132 | 133 | [tool.hatch.envs.matrix] 134 | template = "test" 135 | 136 | [[tool.hatch.envs.matrix.matrix]] 137 | python = ["3.8", "3.9", "3.10", "3.11", "3.12"] 138 | 139 | [tool.hatch.envs.test] 140 | dependencies = [ 141 | "pytest", 142 | "pytest-cov", 143 | "tomlkit", 144 | "pytest-xdist" 145 | ] 146 | 147 | [tool.hatch.envs.test.scripts] 148 | cov = [ 149 | "pytest --cov --cov-config=pyproject.toml --cov-report=xml --cov-report=term-missing {args: -n auto tests/ -vv}" 150 | ] 151 | test = [ 152 | "pytest {args: -n auto tests/ -vv}" 153 | ] 154 | 155 | [tool.hatch.envs.versions] 156 | dependencies = [] 157 | pip-compile-constraint = "" 158 | template = "test" 159 | type = "pip-compile" 160 | 161 | [[tool.hatch.envs.versions.matrix]] 162 | version = [ 163 | "1.7.x", 164 | "1.8.x", 165 | "1.9.x", 166 | "1.10.x", 167 | "1.11.x", 168 | "1.12.x" 169 | ] 170 | 171 | [tool.hatch.envs.versions.overrides] 172 | matrix.version.dependencies = [ 173 | "pytest", 174 | "pytest-cov", 175 | "tomlkit", 176 | "pytest-xdist", 177 | {value = "hatch~=1.7.0", if = ["1.7.x"]}, 178 | {value = "hatch~=1.8.1", if = ["1.8.x"]}, 179 | {value = "hatch~=1.9.7", if = ["1.9.x"]}, 180 | {value = "hatch~=1.10.0", if = ["1.10.x"]}, 181 | {value = "hatch~=1.11.1", if = ["1.11.x"]}, 182 | {value = "hatch~=1.12.0", if = ["1.12.x"]} 183 | ] 184 | 185 | [tool.hatch.version] 186 | path = "hatch_pip_compile/__about__.py" 187 | 188 | [tool.mypy] 189 | ignore_missing_imports = true 190 | 191 | [tool.ruff] 192 | ignore = [ 193 | # Allow non-abstract empty methods in abstract base classes 194 | "B027", 195 | # Boolean-typed positional argument in function definition 196 | "FBT001", 197 | # Boolean default positional argument in function definition 198 | "FBT002", 199 | # Allow boolean positional values in function calls, like `dict.get(... True)` 200 | "FBT003", 201 | # Ignore checks for possible passwords 202 | "S105", 203 | "S106", 204 | "S107", 205 | # Ignore complexity 206 | "C901", 207 | "PLR0911", 208 | "PLR0912", 209 | "PLR0913", 210 | "PLR0915", 211 | # Prefer `next(iter(mock_check_command.call_args))` 212 | "RUF015" 213 | ] 214 | line-length = 100 215 | select = [ 216 | "A", # flake8-builtins 217 | "ARG", # flake8-unused-arguments 218 | "B", # flake8-bugbear 219 | "C", # mccabe 220 | "DTZ", # flake8-datetimez 221 | "E", # pycodestyle (Error) 222 | "EM", # flake8-errmsg 223 | "F", # Pyflakes 224 | "FBT", # flake8-boolean-trap 225 | "I", # isort 226 | "ICN", # flake8-import-conventions 227 | "N", # pep8-naming 228 | "PLC", # Pylint (Convention message) 229 | "PLE", # Pylint (Error message) 230 | "PLR", # Pylint (Refactor message) 231 | "PLW", # Pylint (Warning message) 232 | "Q", # flake8-quotes 233 | "RUF", # Ruff-specific rules 234 | "S", # flake8-bandit 235 | "T", # flake8-debugger (T10) and flake8-print (T20) 236 | "TID", # flake8-tidy-imports 237 | "UP", # pyupgrade 238 | "W", # pycodestyle (Warning) 239 | "YTT" # flake8-2020 240 | ] 241 | target-version = "py38" 242 | 243 | [tool.ruff.flake8-tidy-imports] 244 | ban-relative-imports = "all" 245 | 246 | [tool.ruff.isort] 247 | known-first-party = ["hatch_pip_compile"] 248 | 249 | [tool.ruff.per-file-ignores] 250 | # Tests can use magic values, assertions, relative imports, and unused arguments 251 | "tests/**/*" = ["PLR2004", "S101", "TID252", "ARG001"] 252 | 253 | [tool.ruff.pydocstyle] 254 | convention = "numpy" 255 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - click 5 | # - hatch<2,>=1.7.0 6 | # - pip-tools>=6 7 | # - rich 8 | # 9 | 10 | anyio==4.4.0 11 | # via httpx 12 | backports-tarfile==1.2.0 13 | # via jaraco-context 14 | build==1.2.1 15 | # via pip-tools 16 | certifi==2024.7.4 17 | # via 18 | # httpcore 19 | # httpx 20 | click==8.1.7 21 | # via 22 | # hatch.envs.default 23 | # hatch 24 | # pip-tools 25 | # userpath 26 | distlib==0.3.8 27 | # via virtualenv 28 | filelock==3.15.4 29 | # via virtualenv 30 | h11==0.14.0 31 | # via httpcore 32 | hatch==1.12.0 33 | # via hatch.envs.default 34 | hatchling==1.25.0 35 | # via hatch 36 | httpcore==1.0.5 37 | # via httpx 38 | httpx==0.27.0 39 | # via hatch 40 | hyperlink==21.0.0 41 | # via hatch 42 | idna==3.7 43 | # via 44 | # anyio 45 | # httpx 46 | # hyperlink 47 | importlib-metadata==8.0.0 48 | # via keyring 49 | jaraco-classes==3.4.0 50 | # via keyring 51 | jaraco-context==5.3.0 52 | # via keyring 53 | jaraco-functools==4.0.1 54 | # via keyring 55 | keyring==25.2.1 56 | # via hatch 57 | markdown-it-py==3.0.0 58 | # via rich 59 | mdurl==0.1.2 60 | # via markdown-it-py 61 | more-itertools==10.3.0 62 | # via 63 | # jaraco-classes 64 | # jaraco-functools 65 | packaging==24.1 66 | # via 67 | # build 68 | # hatch 69 | # hatchling 70 | pathspec==0.12.1 71 | # via hatchling 72 | pexpect==4.9.0 73 | # via hatch 74 | pip-tools==7.4.1 75 | # via hatch.envs.default 76 | platformdirs==4.2.2 77 | # via 78 | # hatch 79 | # virtualenv 80 | pluggy==1.5.0 81 | # via hatchling 82 | ptyprocess==0.7.0 83 | # via pexpect 84 | pygments==2.18.0 85 | # via rich 86 | pyproject-hooks==1.1.0 87 | # via 88 | # build 89 | # pip-tools 90 | rich==13.7.1 91 | # via 92 | # hatch.envs.default 93 | # hatch 94 | shellingham==1.5.4 95 | # via hatch 96 | sniffio==1.3.1 97 | # via 98 | # anyio 99 | # httpx 100 | tomli-w==1.0.0 101 | # via hatch 102 | tomlkit==0.13.0 103 | # via hatch 104 | trove-classifiers==2024.7.2 105 | # via hatchling 106 | userpath==1.9.2 107 | # via hatch 108 | uv==0.2.24 109 | # via hatch 110 | virtualenv==20.26.3 111 | # via hatch 112 | wheel==0.43.0 113 | # via pip-tools 114 | zipp==3.19.2 115 | # via importlib-metadata 116 | zstandard==0.22.0 117 | # via hatch 118 | 119 | # The following packages are considered to be unsafe in a requirements file: 120 | # pip 121 | # setuptools 122 | -------------------------------------------------------------------------------- /requirements/requirements-docs.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - markdown-callouts 7 | # - markdown-exec 8 | # - mkdocs 9 | # - mkdocs-click 10 | # - mkdocs-gen-files 11 | # - mkdocs-literate-nav 12 | # - mkdocs-material 13 | # - mkdocs-section-index 14 | # - mkdocstrings 15 | # - mkdocstrings-python 16 | # - pymdown-extensions 17 | # - click 18 | # - hatch<2,>=1.7.0 19 | # - pip-tools>=6 20 | # - rich 21 | # 22 | 23 | anyio==4.4.0 24 | # via 25 | # -c requirements.txt 26 | # httpx 27 | babel==2.15.0 28 | # via mkdocs-material 29 | backports-tarfile==1.2.0 30 | # via 31 | # -c requirements.txt 32 | # jaraco-context 33 | build==1.2.1 34 | # via 35 | # -c requirements.txt 36 | # pip-tools 37 | certifi==2024.7.4 38 | # via 39 | # -c requirements.txt 40 | # httpcore 41 | # httpx 42 | # requests 43 | charset-normalizer==3.3.2 44 | # via requests 45 | click==8.1.7 46 | # via 47 | # -c requirements.txt 48 | # hatch.envs.docs 49 | # hatch 50 | # mkdocs 51 | # mkdocs-click 52 | # mkdocstrings 53 | # pip-tools 54 | # userpath 55 | colorama==0.4.6 56 | # via 57 | # griffe 58 | # mkdocs-material 59 | distlib==0.3.8 60 | # via 61 | # -c requirements.txt 62 | # virtualenv 63 | filelock==3.15.4 64 | # via 65 | # -c requirements.txt 66 | # virtualenv 67 | ghp-import==2.1.0 68 | # via mkdocs 69 | griffe==0.47.0 70 | # via mkdocstrings-python 71 | h11==0.14.0 72 | # via 73 | # -c requirements.txt 74 | # httpcore 75 | hatch==1.12.0 76 | # via 77 | # -c requirements.txt 78 | # hatch.envs.docs 79 | hatchling==1.25.0 80 | # via 81 | # -c requirements.txt 82 | # hatch 83 | httpcore==1.0.5 84 | # via 85 | # -c requirements.txt 86 | # httpx 87 | httpx==0.27.0 88 | # via 89 | # -c requirements.txt 90 | # hatch 91 | hyperlink==21.0.0 92 | # via 93 | # -c requirements.txt 94 | # hatch 95 | idna==3.7 96 | # via 97 | # -c requirements.txt 98 | # anyio 99 | # httpx 100 | # hyperlink 101 | # requests 102 | importlib-metadata==8.0.0 103 | # via 104 | # -c requirements.txt 105 | # keyring 106 | jaraco-classes==3.4.0 107 | # via 108 | # -c requirements.txt 109 | # keyring 110 | jaraco-context==5.3.0 111 | # via 112 | # -c requirements.txt 113 | # keyring 114 | jaraco-functools==4.0.1 115 | # via 116 | # -c requirements.txt 117 | # keyring 118 | jinja2==3.1.4 119 | # via 120 | # mkdocs 121 | # mkdocs-material 122 | # mkdocstrings 123 | keyring==25.2.1 124 | # via 125 | # -c requirements.txt 126 | # hatch 127 | markdown==3.6 128 | # via 129 | # markdown-callouts 130 | # mkdocs 131 | # mkdocs-autorefs 132 | # mkdocs-click 133 | # mkdocs-material 134 | # mkdocstrings 135 | # pymdown-extensions 136 | markdown-callouts==0.4.0 137 | # via hatch.envs.docs 138 | markdown-exec==1.9.3 139 | # via hatch.envs.docs 140 | markdown-it-py==3.0.0 141 | # via 142 | # -c requirements.txt 143 | # rich 144 | markupsafe==2.1.5 145 | # via 146 | # jinja2 147 | # mkdocs 148 | # mkdocs-autorefs 149 | # mkdocstrings 150 | mdurl==0.1.2 151 | # via 152 | # -c requirements.txt 153 | # markdown-it-py 154 | mergedeep==1.3.4 155 | # via 156 | # mkdocs 157 | # mkdocs-get-deps 158 | mkdocs==1.6.0 159 | # via 160 | # hatch.envs.docs 161 | # mkdocs-autorefs 162 | # mkdocs-gen-files 163 | # mkdocs-literate-nav 164 | # mkdocs-material 165 | # mkdocs-section-index 166 | # mkdocstrings 167 | mkdocs-autorefs==1.0.1 168 | # via mkdocstrings 169 | mkdocs-click==0.8.1 170 | # via hatch.envs.docs 171 | mkdocs-gen-files==0.5.0 172 | # via hatch.envs.docs 173 | mkdocs-get-deps==0.2.0 174 | # via mkdocs 175 | mkdocs-literate-nav==0.6.1 176 | # via hatch.envs.docs 177 | mkdocs-material==9.5.28 178 | # via hatch.envs.docs 179 | mkdocs-material-extensions==1.3.1 180 | # via mkdocs-material 181 | mkdocs-section-index==0.3.9 182 | # via hatch.envs.docs 183 | mkdocstrings==0.25.1 184 | # via 185 | # hatch.envs.docs 186 | # mkdocstrings-python 187 | mkdocstrings-python==1.10.5 188 | # via hatch.envs.docs 189 | more-itertools==10.3.0 190 | # via 191 | # -c requirements.txt 192 | # jaraco-classes 193 | # jaraco-functools 194 | packaging==24.1 195 | # via 196 | # -c requirements.txt 197 | # build 198 | # hatch 199 | # hatchling 200 | # mkdocs 201 | paginate==0.5.6 202 | # via mkdocs-material 203 | pathspec==0.12.1 204 | # via 205 | # -c requirements.txt 206 | # hatchling 207 | # mkdocs 208 | pexpect==4.9.0 209 | # via 210 | # -c requirements.txt 211 | # hatch 212 | pip-tools==7.4.1 213 | # via 214 | # -c requirements.txt 215 | # hatch.envs.docs 216 | platformdirs==4.2.2 217 | # via 218 | # -c requirements.txt 219 | # hatch 220 | # mkdocs-get-deps 221 | # mkdocstrings 222 | # virtualenv 223 | pluggy==1.5.0 224 | # via 225 | # -c requirements.txt 226 | # hatchling 227 | ptyprocess==0.7.0 228 | # via 229 | # -c requirements.txt 230 | # pexpect 231 | pygments==2.18.0 232 | # via 233 | # -c requirements.txt 234 | # mkdocs-material 235 | # rich 236 | pymdown-extensions==10.8.1 237 | # via 238 | # hatch.envs.docs 239 | # markdown-exec 240 | # mkdocs-material 241 | # mkdocstrings 242 | pyproject-hooks==1.1.0 243 | # via 244 | # -c requirements.txt 245 | # build 246 | # pip-tools 247 | python-dateutil==2.9.0.post0 248 | # via ghp-import 249 | pyyaml==6.0.1 250 | # via 251 | # mkdocs 252 | # mkdocs-get-deps 253 | # pymdown-extensions 254 | # pyyaml-env-tag 255 | pyyaml-env-tag==0.1 256 | # via mkdocs 257 | regex==2024.5.15 258 | # via mkdocs-material 259 | requests==2.32.3 260 | # via mkdocs-material 261 | rich==13.7.1 262 | # via 263 | # -c requirements.txt 264 | # hatch.envs.docs 265 | # hatch 266 | shellingham==1.5.4 267 | # via 268 | # -c requirements.txt 269 | # hatch 270 | six==1.16.0 271 | # via python-dateutil 272 | sniffio==1.3.1 273 | # via 274 | # -c requirements.txt 275 | # anyio 276 | # httpx 277 | tomli-w==1.0.0 278 | # via 279 | # -c requirements.txt 280 | # hatch 281 | tomlkit==0.13.0 282 | # via 283 | # -c requirements.txt 284 | # hatch 285 | trove-classifiers==2024.7.2 286 | # via 287 | # -c requirements.txt 288 | # hatchling 289 | urllib3==2.2.2 290 | # via requests 291 | userpath==1.9.2 292 | # via 293 | # -c requirements.txt 294 | # hatch 295 | uv==0.2.24 296 | # via 297 | # -c requirements.txt 298 | # hatch 299 | virtualenv==20.26.3 300 | # via 301 | # -c requirements.txt 302 | # hatch 303 | watchdog==4.0.1 304 | # via mkdocs 305 | wheel==0.43.0 306 | # via 307 | # -c requirements.txt 308 | # pip-tools 309 | zipp==3.19.2 310 | # via 311 | # -c requirements.txt 312 | # importlib-metadata 313 | zstandard==0.22.0 314 | # via 315 | # -c requirements.txt 316 | # hatch 317 | 318 | # The following packages are considered to be unsafe in a requirements file: 319 | # pip 320 | # setuptools 321 | -------------------------------------------------------------------------------- /requirements/requirements-lint.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - mypy>=1.6.1 5 | # - ruff~=0.1.4 6 | # 7 | 8 | mypy==1.10.1 9 | # via hatch.envs.lint 10 | mypy-extensions==1.0.0 11 | # via mypy 12 | ruff==0.1.15 13 | # via hatch.envs.lint 14 | typing-extensions==4.12.2 15 | # via mypy 16 | -------------------------------------------------------------------------------- /requirements/requirements-matrix.py3.10.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.10 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - tomlkit 9 | # - pytest-xdist 10 | # - click 11 | # - hatch<2,>=1.7.0 12 | # - pip-tools>=6 13 | # - rich 14 | # 15 | 16 | anyio==4.4.0 17 | # via 18 | # -c requirements.txt 19 | # httpx 20 | backports-tarfile==1.2.0 21 | # via 22 | # -c requirements.txt 23 | # jaraco-context 24 | build==1.2.1 25 | # via 26 | # -c requirements.txt 27 | # pip-tools 28 | certifi==2024.7.4 29 | # via 30 | # -c requirements.txt 31 | # httpcore 32 | # httpx 33 | click==8.1.7 34 | # via 35 | # -c requirements.txt 36 | # hatch.envs.matrix.py3.10 37 | # hatch 38 | # pip-tools 39 | # userpath 40 | coverage==7.6.0 41 | # via pytest-cov 42 | distlib==0.3.8 43 | # via 44 | # -c requirements.txt 45 | # virtualenv 46 | exceptiongroup==1.2.2 47 | # via 48 | # anyio 49 | # pytest 50 | execnet==2.1.1 51 | # via pytest-xdist 52 | filelock==3.15.4 53 | # via 54 | # -c requirements.txt 55 | # virtualenv 56 | h11==0.14.0 57 | # via 58 | # -c requirements.txt 59 | # httpcore 60 | hatch==1.12.0 61 | # via 62 | # -c requirements.txt 63 | # hatch.envs.matrix.py3.10 64 | hatchling==1.25.0 65 | # via 66 | # -c requirements.txt 67 | # hatch 68 | httpcore==1.0.5 69 | # via 70 | # -c requirements.txt 71 | # httpx 72 | httpx==0.27.0 73 | # via 74 | # -c requirements.txt 75 | # hatch 76 | hyperlink==21.0.0 77 | # via 78 | # -c requirements.txt 79 | # hatch 80 | idna==3.7 81 | # via 82 | # -c requirements.txt 83 | # anyio 84 | # httpx 85 | # hyperlink 86 | importlib-metadata==8.0.0 87 | # via 88 | # -c requirements.txt 89 | # keyring 90 | iniconfig==2.0.0 91 | # via pytest 92 | jaraco-classes==3.4.0 93 | # via 94 | # -c requirements.txt 95 | # keyring 96 | jaraco-context==5.3.0 97 | # via 98 | # -c requirements.txt 99 | # keyring 100 | jaraco-functools==4.0.1 101 | # via 102 | # -c requirements.txt 103 | # keyring 104 | keyring==25.2.1 105 | # via 106 | # -c requirements.txt 107 | # hatch 108 | markdown-it-py==3.0.0 109 | # via 110 | # -c requirements.txt 111 | # rich 112 | mdurl==0.1.2 113 | # via 114 | # -c requirements.txt 115 | # markdown-it-py 116 | more-itertools==10.3.0 117 | # via 118 | # -c requirements.txt 119 | # jaraco-classes 120 | # jaraco-functools 121 | packaging==24.1 122 | # via 123 | # -c requirements.txt 124 | # build 125 | # hatch 126 | # hatchling 127 | # pytest 128 | pathspec==0.12.1 129 | # via 130 | # -c requirements.txt 131 | # hatchling 132 | pexpect==4.9.0 133 | # via 134 | # -c requirements.txt 135 | # hatch 136 | pip-tools==7.4.1 137 | # via 138 | # -c requirements.txt 139 | # hatch.envs.matrix.py3.10 140 | platformdirs==4.2.2 141 | # via 142 | # -c requirements.txt 143 | # hatch 144 | # virtualenv 145 | pluggy==1.5.0 146 | # via 147 | # -c requirements.txt 148 | # hatchling 149 | # pytest 150 | ptyprocess==0.7.0 151 | # via 152 | # -c requirements.txt 153 | # pexpect 154 | pygments==2.18.0 155 | # via 156 | # -c requirements.txt 157 | # rich 158 | pyproject-hooks==1.1.0 159 | # via 160 | # -c requirements.txt 161 | # build 162 | # pip-tools 163 | pytest==8.2.2 164 | # via 165 | # hatch.envs.matrix.py3.10 166 | # pytest-cov 167 | # pytest-xdist 168 | pytest-cov==5.0.0 169 | # via hatch.envs.matrix.py3.10 170 | pytest-xdist==3.6.1 171 | # via hatch.envs.matrix.py3.10 172 | rich==13.7.1 173 | # via 174 | # -c requirements.txt 175 | # hatch.envs.matrix.py3.10 176 | # hatch 177 | shellingham==1.5.4 178 | # via 179 | # -c requirements.txt 180 | # hatch 181 | sniffio==1.3.1 182 | # via 183 | # -c requirements.txt 184 | # anyio 185 | # httpx 186 | tomli==2.0.1 187 | # via 188 | # build 189 | # coverage 190 | # hatchling 191 | # pip-tools 192 | # pytest 193 | tomli-w==1.0.0 194 | # via 195 | # -c requirements.txt 196 | # hatch 197 | tomlkit==0.13.0 198 | # via 199 | # -c requirements.txt 200 | # hatch.envs.matrix.py3.10 201 | # hatch 202 | trove-classifiers==2024.7.2 203 | # via 204 | # -c requirements.txt 205 | # hatchling 206 | typing-extensions==4.12.2 207 | # via anyio 208 | userpath==1.9.2 209 | # via 210 | # -c requirements.txt 211 | # hatch 212 | uv==0.2.24 213 | # via 214 | # -c requirements.txt 215 | # hatch 216 | virtualenv==20.26.3 217 | # via 218 | # -c requirements.txt 219 | # hatch 220 | wheel==0.43.0 221 | # via 222 | # -c requirements.txt 223 | # pip-tools 224 | zipp==3.19.2 225 | # via 226 | # -c requirements.txt 227 | # importlib-metadata 228 | zstandard==0.22.0 229 | # via 230 | # -c requirements.txt 231 | # hatch 232 | 233 | # The following packages are considered to be unsafe in a requirements file: 234 | # pip 235 | # setuptools 236 | -------------------------------------------------------------------------------- /requirements/requirements-matrix.py3.11.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - tomlkit 9 | # - pytest-xdist 10 | # - click 11 | # - hatch<2,>=1.7.0 12 | # - pip-tools>=6 13 | # - rich 14 | # 15 | 16 | anyio==4.4.0 17 | # via 18 | # -c requirements.txt 19 | # httpx 20 | backports-tarfile==1.2.0 21 | # via 22 | # -c requirements.txt 23 | # jaraco-context 24 | build==1.2.1 25 | # via 26 | # -c requirements.txt 27 | # pip-tools 28 | certifi==2024.7.4 29 | # via 30 | # -c requirements.txt 31 | # httpcore 32 | # httpx 33 | click==8.1.7 34 | # via 35 | # -c requirements.txt 36 | # hatch.envs.matrix.py3.11 37 | # hatch 38 | # pip-tools 39 | # userpath 40 | coverage==7.6.0 41 | # via pytest-cov 42 | distlib==0.3.8 43 | # via 44 | # -c requirements.txt 45 | # virtualenv 46 | execnet==2.1.1 47 | # via pytest-xdist 48 | filelock==3.15.4 49 | # via 50 | # -c requirements.txt 51 | # virtualenv 52 | h11==0.14.0 53 | # via 54 | # -c requirements.txt 55 | # httpcore 56 | hatch==1.12.0 57 | # via 58 | # -c requirements.txt 59 | # hatch.envs.matrix.py3.11 60 | hatchling==1.25.0 61 | # via 62 | # -c requirements.txt 63 | # hatch 64 | httpcore==1.0.5 65 | # via 66 | # -c requirements.txt 67 | # httpx 68 | httpx==0.27.0 69 | # via 70 | # -c requirements.txt 71 | # hatch 72 | hyperlink==21.0.0 73 | # via 74 | # -c requirements.txt 75 | # hatch 76 | idna==3.7 77 | # via 78 | # -c requirements.txt 79 | # anyio 80 | # httpx 81 | # hyperlink 82 | importlib-metadata==8.0.0 83 | # via 84 | # -c requirements.txt 85 | # keyring 86 | iniconfig==2.0.0 87 | # via pytest 88 | jaraco-classes==3.4.0 89 | # via 90 | # -c requirements.txt 91 | # keyring 92 | jaraco-context==5.3.0 93 | # via 94 | # -c requirements.txt 95 | # keyring 96 | jaraco-functools==4.0.1 97 | # via 98 | # -c requirements.txt 99 | # keyring 100 | keyring==25.2.1 101 | # via 102 | # -c requirements.txt 103 | # hatch 104 | markdown-it-py==3.0.0 105 | # via 106 | # -c requirements.txt 107 | # rich 108 | mdurl==0.1.2 109 | # via 110 | # -c requirements.txt 111 | # markdown-it-py 112 | more-itertools==10.3.0 113 | # via 114 | # -c requirements.txt 115 | # jaraco-classes 116 | # jaraco-functools 117 | packaging==24.1 118 | # via 119 | # -c requirements.txt 120 | # build 121 | # hatch 122 | # hatchling 123 | # pytest 124 | pathspec==0.12.1 125 | # via 126 | # -c requirements.txt 127 | # hatchling 128 | pexpect==4.9.0 129 | # via 130 | # -c requirements.txt 131 | # hatch 132 | pip-tools==7.4.1 133 | # via 134 | # -c requirements.txt 135 | # hatch.envs.matrix.py3.11 136 | platformdirs==4.2.2 137 | # via 138 | # -c requirements.txt 139 | # hatch 140 | # virtualenv 141 | pluggy==1.5.0 142 | # via 143 | # -c requirements.txt 144 | # hatchling 145 | # pytest 146 | ptyprocess==0.7.0 147 | # via 148 | # -c requirements.txt 149 | # pexpect 150 | pygments==2.18.0 151 | # via 152 | # -c requirements.txt 153 | # rich 154 | pyproject-hooks==1.1.0 155 | # via 156 | # -c requirements.txt 157 | # build 158 | # pip-tools 159 | pytest==8.2.2 160 | # via 161 | # hatch.envs.matrix.py3.11 162 | # pytest-cov 163 | # pytest-xdist 164 | pytest-cov==5.0.0 165 | # via hatch.envs.matrix.py3.11 166 | pytest-xdist==3.6.1 167 | # via hatch.envs.matrix.py3.11 168 | rich==13.7.1 169 | # via 170 | # -c requirements.txt 171 | # hatch.envs.matrix.py3.11 172 | # hatch 173 | shellingham==1.5.4 174 | # via 175 | # -c requirements.txt 176 | # hatch 177 | sniffio==1.3.1 178 | # via 179 | # -c requirements.txt 180 | # anyio 181 | # httpx 182 | tomli-w==1.0.0 183 | # via 184 | # -c requirements.txt 185 | # hatch 186 | tomlkit==0.13.0 187 | # via 188 | # -c requirements.txt 189 | # hatch.envs.matrix.py3.11 190 | # hatch 191 | trove-classifiers==2024.7.2 192 | # via 193 | # -c requirements.txt 194 | # hatchling 195 | userpath==1.9.2 196 | # via 197 | # -c requirements.txt 198 | # hatch 199 | uv==0.2.24 200 | # via 201 | # -c requirements.txt 202 | # hatch 203 | virtualenv==20.26.3 204 | # via 205 | # -c requirements.txt 206 | # hatch 207 | wheel==0.43.0 208 | # via 209 | # -c requirements.txt 210 | # pip-tools 211 | zipp==3.19.2 212 | # via 213 | # -c requirements.txt 214 | # importlib-metadata 215 | zstandard==0.22.0 216 | # via 217 | # -c requirements.txt 218 | # hatch 219 | 220 | # The following packages are considered to be unsafe in a requirements file: 221 | # pip 222 | # setuptools 223 | -------------------------------------------------------------------------------- /requirements/requirements-matrix.py3.12.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.12 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - tomlkit 9 | # - pytest-xdist 10 | # - click 11 | # - hatch<2,>=1.7.0 12 | # - pip-tools>=6 13 | # - rich 14 | # 15 | 16 | anyio==4.4.0 17 | # via 18 | # -c requirements.txt 19 | # httpx 20 | build==1.2.1 21 | # via 22 | # -c requirements.txt 23 | # pip-tools 24 | certifi==2024.7.4 25 | # via 26 | # -c requirements.txt 27 | # httpcore 28 | # httpx 29 | click==8.1.7 30 | # via 31 | # -c requirements.txt 32 | # hatch.envs.matrix.py3.12 33 | # hatch 34 | # pip-tools 35 | # userpath 36 | coverage==7.6.0 37 | # via pytest-cov 38 | distlib==0.3.8 39 | # via 40 | # -c requirements.txt 41 | # virtualenv 42 | execnet==2.1.1 43 | # via pytest-xdist 44 | filelock==3.15.4 45 | # via 46 | # -c requirements.txt 47 | # virtualenv 48 | h11==0.14.0 49 | # via 50 | # -c requirements.txt 51 | # httpcore 52 | hatch==1.12.0 53 | # via 54 | # -c requirements.txt 55 | # hatch.envs.matrix.py3.12 56 | hatchling==1.25.0 57 | # via 58 | # -c requirements.txt 59 | # hatch 60 | httpcore==1.0.5 61 | # via 62 | # -c requirements.txt 63 | # httpx 64 | httpx==0.27.0 65 | # via 66 | # -c requirements.txt 67 | # hatch 68 | hyperlink==21.0.0 69 | # via 70 | # -c requirements.txt 71 | # hatch 72 | idna==3.7 73 | # via 74 | # -c requirements.txt 75 | # anyio 76 | # httpx 77 | # hyperlink 78 | iniconfig==2.0.0 79 | # via pytest 80 | jaraco-classes==3.4.0 81 | # via 82 | # -c requirements.txt 83 | # keyring 84 | jaraco-context==5.3.0 85 | # via 86 | # -c requirements.txt 87 | # keyring 88 | jaraco-functools==4.0.1 89 | # via 90 | # -c requirements.txt 91 | # keyring 92 | keyring==25.2.1 93 | # via 94 | # -c requirements.txt 95 | # hatch 96 | markdown-it-py==3.0.0 97 | # via 98 | # -c requirements.txt 99 | # rich 100 | mdurl==0.1.2 101 | # via 102 | # -c requirements.txt 103 | # markdown-it-py 104 | more-itertools==10.3.0 105 | # via 106 | # -c requirements.txt 107 | # jaraco-classes 108 | # jaraco-functools 109 | packaging==24.1 110 | # via 111 | # -c requirements.txt 112 | # build 113 | # hatch 114 | # hatchling 115 | # pytest 116 | pathspec==0.12.1 117 | # via 118 | # -c requirements.txt 119 | # hatchling 120 | pexpect==4.9.0 121 | # via 122 | # -c requirements.txt 123 | # hatch 124 | pip-tools==7.4.1 125 | # via 126 | # -c requirements.txt 127 | # hatch.envs.matrix.py3.12 128 | platformdirs==4.2.2 129 | # via 130 | # -c requirements.txt 131 | # hatch 132 | # virtualenv 133 | pluggy==1.5.0 134 | # via 135 | # -c requirements.txt 136 | # hatchling 137 | # pytest 138 | ptyprocess==0.7.0 139 | # via 140 | # -c requirements.txt 141 | # pexpect 142 | pygments==2.18.0 143 | # via 144 | # -c requirements.txt 145 | # rich 146 | pyproject-hooks==1.1.0 147 | # via 148 | # -c requirements.txt 149 | # build 150 | # pip-tools 151 | pytest==8.2.2 152 | # via 153 | # hatch.envs.matrix.py3.12 154 | # pytest-cov 155 | # pytest-xdist 156 | pytest-cov==5.0.0 157 | # via hatch.envs.matrix.py3.12 158 | pytest-xdist==3.6.1 159 | # via hatch.envs.matrix.py3.12 160 | rich==13.7.1 161 | # via 162 | # -c requirements.txt 163 | # hatch.envs.matrix.py3.12 164 | # hatch 165 | shellingham==1.5.4 166 | # via 167 | # -c requirements.txt 168 | # hatch 169 | sniffio==1.3.1 170 | # via 171 | # -c requirements.txt 172 | # anyio 173 | # httpx 174 | tomli-w==1.0.0 175 | # via 176 | # -c requirements.txt 177 | # hatch 178 | tomlkit==0.13.0 179 | # via 180 | # -c requirements.txt 181 | # hatch.envs.matrix.py3.12 182 | # hatch 183 | trove-classifiers==2024.7.2 184 | # via 185 | # -c requirements.txt 186 | # hatchling 187 | userpath==1.9.2 188 | # via 189 | # -c requirements.txt 190 | # hatch 191 | uv==0.2.24 192 | # via 193 | # -c requirements.txt 194 | # hatch 195 | virtualenv==20.26.3 196 | # via 197 | # -c requirements.txt 198 | # hatch 199 | wheel==0.43.0 200 | # via 201 | # -c requirements.txt 202 | # pip-tools 203 | zstandard==0.22.0 204 | # via 205 | # -c requirements.txt 206 | # hatch 207 | 208 | # The following packages are considered to be unsafe in a requirements file: 209 | # pip 210 | # setuptools 211 | -------------------------------------------------------------------------------- /requirements/requirements-matrix.py3.8.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.8 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - tomlkit 9 | # - pytest-xdist 10 | # - click 11 | # - hatch<2,>=1.7.0 12 | # - pip-tools>=6 13 | # - rich 14 | # 15 | 16 | anyio==4.4.0 17 | # via 18 | # -c requirements.txt 19 | # httpx 20 | backports-tarfile==1.2.0 21 | # via 22 | # -c requirements.txt 23 | # jaraco-context 24 | build==1.2.1 25 | # via 26 | # -c requirements.txt 27 | # pip-tools 28 | certifi==2024.7.4 29 | # via 30 | # -c requirements.txt 31 | # httpcore 32 | # httpx 33 | click==8.1.7 34 | # via 35 | # -c requirements.txt 36 | # hatch.envs.matrix.py3.8 37 | # hatch 38 | # pip-tools 39 | # userpath 40 | coverage==7.6.0 41 | # via pytest-cov 42 | distlib==0.3.8 43 | # via 44 | # -c requirements.txt 45 | # virtualenv 46 | exceptiongroup==1.2.2 47 | # via 48 | # anyio 49 | # pytest 50 | execnet==2.1.1 51 | # via pytest-xdist 52 | filelock==3.15.4 53 | # via 54 | # -c requirements.txt 55 | # virtualenv 56 | h11==0.14.0 57 | # via 58 | # -c requirements.txt 59 | # httpcore 60 | hatch==1.12.0 61 | # via 62 | # -c requirements.txt 63 | # hatch.envs.matrix.py3.8 64 | hatchling==1.25.0 65 | # via 66 | # -c requirements.txt 67 | # hatch 68 | httpcore==1.0.5 69 | # via 70 | # -c requirements.txt 71 | # httpx 72 | httpx==0.27.0 73 | # via 74 | # -c requirements.txt 75 | # hatch 76 | hyperlink==21.0.0 77 | # via 78 | # -c requirements.txt 79 | # hatch 80 | idna==3.7 81 | # via 82 | # -c requirements.txt 83 | # anyio 84 | # httpx 85 | # hyperlink 86 | importlib-metadata==8.0.0 87 | # via 88 | # -c requirements.txt 89 | # build 90 | # keyring 91 | importlib-resources==6.4.0 92 | # via keyring 93 | iniconfig==2.0.0 94 | # via pytest 95 | jaraco-classes==3.4.0 96 | # via 97 | # -c requirements.txt 98 | # keyring 99 | jaraco-context==5.3.0 100 | # via 101 | # -c requirements.txt 102 | # keyring 103 | jaraco-functools==4.0.1 104 | # via 105 | # -c requirements.txt 106 | # keyring 107 | keyring==25.2.1 108 | # via 109 | # -c requirements.txt 110 | # hatch 111 | markdown-it-py==3.0.0 112 | # via 113 | # -c requirements.txt 114 | # rich 115 | mdurl==0.1.2 116 | # via 117 | # -c requirements.txt 118 | # markdown-it-py 119 | more-itertools==10.3.0 120 | # via 121 | # -c requirements.txt 122 | # jaraco-classes 123 | # jaraco-functools 124 | packaging==24.1 125 | # via 126 | # -c requirements.txt 127 | # build 128 | # hatch 129 | # hatchling 130 | # pytest 131 | pathspec==0.12.1 132 | # via 133 | # -c requirements.txt 134 | # hatchling 135 | pexpect==4.9.0 136 | # via 137 | # -c requirements.txt 138 | # hatch 139 | pip-tools==7.4.1 140 | # via 141 | # -c requirements.txt 142 | # hatch.envs.matrix.py3.8 143 | platformdirs==4.2.2 144 | # via 145 | # -c requirements.txt 146 | # hatch 147 | # virtualenv 148 | pluggy==1.5.0 149 | # via 150 | # -c requirements.txt 151 | # hatchling 152 | # pytest 153 | ptyprocess==0.7.0 154 | # via 155 | # -c requirements.txt 156 | # pexpect 157 | pygments==2.18.0 158 | # via 159 | # -c requirements.txt 160 | # rich 161 | pyproject-hooks==1.1.0 162 | # via 163 | # -c requirements.txt 164 | # build 165 | # pip-tools 166 | pytest==8.2.2 167 | # via 168 | # hatch.envs.matrix.py3.8 169 | # pytest-cov 170 | # pytest-xdist 171 | pytest-cov==5.0.0 172 | # via hatch.envs.matrix.py3.8 173 | pytest-xdist==3.6.1 174 | # via hatch.envs.matrix.py3.8 175 | rich==13.7.1 176 | # via 177 | # -c requirements.txt 178 | # hatch.envs.matrix.py3.8 179 | # hatch 180 | shellingham==1.5.4 181 | # via 182 | # -c requirements.txt 183 | # hatch 184 | sniffio==1.3.1 185 | # via 186 | # -c requirements.txt 187 | # anyio 188 | # httpx 189 | tomli==2.0.1 190 | # via 191 | # build 192 | # coverage 193 | # hatchling 194 | # pip-tools 195 | # pytest 196 | tomli-w==1.0.0 197 | # via 198 | # -c requirements.txt 199 | # hatch 200 | tomlkit==0.13.0 201 | # via 202 | # -c requirements.txt 203 | # hatch.envs.matrix.py3.8 204 | # hatch 205 | trove-classifiers==2024.7.2 206 | # via 207 | # -c requirements.txt 208 | # hatchling 209 | typing-extensions==4.12.2 210 | # via 211 | # anyio 212 | # rich 213 | userpath==1.9.2 214 | # via 215 | # -c requirements.txt 216 | # hatch 217 | uv==0.2.24 218 | # via 219 | # -c requirements.txt 220 | # hatch 221 | virtualenv==20.26.3 222 | # via 223 | # -c requirements.txt 224 | # hatch 225 | wheel==0.43.0 226 | # via 227 | # -c requirements.txt 228 | # pip-tools 229 | zipp==3.19.2 230 | # via 231 | # -c requirements.txt 232 | # importlib-metadata 233 | # importlib-resources 234 | zstandard==0.22.0 235 | # via 236 | # -c requirements.txt 237 | # hatch 238 | 239 | # The following packages are considered to be unsafe in a requirements file: 240 | # pip 241 | # setuptools 242 | -------------------------------------------------------------------------------- /requirements/requirements-matrix.py3.9.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.9 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - tomlkit 9 | # - pytest-xdist 10 | # - click 11 | # - hatch<2,>=1.7.0 12 | # - pip-tools>=6 13 | # - rich 14 | # 15 | 16 | anyio==4.4.0 17 | # via 18 | # -c requirements.txt 19 | # httpx 20 | backports-tarfile==1.2.0 21 | # via 22 | # -c requirements.txt 23 | # jaraco-context 24 | build==1.2.1 25 | # via 26 | # -c requirements.txt 27 | # pip-tools 28 | certifi==2024.7.4 29 | # via 30 | # -c requirements.txt 31 | # httpcore 32 | # httpx 33 | click==8.1.7 34 | # via 35 | # -c requirements.txt 36 | # hatch.envs.matrix.py3.9 37 | # hatch 38 | # pip-tools 39 | # userpath 40 | coverage==7.6.0 41 | # via pytest-cov 42 | distlib==0.3.8 43 | # via 44 | # -c requirements.txt 45 | # virtualenv 46 | exceptiongroup==1.2.2 47 | # via 48 | # anyio 49 | # pytest 50 | execnet==2.1.1 51 | # via pytest-xdist 52 | filelock==3.15.4 53 | # via 54 | # -c requirements.txt 55 | # virtualenv 56 | h11==0.14.0 57 | # via 58 | # -c requirements.txt 59 | # httpcore 60 | hatch==1.12.0 61 | # via 62 | # -c requirements.txt 63 | # hatch.envs.matrix.py3.9 64 | hatchling==1.25.0 65 | # via 66 | # -c requirements.txt 67 | # hatch 68 | httpcore==1.0.5 69 | # via 70 | # -c requirements.txt 71 | # httpx 72 | httpx==0.27.0 73 | # via 74 | # -c requirements.txt 75 | # hatch 76 | hyperlink==21.0.0 77 | # via 78 | # -c requirements.txt 79 | # hatch 80 | idna==3.7 81 | # via 82 | # -c requirements.txt 83 | # anyio 84 | # httpx 85 | # hyperlink 86 | importlib-metadata==8.0.0 87 | # via 88 | # -c requirements.txt 89 | # build 90 | # keyring 91 | iniconfig==2.0.0 92 | # via pytest 93 | jaraco-classes==3.4.0 94 | # via 95 | # -c requirements.txt 96 | # keyring 97 | jaraco-context==5.3.0 98 | # via 99 | # -c requirements.txt 100 | # keyring 101 | jaraco-functools==4.0.1 102 | # via 103 | # -c requirements.txt 104 | # keyring 105 | keyring==25.2.1 106 | # via 107 | # -c requirements.txt 108 | # hatch 109 | markdown-it-py==3.0.0 110 | # via 111 | # -c requirements.txt 112 | # rich 113 | mdurl==0.1.2 114 | # via 115 | # -c requirements.txt 116 | # markdown-it-py 117 | more-itertools==10.3.0 118 | # via 119 | # -c requirements.txt 120 | # jaraco-classes 121 | # jaraco-functools 122 | packaging==24.1 123 | # via 124 | # -c requirements.txt 125 | # build 126 | # hatch 127 | # hatchling 128 | # pytest 129 | pathspec==0.12.1 130 | # via 131 | # -c requirements.txt 132 | # hatchling 133 | pexpect==4.9.0 134 | # via 135 | # -c requirements.txt 136 | # hatch 137 | pip-tools==7.4.1 138 | # via 139 | # -c requirements.txt 140 | # hatch.envs.matrix.py3.9 141 | platformdirs==4.2.2 142 | # via 143 | # -c requirements.txt 144 | # hatch 145 | # virtualenv 146 | pluggy==1.5.0 147 | # via 148 | # -c requirements.txt 149 | # hatchling 150 | # pytest 151 | ptyprocess==0.7.0 152 | # via 153 | # -c requirements.txt 154 | # pexpect 155 | pygments==2.18.0 156 | # via 157 | # -c requirements.txt 158 | # rich 159 | pyproject-hooks==1.1.0 160 | # via 161 | # -c requirements.txt 162 | # build 163 | # pip-tools 164 | pytest==8.2.2 165 | # via 166 | # hatch.envs.matrix.py3.9 167 | # pytest-cov 168 | # pytest-xdist 169 | pytest-cov==5.0.0 170 | # via hatch.envs.matrix.py3.9 171 | pytest-xdist==3.6.1 172 | # via hatch.envs.matrix.py3.9 173 | rich==13.7.1 174 | # via 175 | # -c requirements.txt 176 | # hatch.envs.matrix.py3.9 177 | # hatch 178 | shellingham==1.5.4 179 | # via 180 | # -c requirements.txt 181 | # hatch 182 | sniffio==1.3.1 183 | # via 184 | # -c requirements.txt 185 | # anyio 186 | # httpx 187 | tomli==2.0.1 188 | # via 189 | # build 190 | # coverage 191 | # hatchling 192 | # pip-tools 193 | # pytest 194 | tomli-w==1.0.0 195 | # via 196 | # -c requirements.txt 197 | # hatch 198 | tomlkit==0.13.0 199 | # via 200 | # -c requirements.txt 201 | # hatch.envs.matrix.py3.9 202 | # hatch 203 | trove-classifiers==2024.7.2 204 | # via 205 | # -c requirements.txt 206 | # hatchling 207 | typing-extensions==4.12.2 208 | # via anyio 209 | userpath==1.9.2 210 | # via 211 | # -c requirements.txt 212 | # hatch 213 | uv==0.2.24 214 | # via 215 | # -c requirements.txt 216 | # hatch 217 | virtualenv==20.26.3 218 | # via 219 | # -c requirements.txt 220 | # hatch 221 | wheel==0.43.0 222 | # via 223 | # -c requirements.txt 224 | # pip-tools 225 | zipp==3.19.2 226 | # via 227 | # -c requirements.txt 228 | # importlib-metadata 229 | zstandard==0.22.0 230 | # via 231 | # -c requirements.txt 232 | # hatch 233 | 234 | # The following packages are considered to be unsafe in a requirements file: 235 | # pip 236 | # setuptools 237 | -------------------------------------------------------------------------------- /requirements/requirements-test.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # [constraints] requirements.txt (SHA256: f944fb6ce9cada73ee126b3fa6f6999e1fb2e71c2fc2f9bc2efb209448582a3f) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - tomlkit 9 | # - pytest-xdist 10 | # - click 11 | # - hatch<2,>=1.7.0 12 | # - pip-tools>=6 13 | # - rich 14 | # 15 | 16 | anyio==4.4.0 17 | # via 18 | # -c requirements.txt 19 | # httpx 20 | backports-tarfile==1.2.0 21 | # via 22 | # -c requirements.txt 23 | # jaraco-context 24 | build==1.2.1 25 | # via 26 | # -c requirements.txt 27 | # pip-tools 28 | certifi==2024.7.4 29 | # via 30 | # -c requirements.txt 31 | # httpcore 32 | # httpx 33 | click==8.1.7 34 | # via 35 | # -c requirements.txt 36 | # hatch.envs.test 37 | # hatch 38 | # pip-tools 39 | # userpath 40 | coverage==7.6.0 41 | # via pytest-cov 42 | distlib==0.3.8 43 | # via 44 | # -c requirements.txt 45 | # virtualenv 46 | execnet==2.1.1 47 | # via pytest-xdist 48 | filelock==3.15.4 49 | # via 50 | # -c requirements.txt 51 | # virtualenv 52 | h11==0.14.0 53 | # via 54 | # -c requirements.txt 55 | # httpcore 56 | hatch==1.12.0 57 | # via 58 | # -c requirements.txt 59 | # hatch.envs.test 60 | hatchling==1.25.0 61 | # via 62 | # -c requirements.txt 63 | # hatch 64 | httpcore==1.0.5 65 | # via 66 | # -c requirements.txt 67 | # httpx 68 | httpx==0.27.0 69 | # via 70 | # -c requirements.txt 71 | # hatch 72 | hyperlink==21.0.0 73 | # via 74 | # -c requirements.txt 75 | # hatch 76 | idna==3.7 77 | # via 78 | # -c requirements.txt 79 | # anyio 80 | # httpx 81 | # hyperlink 82 | importlib-metadata==8.0.0 83 | # via 84 | # -c requirements.txt 85 | # keyring 86 | iniconfig==2.0.0 87 | # via pytest 88 | jaraco-classes==3.4.0 89 | # via 90 | # -c requirements.txt 91 | # keyring 92 | jaraco-context==5.3.0 93 | # via 94 | # -c requirements.txt 95 | # keyring 96 | jaraco-functools==4.0.1 97 | # via 98 | # -c requirements.txt 99 | # keyring 100 | keyring==25.2.1 101 | # via 102 | # -c requirements.txt 103 | # hatch 104 | markdown-it-py==3.0.0 105 | # via 106 | # -c requirements.txt 107 | # rich 108 | mdurl==0.1.2 109 | # via 110 | # -c requirements.txt 111 | # markdown-it-py 112 | more-itertools==10.3.0 113 | # via 114 | # -c requirements.txt 115 | # jaraco-classes 116 | # jaraco-functools 117 | packaging==24.1 118 | # via 119 | # -c requirements.txt 120 | # build 121 | # hatch 122 | # hatchling 123 | # pytest 124 | pathspec==0.12.1 125 | # via 126 | # -c requirements.txt 127 | # hatchling 128 | pexpect==4.9.0 129 | # via 130 | # -c requirements.txt 131 | # hatch 132 | pip-tools==7.4.1 133 | # via 134 | # -c requirements.txt 135 | # hatch.envs.test 136 | platformdirs==4.2.2 137 | # via 138 | # -c requirements.txt 139 | # hatch 140 | # virtualenv 141 | pluggy==1.5.0 142 | # via 143 | # -c requirements.txt 144 | # hatchling 145 | # pytest 146 | ptyprocess==0.7.0 147 | # via 148 | # -c requirements.txt 149 | # pexpect 150 | pygments==2.18.0 151 | # via 152 | # -c requirements.txt 153 | # rich 154 | pyproject-hooks==1.1.0 155 | # via 156 | # -c requirements.txt 157 | # build 158 | # pip-tools 159 | pytest==8.2.2 160 | # via 161 | # hatch.envs.test 162 | # pytest-cov 163 | # pytest-xdist 164 | pytest-cov==5.0.0 165 | # via hatch.envs.test 166 | pytest-xdist==3.6.1 167 | # via hatch.envs.test 168 | rich==13.7.1 169 | # via 170 | # -c requirements.txt 171 | # hatch.envs.test 172 | # hatch 173 | shellingham==1.5.4 174 | # via 175 | # -c requirements.txt 176 | # hatch 177 | sniffio==1.3.1 178 | # via 179 | # -c requirements.txt 180 | # anyio 181 | # httpx 182 | tomli-w==1.0.0 183 | # via 184 | # -c requirements.txt 185 | # hatch 186 | tomlkit==0.13.0 187 | # via 188 | # -c requirements.txt 189 | # hatch.envs.test 190 | # hatch 191 | trove-classifiers==2024.7.2 192 | # via 193 | # -c requirements.txt 194 | # hatchling 195 | userpath==1.9.2 196 | # via 197 | # -c requirements.txt 198 | # hatch 199 | uv==0.2.24 200 | # via 201 | # -c requirements.txt 202 | # hatch 203 | virtualenv==20.26.3 204 | # via 205 | # -c requirements.txt 206 | # hatch 207 | wheel==0.43.0 208 | # via 209 | # -c requirements.txt 210 | # pip-tools 211 | zipp==3.19.2 212 | # via 213 | # -c requirements.txt 214 | # importlib-metadata 215 | zstandard==0.22.0 216 | # via 217 | # -c requirements.txt 218 | # hatch 219 | 220 | # The following packages are considered to be unsafe in a requirements file: 221 | # pip 222 | # setuptools 223 | -------------------------------------------------------------------------------- /requirements/requirements-versions.1.10.x.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - pytest 5 | # - pytest-cov 6 | # - tomlkit 7 | # - pytest-xdist 8 | # - hatch~=1.10.0 9 | # - click 10 | # - hatch<2,>=1.7.0 11 | # - pip-tools>=6 12 | # - rich 13 | # 14 | 15 | anyio==4.4.0 16 | # via httpx 17 | backports-tarfile==1.2.0 18 | # via jaraco-context 19 | build==1.2.1 20 | # via pip-tools 21 | certifi==2024.7.4 22 | # via 23 | # httpcore 24 | # httpx 25 | click==8.1.7 26 | # via 27 | # hatch.envs.versions.1.10.x 28 | # hatch 29 | # pip-tools 30 | # userpath 31 | coverage==7.6.0 32 | # via pytest-cov 33 | distlib==0.3.8 34 | # via virtualenv 35 | execnet==2.1.1 36 | # via pytest-xdist 37 | filelock==3.15.4 38 | # via virtualenv 39 | h11==0.14.0 40 | # via httpcore 41 | hatch==1.10.0 42 | # via hatch.envs.versions.1.10.x 43 | hatchling==1.25.0 44 | # via hatch 45 | httpcore==1.0.5 46 | # via httpx 47 | httpx==0.27.0 48 | # via hatch 49 | hyperlink==21.0.0 50 | # via hatch 51 | idna==3.7 52 | # via 53 | # anyio 54 | # httpx 55 | # hyperlink 56 | importlib-metadata==8.0.0 57 | # via keyring 58 | iniconfig==2.0.0 59 | # via pytest 60 | jaraco-classes==3.4.0 61 | # via keyring 62 | jaraco-context==5.3.0 63 | # via keyring 64 | jaraco-functools==4.0.1 65 | # via keyring 66 | keyring==25.2.1 67 | # via hatch 68 | markdown-it-py==3.0.0 69 | # via rich 70 | mdurl==0.1.2 71 | # via markdown-it-py 72 | more-itertools==10.3.0 73 | # via 74 | # jaraco-classes 75 | # jaraco-functools 76 | packaging==24.1 77 | # via 78 | # build 79 | # hatch 80 | # hatchling 81 | # pytest 82 | pathspec==0.12.1 83 | # via hatchling 84 | pexpect==4.9.0 85 | # via hatch 86 | pip-tools==7.4.1 87 | # via hatch.envs.versions.1.10.x 88 | platformdirs==4.2.2 89 | # via 90 | # hatch 91 | # virtualenv 92 | pluggy==1.5.0 93 | # via 94 | # hatchling 95 | # pytest 96 | ptyprocess==0.7.0 97 | # via pexpect 98 | pygments==2.18.0 99 | # via rich 100 | pyproject-hooks==1.1.0 101 | # via 102 | # build 103 | # pip-tools 104 | pytest==8.2.2 105 | # via 106 | # hatch.envs.versions.1.10.x 107 | # pytest-cov 108 | # pytest-xdist 109 | pytest-cov==5.0.0 110 | # via hatch.envs.versions.1.10.x 111 | pytest-xdist==3.6.1 112 | # via hatch.envs.versions.1.10.x 113 | rich==13.7.1 114 | # via 115 | # hatch.envs.versions.1.10.x 116 | # hatch 117 | shellingham==1.5.4 118 | # via hatch 119 | sniffio==1.3.1 120 | # via 121 | # anyio 122 | # httpx 123 | tomli-w==1.0.0 124 | # via hatch 125 | tomlkit==0.13.0 126 | # via 127 | # hatch.envs.versions.1.10.x 128 | # hatch 129 | trove-classifiers==2024.7.2 130 | # via hatchling 131 | userpath==1.9.2 132 | # via hatch 133 | uv==0.2.24 134 | # via hatch 135 | virtualenv==20.26.3 136 | # via hatch 137 | wheel==0.43.0 138 | # via pip-tools 139 | zipp==3.19.2 140 | # via importlib-metadata 141 | zstandard==0.22.0 142 | # via hatch 143 | 144 | # The following packages are considered to be unsafe in a requirements file: 145 | # pip 146 | # setuptools 147 | -------------------------------------------------------------------------------- /requirements/requirements-versions.1.11.x.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - pytest 5 | # - pytest-cov 6 | # - tomlkit 7 | # - pytest-xdist 8 | # - hatch~=1.11.1 9 | # - click 10 | # - hatch<2,>=1.7.0 11 | # - pip-tools>=6 12 | # - rich 13 | # 14 | 15 | anyio==4.4.0 16 | # via httpx 17 | backports-tarfile==1.2.0 18 | # via jaraco-context 19 | build==1.2.1 20 | # via pip-tools 21 | certifi==2024.7.4 22 | # via 23 | # httpcore 24 | # httpx 25 | click==8.1.7 26 | # via 27 | # hatch.envs.versions.1.11.x 28 | # hatch 29 | # pip-tools 30 | # userpath 31 | coverage==7.6.0 32 | # via pytest-cov 33 | distlib==0.3.8 34 | # via virtualenv 35 | execnet==2.1.1 36 | # via pytest-xdist 37 | filelock==3.15.4 38 | # via virtualenv 39 | h11==0.14.0 40 | # via httpcore 41 | hatch==1.11.1 42 | # via hatch.envs.versions.1.11.x 43 | hatchling==1.25.0 44 | # via hatch 45 | httpcore==1.0.5 46 | # via httpx 47 | httpx==0.27.0 48 | # via hatch 49 | hyperlink==21.0.0 50 | # via hatch 51 | idna==3.7 52 | # via 53 | # anyio 54 | # httpx 55 | # hyperlink 56 | importlib-metadata==8.0.0 57 | # via keyring 58 | iniconfig==2.0.0 59 | # via pytest 60 | jaraco-classes==3.4.0 61 | # via keyring 62 | jaraco-context==5.3.0 63 | # via keyring 64 | jaraco-functools==4.0.1 65 | # via keyring 66 | keyring==25.2.1 67 | # via hatch 68 | markdown-it-py==3.0.0 69 | # via rich 70 | mdurl==0.1.2 71 | # via markdown-it-py 72 | more-itertools==10.3.0 73 | # via 74 | # jaraco-classes 75 | # jaraco-functools 76 | packaging==24.1 77 | # via 78 | # build 79 | # hatch 80 | # hatchling 81 | # pytest 82 | pathspec==0.12.1 83 | # via hatchling 84 | pexpect==4.9.0 85 | # via hatch 86 | pip-tools==7.4.1 87 | # via hatch.envs.versions.1.11.x 88 | platformdirs==4.2.2 89 | # via 90 | # hatch 91 | # virtualenv 92 | pluggy==1.5.0 93 | # via 94 | # hatchling 95 | # pytest 96 | ptyprocess==0.7.0 97 | # via pexpect 98 | pygments==2.18.0 99 | # via rich 100 | pyproject-hooks==1.1.0 101 | # via 102 | # build 103 | # pip-tools 104 | pytest==8.2.2 105 | # via 106 | # hatch.envs.versions.1.11.x 107 | # pytest-cov 108 | # pytest-xdist 109 | pytest-cov==5.0.0 110 | # via hatch.envs.versions.1.11.x 111 | pytest-xdist==3.6.1 112 | # via hatch.envs.versions.1.11.x 113 | rich==13.7.1 114 | # via 115 | # hatch.envs.versions.1.11.x 116 | # hatch 117 | shellingham==1.5.4 118 | # via hatch 119 | sniffio==1.3.1 120 | # via 121 | # anyio 122 | # httpx 123 | tomli-w==1.0.0 124 | # via hatch 125 | tomlkit==0.13.0 126 | # via 127 | # hatch.envs.versions.1.11.x 128 | # hatch 129 | trove-classifiers==2024.7.2 130 | # via hatchling 131 | userpath==1.9.2 132 | # via hatch 133 | uv==0.2.24 134 | # via hatch 135 | virtualenv==20.26.3 136 | # via hatch 137 | wheel==0.43.0 138 | # via pip-tools 139 | zipp==3.19.2 140 | # via importlib-metadata 141 | zstandard==0.22.0 142 | # via hatch 143 | 144 | # The following packages are considered to be unsafe in a requirements file: 145 | # pip 146 | # setuptools 147 | -------------------------------------------------------------------------------- /requirements/requirements-versions.1.12.x.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - pytest 5 | # - pytest-cov 6 | # - tomlkit 7 | # - pytest-xdist 8 | # - hatch~=1.12.0 9 | # - click 10 | # - hatch<2,>=1.7.0 11 | # - pip-tools>=6 12 | # - rich 13 | # 14 | 15 | anyio==4.4.0 16 | # via httpx 17 | backports-tarfile==1.2.0 18 | # via jaraco-context 19 | build==1.2.1 20 | # via pip-tools 21 | certifi==2024.7.4 22 | # via 23 | # httpcore 24 | # httpx 25 | click==8.1.7 26 | # via 27 | # hatch.envs.versions.1.12.x 28 | # hatch 29 | # pip-tools 30 | # userpath 31 | coverage==7.6.0 32 | # via pytest-cov 33 | distlib==0.3.8 34 | # via virtualenv 35 | execnet==2.1.1 36 | # via pytest-xdist 37 | filelock==3.15.4 38 | # via virtualenv 39 | h11==0.14.0 40 | # via httpcore 41 | hatch==1.12.0 42 | # via hatch.envs.versions.1.12.x 43 | hatchling==1.25.0 44 | # via hatch 45 | httpcore==1.0.5 46 | # via httpx 47 | httpx==0.27.0 48 | # via hatch 49 | hyperlink==21.0.0 50 | # via hatch 51 | idna==3.7 52 | # via 53 | # anyio 54 | # httpx 55 | # hyperlink 56 | importlib-metadata==8.0.0 57 | # via keyring 58 | iniconfig==2.0.0 59 | # via pytest 60 | jaraco-classes==3.4.0 61 | # via keyring 62 | jaraco-context==5.3.0 63 | # via keyring 64 | jaraco-functools==4.0.1 65 | # via keyring 66 | keyring==25.2.1 67 | # via hatch 68 | markdown-it-py==3.0.0 69 | # via rich 70 | mdurl==0.1.2 71 | # via markdown-it-py 72 | more-itertools==10.3.0 73 | # via 74 | # jaraco-classes 75 | # jaraco-functools 76 | packaging==24.1 77 | # via 78 | # build 79 | # hatch 80 | # hatchling 81 | # pytest 82 | pathspec==0.12.1 83 | # via hatchling 84 | pexpect==4.9.0 85 | # via hatch 86 | pip-tools==7.4.1 87 | # via hatch.envs.versions.1.12.x 88 | platformdirs==4.2.2 89 | # via 90 | # hatch 91 | # virtualenv 92 | pluggy==1.5.0 93 | # via 94 | # hatchling 95 | # pytest 96 | ptyprocess==0.7.0 97 | # via pexpect 98 | pygments==2.18.0 99 | # via rich 100 | pyproject-hooks==1.1.0 101 | # via 102 | # build 103 | # pip-tools 104 | pytest==8.2.2 105 | # via 106 | # hatch.envs.versions.1.12.x 107 | # pytest-cov 108 | # pytest-xdist 109 | pytest-cov==5.0.0 110 | # via hatch.envs.versions.1.12.x 111 | pytest-xdist==3.6.1 112 | # via hatch.envs.versions.1.12.x 113 | rich==13.7.1 114 | # via 115 | # hatch.envs.versions.1.12.x 116 | # hatch 117 | shellingham==1.5.4 118 | # via hatch 119 | sniffio==1.3.1 120 | # via 121 | # anyio 122 | # httpx 123 | tomli-w==1.0.0 124 | # via hatch 125 | tomlkit==0.13.0 126 | # via 127 | # hatch.envs.versions.1.12.x 128 | # hatch 129 | trove-classifiers==2024.7.2 130 | # via hatchling 131 | userpath==1.9.2 132 | # via hatch 133 | uv==0.2.24 134 | # via hatch 135 | virtualenv==20.26.3 136 | # via hatch 137 | wheel==0.43.0 138 | # via pip-tools 139 | zipp==3.19.2 140 | # via importlib-metadata 141 | zstandard==0.22.0 142 | # via hatch 143 | 144 | # The following packages are considered to be unsafe in a requirements file: 145 | # pip 146 | # setuptools 147 | -------------------------------------------------------------------------------- /requirements/requirements-versions.1.7.x.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - pytest 5 | # - pytest-cov 6 | # - tomlkit 7 | # - pytest-xdist 8 | # - hatch~=1.7.0 9 | # - click 10 | # - hatch<2,>=1.7.0 11 | # - pip-tools>=6 12 | # - rich 13 | # 14 | 15 | anyio==4.4.0 16 | # via httpx 17 | backports-tarfile==1.2.0 18 | # via jaraco-context 19 | build==1.2.1 20 | # via pip-tools 21 | certifi==2024.7.4 22 | # via 23 | # httpcore 24 | # httpx 25 | click==8.1.7 26 | # via 27 | # hatch.envs.versions.1.7.x 28 | # hatch 29 | # pip-tools 30 | # userpath 31 | coverage==7.6.0 32 | # via pytest-cov 33 | distlib==0.3.8 34 | # via virtualenv 35 | execnet==2.1.1 36 | # via pytest-xdist 37 | filelock==3.15.4 38 | # via virtualenv 39 | h11==0.14.0 40 | # via httpcore 41 | hatch==1.7.0 42 | # via hatch.envs.versions.1.7.x 43 | hatchling==1.25.0 44 | # via hatch 45 | httpcore==1.0.5 46 | # via httpx 47 | httpx==0.27.0 48 | # via hatch 49 | hyperlink==21.0.0 50 | # via hatch 51 | idna==3.7 52 | # via 53 | # anyio 54 | # httpx 55 | # hyperlink 56 | importlib-metadata==8.0.0 57 | # via keyring 58 | iniconfig==2.0.0 59 | # via pytest 60 | jaraco-classes==3.4.0 61 | # via keyring 62 | jaraco-context==5.3.0 63 | # via keyring 64 | jaraco-functools==4.0.1 65 | # via keyring 66 | keyring==25.2.1 67 | # via hatch 68 | markdown-it-py==3.0.0 69 | # via rich 70 | mdurl==0.1.2 71 | # via markdown-it-py 72 | more-itertools==10.3.0 73 | # via 74 | # jaraco-classes 75 | # jaraco-functools 76 | packaging==24.1 77 | # via 78 | # build 79 | # hatch 80 | # hatchling 81 | # pytest 82 | pathspec==0.12.1 83 | # via hatchling 84 | pexpect==4.9.0 85 | # via hatch 86 | pip-tools==7.4.1 87 | # via hatch.envs.versions.1.7.x 88 | platformdirs==4.2.2 89 | # via 90 | # hatch 91 | # virtualenv 92 | pluggy==1.5.0 93 | # via 94 | # hatchling 95 | # pytest 96 | ptyprocess==0.7.0 97 | # via pexpect 98 | pygments==2.18.0 99 | # via rich 100 | pyperclip==1.9.0 101 | # via hatch 102 | pyproject-hooks==1.1.0 103 | # via 104 | # build 105 | # pip-tools 106 | pytest==8.2.2 107 | # via 108 | # hatch.envs.versions.1.7.x 109 | # pytest-cov 110 | # pytest-xdist 111 | pytest-cov==5.0.0 112 | # via hatch.envs.versions.1.7.x 113 | pytest-xdist==3.6.1 114 | # via hatch.envs.versions.1.7.x 115 | rich==13.7.1 116 | # via 117 | # hatch.envs.versions.1.7.x 118 | # hatch 119 | shellingham==1.5.4 120 | # via hatch 121 | sniffio==1.3.1 122 | # via 123 | # anyio 124 | # httpx 125 | tomli-w==1.0.0 126 | # via hatch 127 | tomlkit==0.13.0 128 | # via 129 | # hatch.envs.versions.1.7.x 130 | # hatch 131 | trove-classifiers==2024.7.2 132 | # via hatchling 133 | userpath==1.9.2 134 | # via hatch 135 | virtualenv==20.26.3 136 | # via hatch 137 | wheel==0.43.0 138 | # via pip-tools 139 | zipp==3.19.2 140 | # via importlib-metadata 141 | 142 | # The following packages are considered to be unsafe in a requirements file: 143 | # pip 144 | # setuptools 145 | -------------------------------------------------------------------------------- /requirements/requirements-versions.1.8.x.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - pytest 5 | # - pytest-cov 6 | # - tomlkit 7 | # - pytest-xdist 8 | # - hatch~=1.8.1 9 | # - click 10 | # - hatch<2,>=1.7.0 11 | # - pip-tools>=6 12 | # - rich 13 | # 14 | 15 | anyio==4.4.0 16 | # via httpx 17 | backports-tarfile==1.2.0 18 | # via jaraco-context 19 | build==1.2.1 20 | # via pip-tools 21 | certifi==2024.7.4 22 | # via 23 | # httpcore 24 | # httpx 25 | click==8.1.7 26 | # via 27 | # hatch.envs.versions.1.8.x 28 | # hatch 29 | # pip-tools 30 | # userpath 31 | coverage==7.6.0 32 | # via pytest-cov 33 | distlib==0.3.8 34 | # via virtualenv 35 | execnet==2.1.1 36 | # via pytest-xdist 37 | filelock==3.15.4 38 | # via virtualenv 39 | h11==0.14.0 40 | # via httpcore 41 | hatch==1.8.1 42 | # via hatch.envs.versions.1.8.x 43 | hatchling==1.25.0 44 | # via hatch 45 | httpcore==1.0.5 46 | # via httpx 47 | httpx==0.27.0 48 | # via hatch 49 | hyperlink==21.0.0 50 | # via hatch 51 | idna==3.7 52 | # via 53 | # anyio 54 | # httpx 55 | # hyperlink 56 | importlib-metadata==8.0.0 57 | # via keyring 58 | iniconfig==2.0.0 59 | # via pytest 60 | jaraco-classes==3.4.0 61 | # via keyring 62 | jaraco-context==5.3.0 63 | # via keyring 64 | jaraco-functools==4.0.1 65 | # via keyring 66 | keyring==25.2.1 67 | # via hatch 68 | markdown-it-py==3.0.0 69 | # via rich 70 | mdurl==0.1.2 71 | # via markdown-it-py 72 | more-itertools==10.3.0 73 | # via 74 | # jaraco-classes 75 | # jaraco-functools 76 | packaging==24.1 77 | # via 78 | # build 79 | # hatch 80 | # hatchling 81 | # pytest 82 | pathspec==0.12.1 83 | # via hatchling 84 | pexpect==4.9.0 85 | # via hatch 86 | pip-tools==7.4.1 87 | # via hatch.envs.versions.1.8.x 88 | platformdirs==4.2.2 89 | # via 90 | # hatch 91 | # virtualenv 92 | pluggy==1.5.0 93 | # via 94 | # hatchling 95 | # pytest 96 | ptyprocess==0.7.0 97 | # via pexpect 98 | pygments==2.18.0 99 | # via rich 100 | pyproject-hooks==1.1.0 101 | # via 102 | # build 103 | # pip-tools 104 | pytest==8.2.2 105 | # via 106 | # hatch.envs.versions.1.8.x 107 | # pytest-cov 108 | # pytest-xdist 109 | pytest-cov==5.0.0 110 | # via hatch.envs.versions.1.8.x 111 | pytest-xdist==3.6.1 112 | # via hatch.envs.versions.1.8.x 113 | rich==13.7.1 114 | # via 115 | # hatch.envs.versions.1.8.x 116 | # hatch 117 | shellingham==1.5.4 118 | # via hatch 119 | sniffio==1.3.1 120 | # via 121 | # anyio 122 | # httpx 123 | tomli-w==1.0.0 124 | # via hatch 125 | tomlkit==0.13.0 126 | # via 127 | # hatch.envs.versions.1.8.x 128 | # hatch 129 | trove-classifiers==2024.7.2 130 | # via hatchling 131 | userpath==1.9.2 132 | # via hatch 133 | virtualenv==20.26.3 134 | # via hatch 135 | wheel==0.43.0 136 | # via pip-tools 137 | zipp==3.19.2 138 | # via importlib-metadata 139 | zstandard==0.22.0 140 | # via hatch 141 | 142 | # The following packages are considered to be unsafe in a requirements file: 143 | # pip 144 | # setuptools 145 | -------------------------------------------------------------------------------- /requirements/requirements-versions.1.9.x.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - pytest 5 | # - pytest-cov 6 | # - tomlkit 7 | # - pytest-xdist 8 | # - hatch~=1.9.7 9 | # - click 10 | # - hatch<2,>=1.7.0 11 | # - pip-tools>=6 12 | # - rich 13 | # 14 | 15 | anyio==4.4.0 16 | # via httpx 17 | backports-tarfile==1.2.0 18 | # via jaraco-context 19 | build==1.2.1 20 | # via pip-tools 21 | certifi==2024.7.4 22 | # via 23 | # httpcore 24 | # httpx 25 | click==8.1.7 26 | # via 27 | # hatch.envs.versions.1.9.x 28 | # hatch 29 | # pip-tools 30 | # userpath 31 | coverage==7.6.0 32 | # via pytest-cov 33 | distlib==0.3.8 34 | # via virtualenv 35 | editables==0.5 36 | # via hatchling 37 | execnet==2.1.1 38 | # via pytest-xdist 39 | filelock==3.15.4 40 | # via virtualenv 41 | h11==0.14.0 42 | # via httpcore 43 | hatch==1.9.7 44 | # via hatch.envs.versions.1.9.x 45 | hatchling==1.21.1 46 | # via hatch 47 | httpcore==1.0.5 48 | # via httpx 49 | httpx==0.27.0 50 | # via hatch 51 | hyperlink==21.0.0 52 | # via hatch 53 | idna==3.7 54 | # via 55 | # anyio 56 | # httpx 57 | # hyperlink 58 | importlib-metadata==8.0.0 59 | # via keyring 60 | iniconfig==2.0.0 61 | # via pytest 62 | jaraco-classes==3.4.0 63 | # via keyring 64 | jaraco-context==5.3.0 65 | # via keyring 66 | jaraco-functools==4.0.1 67 | # via keyring 68 | keyring==25.2.1 69 | # via hatch 70 | markdown-it-py==3.0.0 71 | # via rich 72 | mdurl==0.1.2 73 | # via markdown-it-py 74 | more-itertools==10.3.0 75 | # via 76 | # jaraco-classes 77 | # jaraco-functools 78 | packaging==24.1 79 | # via 80 | # build 81 | # hatch 82 | # hatchling 83 | # pytest 84 | pathspec==0.12.1 85 | # via hatchling 86 | pexpect==4.9.0 87 | # via hatch 88 | pip-tools==7.4.1 89 | # via hatch.envs.versions.1.9.x 90 | platformdirs==4.2.2 91 | # via 92 | # hatch 93 | # virtualenv 94 | pluggy==1.5.0 95 | # via 96 | # hatchling 97 | # pytest 98 | ptyprocess==0.7.0 99 | # via pexpect 100 | pygments==2.18.0 101 | # via rich 102 | pyproject-hooks==1.1.0 103 | # via 104 | # build 105 | # pip-tools 106 | pytest==8.2.2 107 | # via 108 | # hatch.envs.versions.1.9.x 109 | # pytest-cov 110 | # pytest-xdist 111 | pytest-cov==5.0.0 112 | # via hatch.envs.versions.1.9.x 113 | pytest-xdist==3.6.1 114 | # via hatch.envs.versions.1.9.x 115 | rich==13.7.1 116 | # via 117 | # hatch.envs.versions.1.9.x 118 | # hatch 119 | shellingham==1.5.4 120 | # via hatch 121 | sniffio==1.3.1 122 | # via 123 | # anyio 124 | # httpx 125 | tomli-w==1.0.0 126 | # via hatch 127 | tomlkit==0.13.0 128 | # via 129 | # hatch.envs.versions.1.9.x 130 | # hatch 131 | trove-classifiers==2024.7.2 132 | # via hatchling 133 | userpath==1.9.2 134 | # via hatch 135 | virtualenv==20.25.3 136 | # via hatch 137 | wheel==0.43.0 138 | # via pip-tools 139 | zipp==3.19.2 140 | # via importlib-metadata 141 | zstandard==0.22.0 142 | # via hatch 143 | 144 | # The following packages are considered to be unsafe in a requirements file: 145 | # pip 146 | # setuptools 147 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juftin/hatch-pip-compile/9337724557a2a8efe663d76a98faf96d3176d063/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shared fixtures for tests. 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | import contextlib 8 | import os 9 | import pathlib 10 | import shutil 11 | from dataclasses import dataclass, field 12 | from subprocess import CompletedProcess 13 | from typing import Generator 14 | from unittest.mock import patch 15 | 16 | import hatch 17 | import pytest 18 | import tomlkit 19 | from click.testing import CliRunner, Result 20 | from hatch.cli.application import Application 21 | from hatch.config.constants import AppEnvVars, ConfigEnvVars, PublishEnvVars 22 | from hatch.project.core import Project 23 | from hatch.utils.fs import Path, temp_directory 24 | from hatch.utils.platform import Platform 25 | 26 | from hatch_pip_compile.plugin import PipCompileEnvironment 27 | 28 | 29 | @pytest.fixture 30 | def mock_check_command() -> Generator[patch, None, None]: 31 | """ 32 | Disable the `plugin_check_command` for testing 33 | """ 34 | with patch("hatch_pip_compile.plugin.PipCompileEnvironment.plugin_check_command") as mock: 35 | mock.return_value = CompletedProcess(args=[], returncode=0, stdout=b"", stderr=b"") 36 | yield mock 37 | 38 | 39 | @pytest.fixture 40 | def subprocess_run() -> Generator[patch, None, None]: 41 | """ 42 | Disable the `subprocess.run` for testing 43 | """ 44 | with patch("subprocess.run") as mock: 45 | mock.return_value = CompletedProcess(args=[], returncode=0, stdout=b"", stderr=b"") 46 | yield mock 47 | 48 | 49 | @pytest.fixture 50 | def platform() -> Platform: 51 | """ 52 | Platform 53 | """ 54 | return Platform() 55 | 56 | 57 | @pytest.fixture 58 | def isolation(platform: Platform) -> Generator[Path, None, None]: 59 | """ 60 | Isolated hatch environment for testing. 61 | """ 62 | with temp_directory() as temp_dir: 63 | data_dir = pathlib.Path(__file__).parent / "data" 64 | shutil.copytree(data_dir, temp_dir, dirs_exist_ok=True) 65 | data_dir = temp_dir / "data" 66 | data_dir.mkdir() 67 | cache_dir = temp_dir / "cache" 68 | cache_dir.mkdir() 69 | default_env_vars = { 70 | AppEnvVars.NO_COLOR: "1", 71 | ConfigEnvVars.DATA: str(data_dir), 72 | ConfigEnvVars.CACHE: str(cache_dir), 73 | PublishEnvVars.REPO: "dev", 74 | "HATCH_SELF_TESTING": "true", 75 | "PYAPP_COMMAND_NAME": os.urandom(4).hex(), 76 | "GIT_AUTHOR_NAME": "Foo Bar", 77 | "GIT_AUTHOR_EMAIL": "foo@bar.baz", 78 | "COLUMNS": "80", 79 | "LINES": "24", 80 | } 81 | if platform.windows: # pragma: no cover 82 | default_env_vars["COMSPEC"] = "cmd.exe" 83 | else: 84 | default_env_vars["SHELL"] = "sh" 85 | 86 | with temp_dir.as_cwd(default_env_vars): 87 | os.environ.pop(AppEnvVars.ENV_ACTIVE, None) 88 | os.environ.pop(AppEnvVars.FORCE_COLOR, None) 89 | yield temp_dir 90 | 91 | 92 | @dataclass 93 | class PipCompileFixture: 94 | """ 95 | Testing Fixture Data Container 96 | """ 97 | 98 | __test__ = False 99 | 100 | isolation: pathlib.Path 101 | toml_doc: tomlkit.TOMLDocument 102 | pyproject: pathlib.Path 103 | project: Project 104 | platform: Platform 105 | isolated_data_dir: pathlib.Path 106 | 107 | application: Application = field(init=False) 108 | default_environment: PipCompileEnvironment = field(init=False) 109 | test_environment: PipCompileEnvironment = field(init=False) 110 | cli_runner: CliRunner = field(init=False) 111 | 112 | def __post_init__(self) -> None: 113 | """ 114 | Post Init 115 | """ 116 | self.application = Application( 117 | exit_func=lambda x: None, # noqa: ARG005 118 | verbosity=0, 119 | interactive=False, 120 | enable_color=False, 121 | ) 122 | self.application.data_dir = self.isolated_data_dir / "data" 123 | self.application.config_file.load() 124 | self.application.cache_dir = self.isolated_data_dir / "cache" 125 | self.application.project = self.project 126 | self.current_environment = self.reload_environment("default") 127 | self.default_environment = self.reload_environment("default") 128 | self.test_environment = self.reload_environment("test") 129 | self.cli_runner = CliRunner() 130 | 131 | def reload_environment(self, environment: str | PipCompileEnvironment) -> PipCompileEnvironment: 132 | """ 133 | Reload a new environment given the current state of the isolated project 134 | """ 135 | if isinstance(environment, PipCompileEnvironment): 136 | environment_name = environment.name 137 | else: 138 | environment_name = environment 139 | env = self.application.get_environment(env_name=environment_name) 140 | return env 141 | 142 | def update_pyproject(self) -> None: 143 | """ 144 | Update pyproject.toml 145 | """ 146 | tomlkit.dump(self.toml_doc, self.pyproject.open("w")) 147 | 148 | @contextlib.contextmanager 149 | def chdir(self) -> Generator[None, None, None]: 150 | """ 151 | Change the working directory to the isolation 152 | """ 153 | current_dir = os.getcwd() 154 | try: 155 | os.chdir(self.isolation) 156 | yield 157 | finally: 158 | os.chdir(current_dir) 159 | 160 | def update_environment_resolver( 161 | self, environment: str | PipCompileEnvironment, resolver: str 162 | ) -> PipCompileEnvironment: 163 | """ 164 | Update the environment resolver 165 | """ 166 | if isinstance(environment, PipCompileEnvironment): 167 | environment_name = environment.name 168 | else: 169 | environment_name = environment 170 | self.toml_doc["tool"]["hatch"]["envs"][environment_name]["pip-compile-resolver"] = resolver 171 | self.update_pyproject() 172 | return self.reload_environment(environment_name) 173 | 174 | def update_environment_installer( 175 | self, environment: str | PipCompileEnvironment, installer: str 176 | ) -> PipCompileEnvironment: 177 | """ 178 | Update the environment installer 179 | """ 180 | if isinstance(environment, PipCompileEnvironment): 181 | environment_name = environment.name 182 | else: 183 | environment_name = environment 184 | self.toml_doc["tool"]["hatch"]["envs"][environment_name][ 185 | "pip-compile-installer" 186 | ] = installer 187 | self.update_pyproject() 188 | return self.reload_environment(environment_name) 189 | 190 | def cli_invoke(self, args: list[str], env: dict[str, str] | None = None) -> Result: 191 | """ 192 | Invoke the CLI 193 | """ 194 | invoke_kwargs = {"args": args} 195 | if env: 196 | invoke_kwargs["env"] = env 197 | with self.cli_runner.isolated_filesystem(self.isolation): 198 | return self.cli_runner.invoke(hatch.cli.hatch, **invoke_kwargs) 199 | 200 | def invoke_environment( 201 | self, environment: PipCompileEnvironment, env: dict[str, str] | None = None 202 | ) -> Result: 203 | """ 204 | Sync the environment 205 | """ 206 | result = self.cli_invoke( 207 | args=["env", "run", "--env", environment.name, "--", "python", "--version"], 208 | env=env, 209 | ) 210 | return result 211 | 212 | @staticmethod 213 | def virtualenv_exists(environment: PipCompileEnvironment) -> bool: 214 | """ 215 | Check if the virtual environment exists 216 | """ 217 | virtualenv_cfg = environment.virtual_env.directory / "pyvenv.cfg" 218 | return virtualenv_cfg.exists() 219 | 220 | def is_installed( 221 | self, 222 | environment: PipCompileEnvironment, 223 | package: str, 224 | ) -> bool: 225 | """ 226 | Check if a package is installed in the environment 227 | 228 | This method simply checks if the package is in the site-packages directory 229 | of the virtual environment. 230 | """ 231 | if not self.virtualenv_exists(environment=environment): 232 | return False 233 | if self.platform.windows: 234 | site_packages = environment.virtual_env.directory / "Lib" / "site-packages" 235 | if not site_packages.exists(): 236 | return False 237 | else: 238 | site_packages_search = environment.virtual_env.directory.glob( 239 | "lib/python*/site-packages" 240 | ) 241 | site_packages = next(site_packages_search, None) 242 | if site_packages is None: 243 | return False 244 | package_dir = site_packages / package 245 | return (package_dir / "__init__.py").exists() 246 | 247 | 248 | @pytest.fixture 249 | def pip_compile( 250 | isolation: Path, 251 | platform: Platform, 252 | ) -> PipCompileFixture: 253 | """ 254 | PipCompile testing fixture 255 | """ 256 | pyproject = isolation / "pyproject.toml" 257 | isolated_data_dir = Path(os.environ[ConfigEnvVars.DATA]) 258 | return PipCompileFixture( 259 | isolation=isolation, 260 | toml_doc=tomlkit.parse(string=pyproject.read_text()), 261 | pyproject=pyproject, 262 | project=Project(path=isolation), 263 | platform=platform, 264 | isolated_data_dir=isolated_data_dir, 265 | ) 266 | 267 | 268 | @pytest.fixture(autouse=True) 269 | def pip_compile_disable(monkeypatch: pytest.MonkeyPatch) -> None: 270 | """ 271 | Delete the PIP_COMPILE_DISABLE environment variable 272 | """ 273 | monkeypatch.delenv("PIP_COMPILE_DISABLE", raising=False) 274 | 275 | 276 | resolver_param = pytest.mark.parametrize("resolver", ["pip-compile", "uv"]) 277 | installer_param = pytest.mark.parametrize("installer", ["pip", "pip-sync", "uv"]) 278 | -------------------------------------------------------------------------------- /tests/data/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juftin/hatch-pip-compile/9337724557a2a8efe663d76a98faf96d3176d063/tests/data/README.md -------------------------------------------------------------------------------- /tests/data/hatch_pip_compile_test.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juftin/hatch-pip-compile/9337724557a2a8efe663d76a98faf96d3176d063/tests/data/hatch_pip_compile_test.py -------------------------------------------------------------------------------- /tests/data/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "hatchling.build" 3 | requires = ["hatchling"] 4 | 5 | [project] 6 | authors = [ 7 | {name = "Justin Flannery", email = "juftin@juftin.com"} 8 | ] 9 | dependencies = [ 10 | "hatch" 11 | ] 12 | description = "testing hatch-pip-compile" 13 | license = "MIT" 14 | name = "hatch-pip-compile-test" 15 | readme = "README.md" 16 | requires-python = ">=3.8" 17 | version = "0.1.0" 18 | 19 | [tool.hatch.envs.default] 20 | path = ".venv/hatch-pip-compile-test" 21 | pip-compile-constraint = "default" 22 | type = "pip-compile" 23 | 24 | [tool.hatch.envs.docs] 25 | dependencies = [ 26 | "mkdocs" 27 | ] 28 | dev-mode = false 29 | lock-filename = "requirements/{env_name}.lock" 30 | path = ".venv/docs" 31 | pip-compile-constraint = "misc" 32 | pip-compile-hashes = true 33 | 34 | [tool.hatch.envs.lint] 35 | dependencies = [ 36 | "mypy>=1.6.1", 37 | "ruff~=0.1.4" 38 | ] 39 | detached = true 40 | path = ".venv/lint" 41 | type = "pip-compile" 42 | 43 | [tool.hatch.envs.misc] 44 | dependencies = [] 45 | detached = true 46 | path = ".venv/misc" 47 | skip-install = true 48 | type = "pip-compile" 49 | 50 | [tool.hatch.envs.test] 51 | dependencies = [ 52 | "pytest", 53 | "pytest-cov" 54 | ] 55 | path = ".venv/test" 56 | -------------------------------------------------------------------------------- /tests/data/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - hatch 5 | # 6 | 7 | anyio==4.1.0 8 | # via httpx 9 | certifi==2023.11.17 10 | # via 11 | # httpcore 12 | # httpx 13 | cffi==1.16.0 14 | # via cryptography 15 | click==8.1.7 16 | # via 17 | # hatch 18 | # userpath 19 | cryptography==41.0.7 20 | # via secretstorage 21 | distlib==0.3.7 22 | # via virtualenv 23 | editables==0.5 24 | # via hatchling 25 | filelock==3.13.1 26 | # via virtualenv 27 | h11==0.14.0 28 | # via httpcore 29 | hatch==1.7.0 30 | # via hatch.envs.default 31 | hatchling==1.18.0 32 | # via hatch 33 | httpcore==1.0.2 34 | # via httpx 35 | httpx==0.25.2 36 | # via hatch 37 | hyperlink==21.0.0 38 | # via hatch 39 | idna==3.6 40 | # via 41 | # anyio 42 | # httpx 43 | # hyperlink 44 | importlib-metadata==7.0.0 45 | # via keyring 46 | jaraco-classes==3.3.0 47 | # via keyring 48 | jeepney==0.8.0 49 | # via 50 | # keyring 51 | # secretstorage 52 | keyring==24.3.0 53 | # via hatch 54 | markdown-it-py==3.0.0 55 | # via rich 56 | mdurl==0.1.2 57 | # via markdown-it-py 58 | more-itertools==10.1.0 59 | # via jaraco-classes 60 | packaging==23.2 61 | # via 62 | # hatch 63 | # hatchling 64 | pathspec==0.11.2 65 | # via hatchling 66 | pexpect==4.9.0 67 | # via hatch 68 | platformdirs==4.1.0 69 | # via 70 | # hatch 71 | # virtualenv 72 | pluggy==1.3.0 73 | # via hatchling 74 | ptyprocess==0.7.0 75 | # via pexpect 76 | pycparser==2.21 77 | # via cffi 78 | pygments==2.17.2 79 | # via rich 80 | pyperclip==1.8.2 81 | # via hatch 82 | rich==13.7.0 83 | # via hatch 84 | secretstorage==3.3.3 85 | # via keyring 86 | shellingham==1.5.4 87 | # via hatch 88 | sniffio==1.3.0 89 | # via 90 | # anyio 91 | # httpx 92 | tomli-w==1.0.0 93 | # via hatch 94 | tomlkit==0.12.5 95 | # via hatch 96 | trove-classifiers==2023.11.29 97 | # via hatchling 98 | userpath==1.9.1 99 | # via hatch 100 | virtualenv==20.25.0 101 | # via hatch 102 | zipp==3.17.0 103 | # via importlib-metadata 104 | -------------------------------------------------------------------------------- /tests/data/requirements/requirements-lint.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # - mypy>=1.6.1 5 | # - ruff~=0.1.4 6 | # 7 | 8 | mypy==1.7.1 9 | # via hatch.envs.lint 10 | mypy-extensions==1.0.0 11 | # via mypy 12 | ruff==0.1.6 13 | # via hatch.envs.lint 14 | typing-extensions==4.8.0 15 | # via mypy 16 | -------------------------------------------------------------------------------- /tests/data/requirements/requirements-test.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by hatch-pip-compile with Python 3.11 3 | # 4 | # [constraints] requirements.txt (SHA256: 9dc610f2c50b92a04dc522873ebc8dd69d35bf8ce9feeef14c5341e8e4ef3019) 5 | # 6 | # - pytest 7 | # - pytest-cov 8 | # - hatch 9 | # 10 | 11 | anyio==4.1.0 12 | # via 13 | # -c requirements.txt 14 | # httpx 15 | certifi==2023.11.17 16 | # via 17 | # -c requirements.txt 18 | # httpcore 19 | # httpx 20 | cffi==1.16.0 21 | # via 22 | # -c requirements.txt 23 | # cryptography 24 | click==8.1.7 25 | # via 26 | # -c requirements.txt 27 | # hatch 28 | # userpath 29 | coverage==7.3.2 30 | # via 31 | # coverage 32 | # pytest-cov 33 | cryptography==41.0.7 34 | # via 35 | # -c requirements.txt 36 | # secretstorage 37 | distlib==0.3.7 38 | # via 39 | # -c requirements.txt 40 | # virtualenv 41 | editables==0.5 42 | # via 43 | # -c requirements.txt 44 | # hatchling 45 | filelock==3.13.1 46 | # via 47 | # -c requirements.txt 48 | # virtualenv 49 | h11==0.14.0 50 | # via 51 | # -c requirements.txt 52 | # httpcore 53 | hatch==1.7.0 54 | # via 55 | # -c requirements.txt 56 | # hatch.envs.test 57 | hatchling==1.18.0 58 | # via 59 | # -c requirements.txt 60 | # hatch 61 | httpcore==1.0.2 62 | # via 63 | # -c requirements.txt 64 | # httpx 65 | httpx==0.25.2 66 | # via 67 | # -c requirements.txt 68 | # hatch 69 | hyperlink==21.0.0 70 | # via 71 | # -c requirements.txt 72 | # hatch 73 | idna==3.6 74 | # via 75 | # -c requirements.txt 76 | # anyio 77 | # httpx 78 | # hyperlink 79 | importlib-metadata==7.0.0 80 | # via 81 | # -c requirements.txt 82 | # keyring 83 | iniconfig==2.0.0 84 | # via pytest 85 | jaraco-classes==3.3.0 86 | # via 87 | # -c requirements.txt 88 | # keyring 89 | jeepney==0.8.0 90 | # via 91 | # -c requirements.txt 92 | # keyring 93 | # secretstorage 94 | keyring==24.3.0 95 | # via 96 | # -c requirements.txt 97 | # hatch 98 | markdown-it-py==3.0.0 99 | # via 100 | # -c requirements.txt 101 | # rich 102 | mdurl==0.1.2 103 | # via 104 | # -c requirements.txt 105 | # markdown-it-py 106 | more-itertools==10.1.0 107 | # via 108 | # -c requirements.txt 109 | # jaraco-classes 110 | packaging==23.2 111 | # via 112 | # -c requirements.txt 113 | # hatch 114 | # hatchling 115 | # pytest 116 | pathspec==0.11.2 117 | # via 118 | # -c requirements.txt 119 | # hatchling 120 | pexpect==4.9.0 121 | # via 122 | # -c requirements.txt 123 | # hatch 124 | platformdirs==4.1.0 125 | # via 126 | # -c requirements.txt 127 | # hatch 128 | # virtualenv 129 | pluggy==1.3.0 130 | # via 131 | # -c requirements.txt 132 | # hatchling 133 | # pytest 134 | ptyprocess==0.7.0 135 | # via 136 | # -c requirements.txt 137 | # pexpect 138 | pycparser==2.21 139 | # via 140 | # -c requirements.txt 141 | # cffi 142 | pygments==2.17.2 143 | # via 144 | # -c requirements.txt 145 | # rich 146 | pyperclip==1.8.2 147 | # via 148 | # -c requirements.txt 149 | # hatch 150 | pytest==7.4.3 151 | # via 152 | # hatch.envs.test 153 | # pytest-cov 154 | pytest-cov==4.1.0 155 | # via hatch.envs.test 156 | rich==13.7.0 157 | # via 158 | # -c requirements.txt 159 | # hatch 160 | secretstorage==3.3.3 161 | # via 162 | # -c requirements.txt 163 | # keyring 164 | shellingham==1.5.4 165 | # via 166 | # -c requirements.txt 167 | # hatch 168 | sniffio==1.3.0 169 | # via 170 | # -c requirements.txt 171 | # anyio 172 | # httpx 173 | tomli-w==1.0.0 174 | # via 175 | # -c requirements.txt 176 | # hatch 177 | tomlkit==0.12.5 178 | # via 179 | # -c requirements.txt 180 | # hatch 181 | trove-classifiers==2023.11.29 182 | # via 183 | # -c requirements.txt 184 | # hatchling 185 | userpath==1.9.1 186 | # via 187 | # -c requirements.txt 188 | # hatch 189 | virtualenv==20.25.0 190 | # via 191 | # -c requirements.txt 192 | # hatch 193 | zipp==3.17.0 194 | # via 195 | # -c requirements.txt 196 | # importlib-metadata 197 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing the hatch-pip-compile CLI 3 | """ 4 | 5 | from unittest.mock import Mock 6 | 7 | import click 8 | import pytest 9 | from click.testing import CliRunner 10 | 11 | from hatch_pip_compile import __version__ 12 | from hatch_pip_compile.cli import HatchCommandRunner, cli 13 | from tests.conftest import PipCompileFixture 14 | 15 | 16 | def test_cli_help() -> None: 17 | """ 18 | Test the CLI with the --help flag 19 | """ 20 | runner = CliRunner() 21 | result = runner.invoke(cli=cli, args=["--help"]) 22 | assert result.exit_code == 0 23 | 24 | 25 | def test_cli_version() -> None: 26 | """ 27 | Test the CLI with the --version flag 28 | """ 29 | runner = CliRunner() 30 | result = runner.invoke(cli=cli, args=["--version"]) 31 | assert result.exit_code == 0 32 | assert f"hatch-pip-compile, version {__version__}" in result.output 33 | 34 | 35 | def test_cli_no_args_mocked(pip_compile: PipCompileFixture, subprocess_run: Mock) -> None: 36 | """ 37 | Test the CLI with no arguments - mock the result 38 | """ 39 | runner = CliRunner() 40 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 41 | _ = runner.invoke(cli=cli) 42 | assert subprocess_run.call_count == 1 43 | subprocess_run.assert_called_once() 44 | subprocess_run.assert_called_with( 45 | args=["hatch", "env", "show", "--json"], capture_output=True, check=True 46 | ) 47 | 48 | 49 | def test_cli_no_args(pip_compile: PipCompileFixture) -> None: 50 | """ 51 | Test the full CLI with no arguments 52 | """ 53 | runner = CliRunner() 54 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 55 | result = runner.invoke(cli=cli) 56 | assert result.exit_code == 0 57 | assert "hatch-pip-compile: Targeting environments: default" in result.output 58 | assert ( 59 | "hatch-pip-compile: Running `hatch env run --env default -- python --version`" 60 | in result.output 61 | ) 62 | 63 | 64 | def test_cli_bad_env(pip_compile: PipCompileFixture) -> None: 65 | """ 66 | Test the full CLI with a non-existent environment 67 | """ 68 | runner = CliRunner() 69 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 70 | result = runner.invoke(cli=cli, args=["bad_env"]) 71 | assert result.exit_code != 0 72 | assert "error" in result.output.lower() 73 | assert ( 74 | "The following environments are not supported or unknown: bad_env. " 75 | "Supported environments are: default, docs, lint, misc, test" 76 | ) in result.output 77 | 78 | 79 | def test_cli_test_env(pip_compile: PipCompileFixture) -> None: 80 | """ 81 | Test the full CLI with the `test` argument 82 | """ 83 | runner = CliRunner() 84 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 85 | result = runner.invoke(cli=cli, args=["test"]) 86 | assert result.exit_code == 0 87 | assert "hatch-pip-compile: Targeting environments: test" in result.output 88 | assert ( 89 | "hatch-pip-compile: Running `hatch env run --env test -- python --version`" 90 | in result.output 91 | ) 92 | 93 | 94 | def test_cli_all(pip_compile: PipCompileFixture) -> None: 95 | """ 96 | Test the full CLI with the `--all` argument 97 | """ 98 | runner = CliRunner() 99 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 100 | result = runner.invoke(cli=cli, args=["--all"]) 101 | assert result.exit_code == 0 102 | assert ( 103 | "hatch-pip-compile: Targeting environments: default, docs, lint, misc, test" 104 | in result.output 105 | ) 106 | 107 | 108 | def test_cli_upgrade(pip_compile: PipCompileFixture) -> None: 109 | """ 110 | Test the full CLI with the `--upgrade` argument 111 | """ 112 | runner = CliRunner() 113 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 114 | result = runner.invoke(cli=cli, args=["--upgrade"]) 115 | assert result.exit_code == 0 116 | assert "hatch-pip-compile: Upgrading all dependencies" in result.output 117 | 118 | 119 | def test_cli_upgrade_packages(pip_compile: PipCompileFixture) -> None: 120 | """ 121 | Test the full CLI with the `--upgrade-package` argument 122 | """ 123 | runner = CliRunner() 124 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 125 | result = runner.invoke( 126 | cli=cli, args=["--upgrade-package", "requests", "--upgrade-package", "urllib3"] 127 | ) 128 | assert result.exit_code == 0 129 | assert "hatch-pip-compile: Upgrading packages: requests, urllib3" in result.output 130 | 131 | 132 | def test_cli_upgrade_test(pip_compile: PipCompileFixture) -> None: 133 | """ 134 | Test the full CLI with the `--upgrade` argument for the `test` environment 135 | """ 136 | runner = CliRunner() 137 | with runner.isolated_filesystem(temp_dir=pip_compile.isolation): 138 | result = runner.invoke(cli=cli, args=["test", "--upgrade"]) 139 | assert result.exit_code == 0 140 | assert "hatch-pip-compile: Upgrading all dependencies" in result.output 141 | assert "hatch-pip-compile: Targeting environments: test" in result.output 142 | 143 | 144 | def test_command_runner_supported_environments( 145 | monkeypatch: pytest.MonkeyPatch, 146 | pip_compile: PipCompileFixture, 147 | ) -> None: 148 | """ 149 | Test the `supported_environments` attribute 150 | """ 151 | with pip_compile.chdir(): 152 | command_runner = HatchCommandRunner( 153 | environments=["test"], 154 | upgrade=True, 155 | upgrade_packages=[], 156 | ) 157 | assert command_runner.supported_environments == {"default", "test", "lint", "docs", "misc"} 158 | 159 | 160 | def test_command_runner_non_supported_environments( 161 | monkeypatch: pytest.MonkeyPatch, 162 | pip_compile: PipCompileFixture, 163 | ) -> None: 164 | """ 165 | Test that a bad environment raises a `BadParameter` exception 166 | """ 167 | with pip_compile.chdir(): 168 | with pytest.raises( 169 | click.BadParameter, 170 | match=( 171 | "The following environments are not supported or unknown: bad_env. " 172 | "Supported environments are: default, docs, lint, misc, test" 173 | ), 174 | ): 175 | _ = HatchCommandRunner( 176 | environments=["bad_env"], 177 | upgrade=True, 178 | upgrade_packages=[], 179 | ) 180 | -------------------------------------------------------------------------------- /tests/test_installer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Installation Tests 3 | """ 4 | 5 | from unittest.mock import Mock 6 | 7 | from tests.conftest import PipCompileFixture 8 | 9 | 10 | def test_pip_install_dependencies(mock_check_command: Mock, pip_compile: PipCompileFixture) -> None: 11 | """ 12 | Assert the `pip` installation command is called with the expected arguments 13 | """ 14 | pip_compile.default_environment.create() 15 | pip_compile.default_environment.installer.install_dependencies() 16 | expected_call = [ 17 | "python", 18 | "-u", 19 | "-m", 20 | "pip", 21 | "install", 22 | "--disable-pip-version-check", 23 | "--no-python-version-warning", 24 | "-q", 25 | "--requirement", 26 | ] 27 | call_args = list(mock_check_command.call_args)[0][0][:-1] 28 | assert call_args == expected_call 29 | -------------------------------------------------------------------------------- /tests/test_integration_cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Integration Tests using the CLI 3 | """ 4 | 5 | import platform 6 | 7 | import pytest 8 | 9 | from hatch_pip_compile.exceptions import HatchPipCompileError 10 | from tests.conftest import PipCompileFixture, installer_param, resolver_param 11 | 12 | 13 | @installer_param 14 | @resolver_param 15 | @pytest.mark.parametrize("environment_name", ["default", "test", "lint", "docs", "misc"]) 16 | def test_invoke_environment_creates_env( 17 | pip_compile: PipCompileFixture, environment_name: str, resolver: str, installer: str 18 | ) -> None: 19 | """ 20 | Test using the CLI runner 21 | """ 22 | if installer == "pip-sync" and platform.system().lower() == "windows": 23 | pytest.skip("pip-sync + CLI runner not supported on Windows") 24 | environment = pip_compile.update_environment_resolver( 25 | environment=environment_name, resolver=resolver 26 | ) 27 | environment = pip_compile.update_environment_installer( 28 | environment=environment, installer=installer 29 | ) 30 | assert not pip_compile.virtualenv_exists(environment=environment) 31 | result = pip_compile.invoke_environment(environment=environment) 32 | assert result.exit_code == 0 33 | assert pip_compile.virtualenv_exists(environment=environment) 34 | 35 | 36 | def test_missing_lockfile_created(pip_compile: PipCompileFixture) -> None: 37 | """ 38 | Running the CLI without a lockfile creates one 39 | """ 40 | environment = pip_compile.default_environment 41 | environment.piptools_lock_file.unlink() 42 | assert not environment.piptools_lock_file.exists() 43 | result = pip_compile.invoke_environment(environment=environment) 44 | assert result.exit_code == 0 45 | assert environment.piptools_lock_file.exists() 46 | assert pip_compile.virtualenv_exists(environment=environment) 47 | 48 | 49 | def test_constraint_env_created(pip_compile: PipCompileFixture) -> None: 50 | """ 51 | Running the CLI with a constraint env creates one 52 | """ 53 | environment = pip_compile.test_environment 54 | environment.piptools_lock_file.unlink() 55 | environment.constraint_env.piptools_lock_file.unlink() 56 | result = pip_compile.invoke_environment(environment=environment) 57 | assert result.exit_code == 0 58 | assert environment.piptools_lock_file.exists() 59 | assert environment.constraint_env.piptools_lock_file.exists() 60 | assert environment.virtual_env.directory.exists() 61 | assert environment.constraint_env.virtual_env.directory.exists() 62 | 63 | 64 | def test_missing_lockfile_after_prepared(pip_compile: PipCompileFixture) -> None: 65 | """ 66 | After an environment is prepared the lockfile is deleted and recreated the next time 67 | """ 68 | environment = pip_compile.default_environment 69 | # Create the environment the first time 70 | result = pip_compile.invoke_environment(environment=environment) 71 | assert result.exit_code == 0 72 | # Delete the lockfile 73 | assert environment.piptools_lock_file.exists() 74 | environment.piptools_lock_file.unlink() 75 | assert not environment.piptools_lock_file.exists() 76 | # Run the environment again 77 | result = pip_compile.invoke_environment( 78 | environment=environment, 79 | ) 80 | assert result.exit_code == 0 81 | # Assert the lockfile was recreated 82 | assert environment.piptools_lock_file.exists() 83 | 84 | 85 | @resolver_param 86 | @pytest.mark.parametrize("environment_name", ["default", "test", "lint"]) 87 | def test_pip_compile_disable_cli( 88 | pip_compile: PipCompileFixture, environment_name: str, resolver: str 89 | ) -> None: 90 | """ 91 | Test that the `PIP_COMPILE_DISABLE` environment variable raises an error 92 | """ 93 | environment = pip_compile.update_environment_resolver( 94 | environment=environment_name, resolver=resolver 95 | ) 96 | environment.piptools_lock_file.unlink(missing_ok=True) 97 | result = pip_compile.invoke_environment( 98 | environment=environment, 99 | env={"PIP_COMPILE_DISABLE": "1"}, 100 | ) 101 | assert result.exit_code == 1 102 | assert isinstance(result.exception, HatchPipCompileError) 103 | 104 | 105 | def test_prune_removes_all_environments(pip_compile: PipCompileFixture) -> None: 106 | """ 107 | Assert that running `hatch env prune` removes all environments 108 | """ 109 | pip_compile.default_environment.create() 110 | pip_compile.test_environment.create() 111 | assert pip_compile.default_environment.virtual_env.directory.exists() 112 | assert pip_compile.test_environment.virtual_env.directory.exists() 113 | result = pip_compile.cli_invoke(args=["env", "prune"]) 114 | assert result.exit_code == 0 115 | assert not pip_compile.default_environment.virtual_env.directory.exists() 116 | assert not pip_compile.test_environment.virtual_env.directory.exists() 117 | 118 | 119 | @installer_param 120 | @resolver_param 121 | def test_add_new_dependency(pip_compile: PipCompileFixture, installer: str, resolver: str) -> None: 122 | """ 123 | Create a new environment, assert that it exists without the `requests` package, 124 | then add the `requests` package to the environment, assert that it is installed. 125 | """ 126 | if platform.system().lower() == "windows" and installer == "pip-sync": 127 | pytest.skip("pip-sync + CLI runner not supported on Windows") 128 | environment = pip_compile.default_environment 129 | assert not environment.virtual_env.directory.exists() 130 | pip_compile.update_environment_resolver(environment=environment, resolver=resolver) 131 | pip_compile.update_environment_installer(environment=environment, installer=installer) 132 | result = pip_compile.invoke_environment(environment=environment) 133 | assert result.exit_code == 0 134 | if result.exit_code != 0: 135 | raise result.exception 136 | assert environment.virtual_env.directory.exists() 137 | assert not pip_compile.is_installed(environment=environment, package="requests") 138 | assert "requests" not in environment.piptools_lock_file.read_text() 139 | pip_compile.toml_doc["project"]["dependencies"] += ["requests"] 140 | pip_compile.update_pyproject() 141 | new_result = pip_compile.invoke_environment(environment=environment) 142 | if new_result.exit_code != 0: 143 | raise new_result.exception 144 | assert "requests" in environment.piptools_lock_file.read_text() 145 | assert pip_compile.is_installed(environment=environment, package="requests") 146 | 147 | 148 | @installer_param 149 | @resolver_param 150 | def test_add_new_dependency_constraint_env( 151 | pip_compile: PipCompileFixture, installer: str, resolver: str 152 | ) -> None: 153 | """ 154 | Test dependency workflow in a constraint environment 155 | """ 156 | if platform.system().lower() == "windows" and installer == "pip-sync": 157 | pytest.skip("pip-sync + CLI runner not supported on Windows") 158 | environment = pip_compile.default_environment 159 | constraint_env = pip_compile.test_environment 160 | # Start with a clean slate, no environments 161 | assert not pip_compile.virtualenv_exists(environment=environment) 162 | assert not pip_compile.virtualenv_exists(environment=constraint_env) 163 | pip_compile.update_environment_resolver(environment=environment, resolver=resolver) 164 | pip_compile.update_environment_installer(environment=environment, installer=installer) 165 | # Invoke the constraint environment first. The default environment should not exist yet 166 | result = pip_compile.invoke_environment(environment=constraint_env) 167 | if result.exit_code != 0: 168 | raise result.exception 169 | assert not pip_compile.virtualenv_exists(environment=environment) 170 | assert pip_compile.is_installed(environment=constraint_env, package="pytest") 171 | assert "requests" not in environment.piptools_lock_file.read_text() 172 | assert not pip_compile.is_installed(environment=constraint_env, package="requests") 173 | # Now add the `requests` package to the default environment, and invoke the constraint env 174 | # The `requests` package should be installed in both environments and both lockfiles updated 175 | pip_compile.toml_doc["project"]["dependencies"] += ["requests"] 176 | pip_compile.update_pyproject() 177 | pip_compile.invoke_environment(environment=constraint_env) 178 | assert "requests" in environment.piptools_lock_file.read_text() 179 | assert "requests" in constraint_env.piptools_lock_file.read_text() 180 | assert pip_compile.is_installed(environment=environment, package="requests") 181 | assert pip_compile.is_installed(environment=constraint_env, package="requests") 182 | -------------------------------------------------------------------------------- /tests/test_lock.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing the `lock` module 3 | """ 4 | from textwrap import dedent 5 | 6 | from packaging.requirements import Requirement 7 | from packaging.version import Version 8 | 9 | from tests.conftest import PipCompileFixture 10 | 11 | 12 | def test_lockfile_python_version( 13 | pip_compile: PipCompileFixture, 14 | ): 15 | """ 16 | Test the expected Python version from the lockfiles 17 | """ 18 | assert pip_compile.default_environment.piptools_lock.lock_file_version == Version("3.11") 19 | assert pip_compile.test_environment.piptools_lock.lock_file_version == Version("3.11") 20 | 21 | 22 | def test_lockfile_dependencies( 23 | pip_compile: PipCompileFixture, 24 | ): 25 | """ 26 | Test the expected dependencies from reading the lockfiles 27 | """ 28 | assert set(pip_compile.default_environment.piptools_lock.read_header_requirements()) == { 29 | Requirement("hatch") 30 | } 31 | assert set(pip_compile.test_environment.piptools_lock.read_header_requirements()) == { 32 | Requirement("pytest"), 33 | Requirement("pytest-cov"), 34 | Requirement("hatch"), 35 | } 36 | 37 | 38 | def test_compare_requirements_match(pip_compile: PipCompileFixture): 39 | """ 40 | Test the `compare_requirements` method with a match 41 | """ 42 | default_check = pip_compile.default_environment.piptools_lock.compare_requirements( 43 | requirements=[Requirement("hatch")], 44 | ) 45 | assert default_check is True 46 | 47 | 48 | def test_compare_requirements_mismatch(pip_compile: PipCompileFixture): 49 | """ 50 | Test the `compare_requirements` method with a mismatch 51 | """ 52 | test_check = pip_compile.test_environment.piptools_lock.compare_requirements( 53 | requirements=[Requirement("hatch")], 54 | ) 55 | assert test_check is False 56 | 57 | 58 | def test_read_lock_requirements(pip_compile: PipCompileFixture) -> None: 59 | """ 60 | Test the `read_lock_requirements` method 61 | """ 62 | linting_env = pip_compile.reload_environment("lint") 63 | requirements = linting_env.piptools_lock.read_lock_requirements() 64 | string_requirements = set(map(str, requirements)) 65 | assert string_requirements == { 66 | "mypy==1.7.1", 67 | "mypy-extensions==1.0.0", 68 | "ruff==0.1.6", 69 | "typing-extensions==4.8.0", 70 | } 71 | 72 | 73 | def test_replace_temporary_lockfile_windows(pip_compile: PipCompileFixture) -> None: 74 | """ 75 | Regex Replace Temporary File Path: Windows 76 | """ 77 | lock_raw = r""" 78 | httpx==0.22.0 79 | # via -r C:\Users\xxx\AppData\Local\Temp\tmp_kn984om\default.in 80 | """ 81 | lock_body = dedent(lock_raw).strip() 82 | cleaned_text = pip_compile.default_environment.piptools_lock.replace_temporary_lockfile( 83 | lock_body 84 | ) 85 | expected_raw = r""" 86 | httpx==0.22.0 87 | # via hatch.envs.default 88 | """ 89 | assert cleaned_text == dedent(expected_raw).strip() 90 | 91 | 92 | def test_replace_temporary_lockfile_unix(pip_compile: PipCompileFixture) -> None: 93 | """ 94 | Regex Replace Temporary File Path: Unix 95 | """ 96 | lock_raw = r""" 97 | httpx==0.22.0 98 | # via -r /tmp/tmp_kn984om/default.in 99 | """ 100 | lock_body = dedent(lock_raw).strip() 101 | cleaned_text = pip_compile.default_environment.piptools_lock.replace_temporary_lockfile( 102 | lock_body 103 | ) 104 | expected_raw = r""" 105 | httpx==0.22.0 106 | # via hatch.envs.default 107 | """ 108 | assert cleaned_text == dedent(expected_raw).strip() 109 | -------------------------------------------------------------------------------- /tests/test_plugin.py: -------------------------------------------------------------------------------- 1 | """ 2 | Plugin tests. 3 | """ 4 | 5 | from unittest.mock import Mock 6 | 7 | import pytest 8 | 9 | from hatch_pip_compile.exceptions import HatchPipCompileError 10 | from hatch_pip_compile.resolver import PipCompileResolver 11 | from tests.conftest import PipCompileFixture 12 | 13 | 14 | def test_lockfile_path( 15 | pip_compile: PipCompileFixture, 16 | ) -> None: 17 | """ 18 | Test the default lockfile paths 19 | """ 20 | assert ( 21 | pip_compile.default_environment.piptools_lock_file 22 | == pip_compile.isolation / "requirements.txt" 23 | ) 24 | assert ( 25 | pip_compile.test_environment.piptools_lock_file 26 | == pip_compile.isolation / "requirements" / "requirements-test.txt" 27 | ) 28 | 29 | 30 | def test_piptools_constraints_file( 31 | pip_compile: PipCompileFixture, 32 | ) -> None: 33 | """ 34 | Test constraints paths 35 | """ 36 | assert pip_compile.default_environment.piptools_constraints_file is None 37 | assert ( 38 | pip_compile.test_environment.piptools_constraints_file 39 | == pip_compile.default_environment.piptools_lock_file 40 | ) 41 | 42 | 43 | def test_expected_dependencies(pip_compile: PipCompileFixture) -> None: 44 | """ 45 | Test expected dependencies from `PipCompileEnvironment` 46 | """ 47 | assert set(pip_compile.default_environment.dependencies) == {"hatch"} 48 | assert set(pip_compile.test_environment.dependencies) == {"pytest", "pytest-cov", "hatch"} 49 | 50 | 51 | def test_lockfile_up_to_date_missing(pip_compile: PipCompileFixture) -> None: 52 | """ 53 | Test the `lockfile_up_to_date` property when the lockfile is missing 54 | """ 55 | pip_compile.default_environment.piptools_lock_file.unlink() 56 | assert pip_compile.default_environment.lockfile_up_to_date is False 57 | assert pip_compile.default_environment.dependencies_in_sync() is False 58 | 59 | 60 | def test_lockfile_up_to_date_empty(pip_compile: PipCompileFixture) -> None: 61 | """ 62 | Test the `lockfile_up_to_date` property when the lockfile is empty 63 | """ 64 | pip_compile.default_environment.piptools_lock_file.write_text("") 65 | assert pip_compile.default_environment.lockfile_up_to_date is False 66 | assert pip_compile.default_environment.dependencies_in_sync() is False 67 | 68 | 69 | def test_lockfile_up_to_date_mismatch(pip_compile: PipCompileFixture) -> None: 70 | """ 71 | Test the `lockfile_up_to_date` property when the lockfile is mismatched 72 | """ 73 | lock_text = pip_compile.default_environment.piptools_lock_file.read_text() 74 | lock_text = lock_text.replace("# - hatch", "#") 75 | pip_compile.default_environment.piptools_lock_file.write_text(lock_text) 76 | assert pip_compile.default_environment.lockfile_up_to_date is False 77 | 78 | 79 | def test_pip_compile_cli(mock_check_command: Mock, pip_compile: PipCompileFixture) -> None: 80 | """ 81 | Test the `pip_compile_cli` method is called with the expected arguments 82 | """ 83 | pip_compile.default_environment.pip_compile_cli() 84 | expected_call = [ 85 | "python", 86 | "-m", 87 | "piptools", 88 | "compile", 89 | "--quiet", 90 | "--no-header", 91 | "--resolver=backtracking", 92 | "--strip-extras", 93 | "--output-file", 94 | ] 95 | call_args = list(mock_check_command.call_args)[0][0][:-2] 96 | assert call_args == expected_call 97 | 98 | 99 | def test_env_var_disabled(pip_compile: PipCompileFixture, monkeypatch: pytest.MonkeyPatch) -> None: 100 | """ 101 | Test the `lockfile_up_to_date` property when the lockfile is empty 102 | """ 103 | monkeypatch.setenv("PIP_COMPILE_DISABLE", "1") 104 | with pytest.raises(HatchPipCompileError, match="attempted to run a lockfile update"): 105 | pip_compile.default_environment.pip_compile_cli() 106 | 107 | 108 | @pytest.mark.parametrize("environment_name", ["default", "misc", "docs"]) 109 | def test_constraint_env_self(pip_compile: PipCompileFixture, environment_name: str) -> None: 110 | """ 111 | Test the value of the constraint env b/w the default and test environments 112 | """ 113 | environment = pip_compile.reload_environment(environment=environment_name) 114 | assert environment.constraint_env is environment 115 | 116 | 117 | @pytest.mark.parametrize("environment_name", ["test"]) 118 | def test_constraint_env_other(pip_compile: PipCompileFixture, environment_name: str) -> None: 119 | """ 120 | Test the value of the constraint env b/w the default and test environments 121 | """ 122 | environment = pip_compile.reload_environment(environment=environment_name) 123 | assert environment.constraint_env.name == pip_compile.default_environment.name 124 | 125 | 126 | @pytest.mark.parametrize("environment_name", ["default", "docs", "misc"]) 127 | def test_prepare_environment(pip_compile: PipCompileFixture, environment_name: str) -> None: 128 | """ 129 | Test the `prepare_environment` method 130 | """ 131 | environment = pip_compile.reload_environment(environment=environment_name) 132 | environment.prepare_environment() 133 | if environment.dependencies: 134 | assert environment.piptools_lock_file.exists() 135 | else: 136 | assert not environment.piptools_lock_file.exists() 137 | assert environment.dependencies_in_sync() is True 138 | assert environment.lockfile_up_to_date is True 139 | 140 | 141 | def test_resolver_instance_default(pip_compile: PipCompileFixture) -> None: 142 | """ 143 | Test the `pip-compile-resolver` option on default resolver 144 | """ 145 | assert isinstance(pip_compile.default_environment.resolver, PipCompileResolver) 146 | 147 | 148 | def test_resolver_instance_pip_compile(pip_compile: PipCompileFixture) -> None: 149 | """ 150 | Test that the `PipCompileResolver` is used 151 | """ 152 | pip_compile.toml_doc["tool"]["hatch"]["envs"]["default"]["pip-compile-resolver"] = "pip-compile" 153 | pip_compile.update_pyproject() 154 | environment = pip_compile.reload_environment("default") 155 | assert isinstance(environment.resolver, PipCompileResolver) 156 | --------------------------------------------------------------------------------