├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── auto-merge.yml │ ├── build-distributions.yml │ ├── build-hatch.yml │ ├── build-hatchling.yml │ ├── cli.yml │ ├── docs-dev.yml │ ├── docs-release.yml │ └── test.yml ├── .gitignore ├── .linkcheckerrc ├── LICENSE.txt ├── README.md ├── backend ├── LICENSE.txt ├── README.md ├── pyproject.toml ├── src │ └── hatchling │ │ ├── __about__.py │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── bridge │ │ ├── __init__.py │ │ └── app.py │ │ ├── build.py │ │ ├── builders │ │ ├── __init__.py │ │ ├── app.py │ │ ├── binary.py │ │ ├── config.py │ │ ├── constants.py │ │ ├── custom.py │ │ ├── hooks │ │ │ ├── __init__.py │ │ │ ├── custom.py │ │ │ ├── plugin │ │ │ │ ├── __init__.py │ │ │ │ ├── hooks.py │ │ │ │ └── interface.py │ │ │ └── version.py │ │ ├── macos.py │ │ ├── plugin │ │ │ ├── __init__.py │ │ │ ├── hooks.py │ │ │ └── interface.py │ │ ├── sdist.py │ │ ├── utils.py │ │ └── wheel.py │ │ ├── cli │ │ ├── __init__.py │ │ ├── build │ │ │ └── __init__.py │ │ ├── dep │ │ │ ├── __init__.py │ │ │ └── core.py │ │ ├── metadata │ │ │ └── __init__.py │ │ └── version │ │ │ └── __init__.py │ │ ├── dep │ │ ├── __init__.py │ │ └── core.py │ │ ├── licenses │ │ ├── __init__.py │ │ └── supported.py │ │ ├── metadata │ │ ├── __init__.py │ │ ├── core.py │ │ ├── custom.py │ │ ├── plugin │ │ │ ├── __init__.py │ │ │ ├── hooks.py │ │ │ └── interface.py │ │ ├── spec.py │ │ └── utils.py │ │ ├── ouroboros.py │ │ ├── plugin │ │ ├── __init__.py │ │ ├── exceptions.py │ │ ├── manager.py │ │ ├── specs.py │ │ └── utils.py │ │ ├── py.typed │ │ ├── utils │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── context.py │ │ └── fs.py │ │ └── version │ │ ├── __init__.py │ │ ├── core.py │ │ ├── scheme │ │ ├── __init__.py │ │ ├── plugin │ │ │ ├── __init__.py │ │ │ ├── hooks.py │ │ │ └── interface.py │ │ └── standard.py │ │ └── source │ │ ├── __init__.py │ │ ├── code.py │ │ ├── env.py │ │ ├── plugin │ │ ├── __init__.py │ │ ├── hooks.py │ │ └── interface.py │ │ └── regex.py └── tests │ ├── __init__.py │ └── downstream │ ├── datadogpy │ ├── data.json │ └── pyproject.toml │ ├── hatch-showcase │ └── data.json │ ├── integrate.py │ └── requirements.txt ├── docs ├── .hooks │ ├── expand_blocks.py │ ├── inject_version.py │ ├── plugin_register.py │ ├── render_default_test_env.py │ ├── render_ruff_defaults.py │ └── title_from_content.py ├── .overrides │ └── partials │ │ └── copyright.html ├── .snippets │ ├── abbrs.txt │ └── links.txt ├── assets │ ├── css │ │ └── custom.css │ └── images │ │ └── logo.svg ├── blog │ ├── .authors.yml │ ├── index.md │ └── posts │ │ ├── release-hatch-1100.md │ │ ├── release-hatch-1100 │ │ ├── run-script.gif │ │ ├── testing-jinja.gif │ │ └── testing-rich.gif │ │ ├── release-hatch-160.md │ │ ├── release-hatch-160 │ │ └── rich-readme.png │ │ ├── release-hatch-180.md │ │ ├── release-hatch-180 │ │ ├── available-pythons.png │ │ └── install-demo.gif │ │ └── release-hatch-190.md ├── build.md ├── cli │ ├── about.md │ └── reference.md ├── community │ ├── contributing.md │ ├── highlights.md │ └── users.md ├── config │ ├── build.md │ ├── context.md │ ├── dependency.md │ ├── environment │ │ ├── advanced.md │ │ └── overview.md │ ├── hatch.md │ ├── internal │ │ ├── build.md │ │ ├── static-analysis.md │ │ └── testing.md │ ├── metadata.md │ └── project-templates.md ├── environment.md ├── history │ ├── hatch.md │ └── hatchling.md ├── how-to │ ├── config │ │ └── dynamic-metadata.md │ ├── environment │ │ ├── dependency-resolution.md │ │ └── select-installer.md │ ├── integrate │ │ ├── vscode.md │ │ └── vscode │ │ │ ├── run-file.png │ │ │ └── select-interpreter.png │ ├── meta │ │ └── report-issues.md │ ├── plugins │ │ └── testing-builds.md │ ├── publish │ │ ├── auth.md │ │ └── repo.md │ ├── python │ │ └── custom.md │ ├── run │ │ └── python-scripts.md │ └── static-analysis │ │ └── behavior.md ├── index.md ├── install.md ├── intro.md ├── meta │ ├── authors.md │ └── faq.md ├── next-steps.md ├── plugins │ ├── about.md │ ├── build-hook │ │ ├── custom.md │ │ ├── reference.md │ │ └── version.md │ ├── builder │ │ ├── binary.md │ │ ├── custom.md │ │ ├── reference.md │ │ ├── sdist.md │ │ └── wheel.md │ ├── environment-collector │ │ ├── custom.md │ │ ├── default.md │ │ └── reference.md │ ├── environment │ │ ├── reference.md │ │ └── virtual.md │ ├── metadata-hook │ │ ├── custom.md │ │ └── reference.md │ ├── publisher │ │ ├── package-index.md │ │ └── reference.md │ ├── utilities.md │ ├── version-scheme │ │ ├── reference.md │ │ └── standard.md │ └── version-source │ │ ├── code.md │ │ ├── env.md │ │ ├── reference.md │ │ └── regex.md ├── publish.md ├── tutorials │ ├── environment │ │ └── basic-usage.md │ ├── python │ │ └── manage.md │ └── testing │ │ └── overview.md ├── version.md └── why.md ├── hatch.toml ├── mkdocs.insiders.yml ├── mkdocs.yml ├── pyoxidizer.bzl ├── pyproject.toml ├── release ├── README.md ├── macos │ ├── build_pkg.py │ └── pkg │ │ ├── distribution.xml │ │ └── resources │ │ └── icon.png ├── unix │ └── make_scripts_portable.py └── windows │ └── make_scripts_portable.py ├── ruff.toml ├── ruff_defaults.toml ├── scripts ├── bump.py ├── generate_coverage_summary.py ├── install_mkdocs_material_insiders.py ├── release_github.py ├── set_release_version.py ├── update_distributions.py ├── update_ruff.py ├── utils.py ├── validate_history.py └── write_coverage_summary_report.py ├── src └── hatch │ ├── __init__.py │ ├── __main__.py │ ├── cli │ ├── __init__.py │ ├── application.py │ ├── build │ │ └── __init__.py │ ├── clean │ │ └── __init__.py │ ├── config │ │ └── __init__.py │ ├── dep │ │ └── __init__.py │ ├── env │ │ ├── __init__.py │ │ ├── create.py │ │ ├── find.py │ │ ├── prune.py │ │ ├── remove.py │ │ ├── run.py │ │ └── show.py │ ├── fmt │ │ ├── __init__.py │ │ └── core.py │ ├── new │ │ ├── __init__.py │ │ └── migrate.py │ ├── project │ │ ├── __init__.py │ │ └── metadata.py │ ├── publish │ │ └── __init__.py │ ├── python │ │ ├── __init__.py │ │ ├── find.py │ │ ├── install.py │ │ ├── remove.py │ │ ├── show.py │ │ └── update.py │ ├── run │ │ └── __init__.py │ ├── self │ │ ├── __init__.py │ │ ├── report.py │ │ ├── restore.py │ │ └── update.py │ ├── shell │ │ └── __init__.py │ ├── status │ │ └── __init__.py │ ├── terminal.py │ ├── test │ │ ├── __init__.py │ │ └── core.py │ └── version │ │ └── __init__.py │ ├── config │ ├── __init__.py │ ├── constants.py │ ├── model.py │ ├── user.py │ └── utils.py │ ├── dep │ ├── __init__.py │ └── sync.py │ ├── env │ ├── __init__.py │ ├── collectors │ │ ├── __init__.py │ │ ├── custom.py │ │ ├── default.py │ │ └── plugin │ │ │ ├── __init__.py │ │ │ ├── hooks.py │ │ │ └── interface.py │ ├── context.py │ ├── internal │ │ ├── __init__.py │ │ ├── build.py │ │ ├── static_analysis.py │ │ ├── test.py │ │ └── uv.py │ ├── plugin │ │ ├── __init__.py │ │ ├── hooks.py │ │ └── interface.py │ ├── system.py │ ├── utils.py │ └── virtual.py │ ├── errors │ └── __init__.py │ ├── index │ ├── __init__.py │ ├── core.py │ ├── errors.py │ └── publish.py │ ├── plugin │ ├── __init__.py │ ├── constants.py │ ├── manager.py │ ├── specs.py │ └── utils.py │ ├── project │ ├── __init__.py │ ├── config.py │ ├── constants.py │ ├── core.py │ ├── env.py │ ├── frontend │ │ ├── __init__.py │ │ ├── core.py │ │ └── scripts │ │ │ ├── __init__.py │ │ │ ├── build_deps.py │ │ │ ├── core_metadata.py │ │ │ └── standard.py │ └── utils.py │ ├── publish │ ├── __init__.py │ ├── auth.py │ ├── index.py │ └── plugin │ │ ├── __init__.py │ │ ├── hooks.py │ │ └── interface.py │ ├── py.typed │ ├── python │ ├── __init__.py │ ├── core.py │ ├── distributions.py │ └── resolve.py │ ├── template │ ├── __init__.py │ ├── default.py │ ├── files_default.py │ ├── files_feature_ci.py │ ├── files_feature_cli.py │ ├── files_feature_tests.py │ └── plugin │ │ ├── __init__.py │ │ ├── hooks.py │ │ └── interface.py │ ├── utils │ ├── __init__.py │ ├── ci.py │ ├── dep.py │ ├── env.py │ ├── fs.py │ ├── network.py │ ├── platform.py │ ├── runner.py │ ├── shells.py │ ├── structures.py │ └── toml.py │ └── venv │ ├── __init__.py │ ├── core.py │ └── utils.py └── tests ├── __init__.py ├── backend ├── __init__.py ├── builders │ ├── __init__.py │ ├── hooks │ │ ├── __init__.py │ │ ├── test_custom.py │ │ └── test_version.py │ ├── plugin │ │ ├── __init__.py │ │ └── test_interface.py │ ├── test_binary.py │ ├── test_config.py │ ├── test_custom.py │ ├── test_sdist.py │ ├── test_wheel.py │ └── utils.py ├── metadata │ ├── __init__.py │ ├── test_build.py │ ├── test_core.py │ ├── test_custom_hook.py │ ├── test_hatch.py │ └── test_spec.py ├── test_build.py ├── utils │ ├── __init__.py │ ├── test_context.py │ ├── test_fs.py │ └── test_macos.py └── version │ ├── __init__.py │ ├── scheme │ ├── __init__.py │ └── test_standard.py │ └── source │ ├── __init__.py │ ├── test_code.py │ ├── test_env.py │ └── test_regex.py ├── cli ├── __init__.py ├── build │ ├── __init__.py │ └── test_build.py ├── clean │ ├── __init__.py │ └── test_clean.py ├── config │ ├── __init__.py │ ├── test_explore.py │ ├── test_find.py │ ├── test_restore.py │ ├── test_set.py │ └── test_show.py ├── dep │ ├── __init__.py │ ├── show │ │ ├── __init__.py │ │ ├── test_requirements.py │ │ └── test_table.py │ └── test_hash.py ├── env │ ├── __init__.py │ ├── test_create.py │ ├── test_find.py │ ├── test_prune.py │ ├── test_remove.py │ ├── test_run.py │ └── test_show.py ├── fmt │ ├── __init__.py │ └── test_fmt.py ├── new │ ├── __init__.py │ └── test_new.py ├── project │ ├── __init__.py │ └── test_metadata.py ├── publish │ ├── __init__.py │ └── test_publish.py ├── python │ ├── __init__.py │ ├── conftest.py │ ├── test_find.py │ ├── test_install.py │ ├── test_remove.py │ ├── test_show.py │ └── test_update.py ├── run │ ├── __init__.py │ └── test_run.py ├── self │ ├── __init__.py │ ├── test_report.py │ └── test_self.py ├── status │ ├── __init__.py │ └── test_status.py ├── test │ ├── __init__.py │ └── test_test.py ├── test_root.py └── version │ ├── __init__.py │ └── test_version.py ├── config ├── __init__.py └── test_model.py ├── conftest.py ├── dep ├── __init__.py └── test_sync.py ├── env ├── __init__.py ├── collectors │ ├── __init__.py │ └── test_custom.py └── plugin │ ├── __init__.py │ └── test_interface.py ├── helpers ├── __init__.py ├── helpers.py └── templates │ ├── __init__.py │ ├── licenses │ └── __init__.py │ ├── new │ ├── __init__.py │ ├── basic.py │ ├── default.py │ ├── feature_ci.py │ ├── feature_cli.py │ ├── feature_no_src_layout.py │ ├── licenses_empty.py │ ├── licenses_multiple.py │ ├── projects_urls_empty.py │ └── projects_urls_space_in_label.py │ ├── sdist │ ├── __init__.py │ ├── standard_default.py │ ├── standard_default_build_script_artifacts.py │ ├── standard_default_build_script_extra_dependencies.py │ ├── standard_default_support_legacy.py │ ├── standard_default_vcs_git_exclusion_files.py │ ├── standard_default_vcs_mercurial_exclusion_files.py │ ├── standard_include.py │ └── standard_include_config_file.py │ └── wheel │ ├── __init__.py │ ├── standard_default_build_script.py │ ├── standard_default_build_script_artifacts.py │ ├── standard_default_build_script_artifacts_with_src_layout.py │ ├── standard_default_build_script_configured_build_hooks.py │ ├── standard_default_build_script_extra_dependencies.py │ ├── standard_default_build_script_force_include.py │ ├── standard_default_build_script_force_include_no_duplication.py │ ├── standard_default_extra_metadata.py │ ├── standard_default_license_multiple.py │ ├── standard_default_license_single.py │ ├── standard_default_namespace_package.py │ ├── standard_default_python_constraint.py │ ├── standard_default_python_constraint_three_components.py │ ├── standard_default_shared_data.py │ ├── standard_default_shared_scripts.py │ ├── standard_default_single_module.py │ ├── standard_default_symlink.py │ ├── standard_editable_exact.py │ ├── standard_editable_exact_extra_dependencies.py │ ├── standard_editable_exact_force_include.py │ ├── standard_editable_pth.py │ ├── standard_editable_pth_extra_dependencies.py │ ├── standard_editable_pth_force_include.py │ ├── standard_entry_points.py │ ├── standard_no_strict_naming.py │ ├── standard_only_packages_artifact_override.py │ ├── standard_tests.py │ └── utils.py ├── index ├── __init__.py ├── server │ ├── devpi │ │ ├── Dockerfile │ │ └── entrypoint.sh │ ├── docker-compose.yaml │ └── nginx │ │ └── nginx.conf └── test_core.py ├── project ├── __init__.py ├── test_config.py ├── test_core.py ├── test_frontend.py └── test_utils.py ├── publish ├── __init__.py └── plugin │ ├── __init__.py │ └── test_interface.py ├── python ├── __init__.py ├── test_core.py └── test_resolve.py ├── utils ├── __init__.py ├── test_auth.py ├── test_fs.py ├── test_platform.py ├── test_runner.py └── test_structures.py └── venv ├── __init__.py ├── test_core.py └── test_utils.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 2 | - ofek 3 | custom: 4 | - https://ofek.dev/donate/ 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: auto-merge 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - reopened 8 | - synchronize 9 | branches: 10 | - master 11 | 12 | jobs: 13 | dependabot: 14 | runs-on: ubuntu-latest 15 | if: ${{ github.actor == 'dependabot[bot]' }} 16 | 17 | steps: 18 | - name: Wait for tests to succeed 19 | uses: lewagon/wait-on-check-action@v1.3.4 20 | with: 21 | ref: ${{ github.ref }} 22 | check-name: check 23 | wait-interval: 10 24 | repo-token: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: Enable auto-merge for Dependabot PRs 27 | run: gh pr merge --auto --squash ${{ github.event.pull_request.html_url }} 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/build-hatchling.yml: -------------------------------------------------------------------------------- 1 | name: build hatchling 2 | 3 | on: 4 | push: 5 | tags: 6 | - hatchling-v* 7 | 8 | env: 9 | PYTHON_VERSION: "3.12" 10 | 11 | jobs: 12 | build: 13 | name: Build wheels and source distribution 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Python ${{ env.PYTHON_VERSION }} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{ env.PYTHON_VERSION }} 23 | 24 | - name: Install UV 25 | uses: astral-sh/setup-uv@v3 26 | 27 | - name: Install build dependencies 28 | run: uv pip install --system --upgrade build 29 | 30 | - name: Build source distribution 31 | run: python -m build backend 32 | 33 | - uses: actions/upload-artifact@v4 34 | with: 35 | name: artifacts 36 | path: backend/dist 37 | if-no-files-found: error 38 | 39 | publish: 40 | name: Publish release 41 | needs: 42 | - build 43 | runs-on: ubuntu-latest 44 | 45 | permissions: 46 | id-token: write 47 | 48 | steps: 49 | - uses: actions/download-artifact@v4 50 | with: 51 | name: artifacts 52 | path: dist 53 | 54 | - name: Push build artifacts to PyPI 55 | uses: pypa/gh-action-pypi-publish@v1.12.3 56 | with: 57 | skip-existing: true 58 | -------------------------------------------------------------------------------- /.github/workflows/cli.yml: -------------------------------------------------------------------------------- 1 | name: CLI experience 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} 13 | cancel-in-progress: true 14 | 15 | env: 16 | STABLE_PYTHON_VERSION: '3.12' 17 | HYPERFINE_VERSION: '1.18.0' 18 | 19 | jobs: 20 | response-time: 21 | name: CLI responsiveness with latest Python 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Set up Python ${{ env.STABLE_PYTHON_VERSION }} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ env.STABLE_PYTHON_VERSION }} 31 | 32 | - name: Install UV 33 | uses: astral-sh/setup-uv@v3 34 | 35 | - name: Install hyperfine 36 | uses: taiki-e/install-action@v2 37 | with: 38 | tool: hyperfine@${{ env.HYPERFINE_VERSION }} 39 | 40 | - name: Install other tools 41 | run: uv pip install --system --upgrade flit poetry pipenv 42 | 43 | - name: Install ourself 44 | run: | 45 | uv pip install --system . 46 | uv pip install --system ./backend 47 | 48 | - name: Benchmark 49 | run: | 50 | hyperfine -m 100 --warmup 10 -i pipenv 51 | hyperfine -m 100 --warmup 10 poetry 52 | hyperfine -m 100 --warmup 10 -i flit 53 | hyperfine -m 100 --warmup 10 hatch 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Global directories 2 | __pycache__/ 3 | 4 | # Global files 5 | *.py[cod] 6 | *.dll 7 | *.so 8 | *.log 9 | *.swp 10 | 11 | # Root directories 12 | /.benchmarks/ 13 | /.cache/ 14 | /.env/ 15 | /.idea/ 16 | /.mypy_cache/ 17 | /.pytest_cache/ 18 | /.ruff_cache/ 19 | /.vscode/ 20 | /backend/dist/ 21 | /dist/ 22 | /site/ 23 | 24 | # Root files 25 | /.coverage* 26 | 27 | # Auto-generated during builds 28 | /src/hatch/_version.py 29 | -------------------------------------------------------------------------------- /.linkcheckerrc: -------------------------------------------------------------------------------- 1 | # https://linkchecker.github.io/linkchecker/man/linkcheckerrc.html 2 | [filtering] 3 | ignore= 4 | https://docs.astral.sh/ruff/rules/.+ 5 | https://github.com/pypa/hatch/releases/tag/hatch-v.+ 6 | 7 | [AnchorCheck] 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present Ofek Lev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /backend/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-present Ofek Lev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /backend/src/hatchling/__about__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.27.0' 2 | -------------------------------------------------------------------------------- /backend/src/hatchling/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if __name__ == '__main__': 4 | from hatchling.cli import hatchling 5 | 6 | sys.exit(hatchling()) 7 | -------------------------------------------------------------------------------- /backend/src/hatchling/bridge/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/bridge/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/builders/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/builders/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/builders/app.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | 5 | from hatchling.builders.binary import BinaryBuilder 6 | 7 | 8 | class AppBuilder(BinaryBuilder): 9 | PLUGIN_NAME = 'app' 10 | 11 | def build_bootstrap( 12 | self, 13 | directory: str, 14 | **build_data: Any, 15 | ) -> str: 16 | self.app.display_warning( 17 | 'The `app` build target is deprecated and will be removed in a future release. ' 18 | 'Use the `binary` build target instead.' 19 | ) 20 | return super().build_bootstrap(directory, **build_data) 21 | -------------------------------------------------------------------------------- /backend/src/hatchling/builders/constants.py: -------------------------------------------------------------------------------- 1 | DEFAULT_BUILD_DIRECTORY = 'dist' 2 | 3 | EXCLUDED_DIRECTORIES = frozenset(( 4 | # Python bytecode 5 | '__pycache__', 6 | # Single virtual environment 7 | '.venv', 8 | # Git 9 | '.git', 10 | # Mercurial 11 | '.hg', 12 | # Hatch 13 | '.hatch', 14 | # tox 15 | '.tox', 16 | # nox 17 | '.nox', 18 | # Ruff 19 | '.ruff_cache', 20 | # pytest 21 | '.pytest_cache', 22 | # Mypy 23 | '.mypy_cache', 24 | # pixi 25 | '.pixi', 26 | )) 27 | EXCLUDED_FILES = frozenset(( 28 | # https://en.wikipedia.org/wiki/.DS_Store 29 | '.DS_Store', 30 | )) 31 | 32 | 33 | class BuildEnvVars: 34 | LOCATION = 'HATCH_BUILD_LOCATION' 35 | HOOKS_ONLY = 'HATCH_BUILD_HOOKS_ONLY' 36 | NO_HOOKS = 'HATCH_BUILD_NO_HOOKS' 37 | HOOKS_ENABLE = 'HATCH_BUILD_HOOKS_ENABLE' 38 | HOOK_ENABLE_PREFIX = 'HATCH_BUILD_HOOK_ENABLE_' 39 | CLEAN = 'HATCH_BUILD_CLEAN' 40 | CLEAN_HOOKS_AFTER = 'HATCH_BUILD_CLEAN_HOOKS_AFTER' 41 | 42 | 43 | EDITABLES_REQUIREMENT = 'editables~=0.3' 44 | -------------------------------------------------------------------------------- /backend/src/hatchling/builders/hooks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/builders/hooks/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/builders/hooks/custom.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | from typing import Any 5 | 6 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 7 | from hatchling.plugin.utils import load_plugin_from_script 8 | from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT 9 | 10 | 11 | class CustomBuildHook: 12 | PLUGIN_NAME = 'custom' 13 | 14 | def __new__( # type: ignore[misc] 15 | cls, 16 | root: str, 17 | config: dict[str, Any], 18 | *args: Any, 19 | **kwargs: Any, 20 | ) -> BuildHookInterface: 21 | build_script = config.get('path', DEFAULT_BUILD_SCRIPT) 22 | if not isinstance(build_script, str): 23 | message = f'Option `path` for build hook `{cls.PLUGIN_NAME}` must be a string' 24 | raise TypeError(message) 25 | 26 | if not build_script: 27 | message = f'Option `path` for build hook `{cls.PLUGIN_NAME}` must not be empty if defined' 28 | raise ValueError(message) 29 | 30 | path = os.path.normpath(os.path.join(root, build_script)) 31 | if not os.path.isfile(path): 32 | message = f'Build script does not exist: {build_script}' 33 | raise OSError(message) 34 | 35 | hook_class = load_plugin_from_script(path, build_script, BuildHookInterface, 'build_hook') 36 | hook = hook_class(root, config, *args, **kwargs) 37 | 38 | # Always keep the name to avoid confusion 39 | hook.PLUGIN_NAME = cls.PLUGIN_NAME 40 | 41 | return hook 42 | -------------------------------------------------------------------------------- /backend/src/hatchling/builders/hooks/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/builders/hooks/plugin/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/builders/hooks/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing 4 | 5 | from hatchling.builders.hooks.custom import CustomBuildHook 6 | from hatchling.builders.hooks.version import VersionBuildHook 7 | from hatchling.plugin import hookimpl 8 | 9 | if typing.TYPE_CHECKING: 10 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 11 | 12 | 13 | @hookimpl 14 | def hatch_register_build_hook() -> list[type[BuildHookInterface]]: 15 | return [CustomBuildHook, VersionBuildHook] # type: ignore[list-item] 16 | -------------------------------------------------------------------------------- /backend/src/hatchling/builders/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/builders/plugin/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/builders/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing 4 | 5 | from hatchling.builders.app import AppBuilder 6 | from hatchling.builders.binary import BinaryBuilder 7 | from hatchling.builders.custom import CustomBuilder 8 | from hatchling.builders.sdist import SdistBuilder 9 | from hatchling.builders.wheel import WheelBuilder 10 | from hatchling.plugin import hookimpl 11 | 12 | if typing.TYPE_CHECKING: 13 | from hatchling.builders.plugin.interface import BuilderInterface 14 | 15 | 16 | @hookimpl 17 | def hatch_register_builder() -> list[type[BuilderInterface]]: 18 | return [AppBuilder, BinaryBuilder, CustomBuilder, SdistBuilder, WheelBuilder] # type: ignore[list-item] 19 | -------------------------------------------------------------------------------- /backend/src/hatchling/cli/__init__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from hatchling.cli.build import build_command 4 | from hatchling.cli.dep import dep_command 5 | from hatchling.cli.metadata import metadata_command 6 | from hatchling.cli.version import version_command 7 | 8 | 9 | def hatchling() -> int: 10 | parser = argparse.ArgumentParser(prog='hatchling', allow_abbrev=False) 11 | subparsers = parser.add_subparsers() 12 | 13 | defaults = {'metavar': ''} 14 | 15 | build_command(subparsers, defaults) 16 | dep_command(subparsers, defaults) 17 | metadata_command(subparsers, defaults) 18 | version_command(subparsers, defaults) 19 | 20 | kwargs = vars(parser.parse_args()) 21 | try: 22 | command = kwargs.pop('func') 23 | except KeyError: 24 | parser.print_help() 25 | else: 26 | command(**kwargs) 27 | 28 | return 0 29 | -------------------------------------------------------------------------------- /backend/src/hatchling/cli/dep/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | from typing import TYPE_CHECKING, Any 5 | 6 | if TYPE_CHECKING: 7 | import argparse 8 | 9 | 10 | def synced_impl(*, dependencies: list[str], python: str) -> None: 11 | import subprocess 12 | from ast import literal_eval 13 | 14 | from packaging.requirements import Requirement 15 | 16 | from hatchling.cli.dep.core import dependencies_in_sync 17 | 18 | sys_path = None 19 | if python: 20 | output = subprocess.check_output([python, '-c', 'import sys;print([path for path in sys.path if path])']) 21 | sys_path = literal_eval(output.strip().decode('utf-8')) 22 | 23 | sys.exit(0 if dependencies_in_sync(list(map(Requirement, dependencies)), sys_path) else 1) 24 | 25 | 26 | def synced_command(subparsers: argparse._SubParsersAction, defaults: Any) -> None: 27 | parser = subparsers.add_parser('synced') 28 | parser.add_argument('dependencies', nargs='+') 29 | parser.add_argument('-p', '--python', dest='python', **defaults) 30 | parser.set_defaults(func=synced_impl) 31 | 32 | 33 | def dep_command(subparsers: argparse._SubParsersAction, defaults: Any) -> None: 34 | parser = subparsers.add_parser('dep') 35 | subparsers = parser.add_subparsers() 36 | 37 | synced_command(subparsers, defaults) 38 | -------------------------------------------------------------------------------- /backend/src/hatchling/dep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/dep/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/dep/core.py: -------------------------------------------------------------------------------- 1 | from hatchling.cli.dep.core import dependencies_in_sync # noqa: F401 2 | -------------------------------------------------------------------------------- /backend/src/hatchling/licenses/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/licenses/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/licenses/supported.py: -------------------------------------------------------------------------------- 1 | from packaging.licenses._spdx import VERSION # noqa: F401, PLC2701 2 | -------------------------------------------------------------------------------- /backend/src/hatchling/metadata/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/metadata/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/metadata/custom.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | from typing import Any 5 | 6 | from hatchling.metadata.plugin.interface import MetadataHookInterface 7 | from hatchling.plugin.utils import load_plugin_from_script 8 | from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT 9 | 10 | 11 | class CustomMetadataHook: 12 | PLUGIN_NAME = 'custom' 13 | 14 | def __new__( # type: ignore[misc] 15 | cls, 16 | root: str, 17 | config: dict[str, Any], 18 | *args: Any, 19 | **kwargs: Any, 20 | ) -> MetadataHookInterface: 21 | build_script = config.get('path', DEFAULT_BUILD_SCRIPT) 22 | if not isinstance(build_script, str): 23 | message = f'Option `path` for metadata hook `{cls.PLUGIN_NAME}` must be a string' 24 | raise TypeError(message) 25 | 26 | if not build_script: 27 | message = f'Option `path` for metadata hook `{cls.PLUGIN_NAME}` must not be empty if defined' 28 | raise ValueError(message) 29 | 30 | path = os.path.normpath(os.path.join(root, build_script)) 31 | if not os.path.isfile(path): 32 | message = f'Build script does not exist: {build_script}' 33 | raise OSError(message) 34 | 35 | hook_class = load_plugin_from_script(path, build_script, MetadataHookInterface, 'metadata_hook') # type: ignore[type-abstract] 36 | hook = hook_class(root, config, *args, **kwargs) 37 | 38 | # Always keep the name to avoid confusion 39 | hook.PLUGIN_NAME = cls.PLUGIN_NAME 40 | 41 | return hook 42 | -------------------------------------------------------------------------------- /backend/src/hatchling/metadata/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/metadata/plugin/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/metadata/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from hatchling.metadata.custom import CustomMetadataHook 6 | from hatchling.plugin import hookimpl 7 | 8 | if TYPE_CHECKING: 9 | from hatchling.metadata.plugin.interface import MetadataHookInterface 10 | 11 | 12 | @hookimpl 13 | def hatch_register_metadata_hook() -> type[MetadataHookInterface]: 14 | return CustomMetadataHook # type: ignore[return-value] 15 | -------------------------------------------------------------------------------- /backend/src/hatchling/ouroboros.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import re 5 | from ast import literal_eval 6 | from typing import Any 7 | 8 | from hatchling.build import * # noqa: F403 9 | 10 | 11 | def read_dependencies() -> list[str]: 12 | pattern = r'^dependencies = (\[.*?\])$' 13 | 14 | with open(os.path.join(os.getcwd(), 'pyproject.toml'), encoding='utf-8') as f: 15 | # Windows \r\n prevents match 16 | contents = '\n'.join(line.rstrip() for line in f) 17 | 18 | match = re.search(pattern, contents, flags=re.MULTILINE | re.DOTALL) 19 | if match is None: 20 | message = 'No dependencies found' 21 | raise ValueError(message) 22 | 23 | return literal_eval(match.group(1)) 24 | 25 | 26 | def get_requires_for_build_sdist( # type: ignore[no-redef] 27 | config_settings: dict[str, Any] | None = None, # noqa: ARG001 28 | ) -> list[str]: 29 | """ 30 | https://peps.python.org/pep-0517/#get-requires-for-build-sdist 31 | """ 32 | return read_dependencies() 33 | 34 | 35 | def get_requires_for_build_wheel( # type: ignore[no-redef] 36 | config_settings: dict[str, Any] | None = None, # noqa: ARG001 37 | ) -> list[str]: 38 | """ 39 | https://peps.python.org/pep-0517/#get-requires-for-build-wheel 40 | """ 41 | return read_dependencies() 42 | 43 | 44 | def get_requires_for_build_editable( # type: ignore[no-redef] 45 | config_settings: dict[str, Any] | None = None, # noqa: ARG001 46 | ) -> list[str]: 47 | """ 48 | https://peps.python.org/pep-0660/#get-requires-for-build-editable 49 | """ 50 | from hatchling.builders.constants import EDITABLES_REQUIREMENT 51 | 52 | return [*read_dependencies(), EDITABLES_REQUIREMENT] 53 | -------------------------------------------------------------------------------- /backend/src/hatchling/plugin/__init__.py: -------------------------------------------------------------------------------- 1 | import pluggy 2 | 3 | hookimpl = pluggy.HookimplMarker('hatch') 4 | -------------------------------------------------------------------------------- /backend/src/hatchling/plugin/exceptions.py: -------------------------------------------------------------------------------- 1 | class UnknownPluginError(ValueError): 2 | pass 3 | -------------------------------------------------------------------------------- /backend/src/hatchling/plugin/specs.py: -------------------------------------------------------------------------------- 1 | import pluggy 2 | 3 | hookspec = pluggy.HookspecMarker('hatch') 4 | 5 | 6 | @hookspec 7 | def hatch_register_version_source() -> None: 8 | """Register new classes that adhere to the version source interface.""" 9 | 10 | 11 | @hookspec 12 | def hatch_register_builder() -> None: 13 | """Register new classes that adhere to the builder interface.""" 14 | 15 | 16 | @hookspec 17 | def hatch_register_build_hook() -> None: 18 | """Register new classes that adhere to the build hook interface.""" 19 | 20 | 21 | @hookspec 22 | def hatch_register_metadata_hook() -> None: 23 | """Register new classes that adhere to the metadata hook interface.""" 24 | -------------------------------------------------------------------------------- /backend/src/hatchling/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/py.typed -------------------------------------------------------------------------------- /backend/src/hatchling/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/utils/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/utils/constants.py: -------------------------------------------------------------------------------- 1 | DEFAULT_BUILD_SCRIPT = 'hatch_build.py' 2 | DEFAULT_CONFIG_FILE = 'hatch.toml' 3 | 4 | 5 | class VersionEnvVars: 6 | VALIDATE_BUMP = 'HATCH_VERSION_VALIDATE_BUMP' 7 | -------------------------------------------------------------------------------- /backend/src/hatchling/utils/fs.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | 5 | 6 | def locate_file(root: str, file_name: str, *, boundary: str | None = None) -> str | None: 7 | while True: 8 | file_path = os.path.join(root, file_name) 9 | if os.path.isfile(file_path): 10 | return file_path 11 | 12 | if boundary is not None and os.path.exists(os.path.join(root, boundary)): 13 | return None 14 | 15 | new_root = os.path.dirname(root) 16 | if new_root == root: 17 | return None 18 | 19 | root = new_root 20 | 21 | 22 | def path_to_uri(path: str) -> str: 23 | if os.sep == '/': 24 | return f'file://{os.path.abspath(path).replace(" ", "%20")}' 25 | 26 | return f'file:///{os.path.abspath(path).replace(" ", "%20").replace(os.sep, "/")}' 27 | -------------------------------------------------------------------------------- /backend/src/hatchling/version/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/version/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/version/scheme/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/version/scheme/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/version/scheme/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/version/scheme/plugin/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/version/scheme/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from hatchling.plugin import hookimpl 6 | from hatchling.version.scheme.standard import StandardScheme 7 | 8 | if TYPE_CHECKING: 9 | from hatchling.version.scheme.plugin.interface import VersionSchemeInterface 10 | 11 | 12 | @hookimpl 13 | def hatch_register_version_scheme() -> type[VersionSchemeInterface]: 14 | return StandardScheme 15 | -------------------------------------------------------------------------------- /backend/src/hatchling/version/source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/version/source/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/version/source/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | 5 | from hatchling.version.source.plugin.interface import VersionSourceInterface 6 | 7 | 8 | class EnvSource(VersionSourceInterface): 9 | PLUGIN_NAME = 'env' 10 | 11 | def get_version_data(self) -> dict: 12 | variable = self.config.get('variable', '') 13 | if not variable: 14 | message = 'option `variable` must be specified' 15 | raise ValueError(message) 16 | 17 | if not isinstance(variable, str): 18 | message = 'option `variable` must be a string' 19 | raise TypeError(message) 20 | 21 | if variable not in os.environ: 22 | message = f'environment variable `{variable}` is not set' 23 | raise RuntimeError(message) 24 | 25 | return {'version': os.environ[variable]} 26 | 27 | def set_version(self, version: str, version_data: dict) -> None: # noqa: ARG002, PLR6301 28 | message = 'Cannot set environment variables' 29 | raise NotImplementedError(message) 30 | -------------------------------------------------------------------------------- /backend/src/hatchling/version/source/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/src/hatchling/version/source/plugin/__init__.py -------------------------------------------------------------------------------- /backend/src/hatchling/version/source/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from hatchling.plugin import hookimpl 6 | from hatchling.version.source.code import CodeSource 7 | from hatchling.version.source.env import EnvSource 8 | from hatchling.version.source.regex import RegexSource 9 | 10 | if TYPE_CHECKING: 11 | from hatchling.version.source.plugin.interface import VersionSourceInterface 12 | 13 | 14 | @hookimpl 15 | def hatch_register_version_source() -> list[type[VersionSourceInterface]]: 16 | return [CodeSource, EnvSource, RegexSource] 17 | -------------------------------------------------------------------------------- /backend/src/hatchling/version/source/regex.py: -------------------------------------------------------------------------------- 1 | from hatchling.version.core import VersionFile 2 | from hatchling.version.source.plugin.interface import VersionSourceInterface 3 | 4 | 5 | class RegexSource(VersionSourceInterface): 6 | PLUGIN_NAME = 'regex' 7 | 8 | def get_version_data(self) -> dict: 9 | relative_path = self.config.get('path', '') 10 | if not relative_path: 11 | message = 'option `path` must be specified' 12 | raise ValueError(message) 13 | 14 | if not isinstance(relative_path, str): 15 | message = 'option `path` must be a string' 16 | raise TypeError(message) 17 | 18 | pattern = self.config.get('pattern', '') 19 | if not isinstance(pattern, str): 20 | message = 'option `pattern` must be a string' 21 | raise TypeError(message) 22 | 23 | version_file = VersionFile(self.root, relative_path) 24 | version = version_file.read(pattern=pattern) 25 | 26 | return {'version': version, 'version_file': version_file} 27 | 28 | def set_version(self, version: str, version_data: dict) -> None: # noqa: PLR6301 29 | version_data['version_file'].set_version(version) 30 | -------------------------------------------------------------------------------- /backend/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/backend/tests/__init__.py -------------------------------------------------------------------------------- /backend/tests/downstream/datadogpy/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "repo_url": "https://github.com/DataDog/datadogpy", 3 | "statements": [ 4 | "from datadog import initialize, api" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /backend/tests/downstream/datadogpy/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "datadog" 7 | description = "The Datadog Python library" 8 | readme = "README.md" 9 | license = "BSD-3-Clause" 10 | keywords = [ 11 | "datadog", 12 | ] 13 | authors = [ 14 | { name = "Datadog, Inc.", email = "dev@datadoghq.com" }, 15 | ] 16 | classifiers = [ 17 | "Operating System :: OS Independent", 18 | "Programming Language :: Python :: 2.7", 19 | "Programming Language :: Python :: 3.4", 20 | "Programming Language :: Python :: 3.5", 21 | "Programming Language :: Python :: 3.6", 22 | "Programming Language :: Python :: 3.7", 23 | "Programming Language :: Python :: 3.8", 24 | "Programming Language :: Python :: 3.9", 25 | 'Programming Language :: Python :: Implementation :: CPython', 26 | "Programming Language :: Python :: Implementation :: PyPy", 27 | ] 28 | dependencies = [ 29 | "requests>=2.6.0", 30 | "typing; python_version<'3.5'", 31 | "configparser<5; python_version<'3.0'", 32 | ] 33 | dynamic = ["version"] 34 | 35 | [project.urls] 36 | "Bug Tracker" = "https://github.com/DataDog/datadogpy/issues" 37 | Documentation = "https://datadogpy.readthedocs.io/en/latest/" 38 | "Source Code" = "https://github.com/DataDog/datadogpy" 39 | 40 | [project.scripts] 41 | dog = "datadog.dogshell:main" 42 | dogwrap = "datadog.dogshell.wrap:main" 43 | dogshell = "datadog.dogshell:main" 44 | dogshellwrap = "datadog.dogshell.wrap:main" 45 | 46 | [tool.hatch.version] 47 | path = "datadog/version.py" 48 | 49 | [tool.hatch.build] 50 | packages = ["datadog"] 51 | 52 | [tool.hatch.build.targets.sdist] 53 | include = [ 54 | "/LICENSE", 55 | "/tests", 56 | ] 57 | 58 | [tool.hatch.build.targets.wheel] 59 | -------------------------------------------------------------------------------- /backend/tests/downstream/hatch-showcase/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "repo_url": "https://github.com/ofek/hatch-showcase", 3 | "statements": [ 4 | "from hatch_showcase.fib import fibonacci; assert fibonacci(32) == 2178309" 5 | ], 6 | "env_vars": { 7 | "HATCH_BUILD_HOOKS_ENABLE": "true" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /backend/tests/downstream/requirements.txt: -------------------------------------------------------------------------------- 1 | build 2 | packaging 3 | requests 4 | tomli 5 | virtualenv>=20.13.1 6 | -------------------------------------------------------------------------------- /docs/.hooks/expand_blocks.py: -------------------------------------------------------------------------------- 1 | import re 2 | import textwrap 3 | 4 | from markdown.preprocessors import Preprocessor 5 | 6 | _code_tab_regex = re.compile( 7 | r'^( *)((`{3,})[^ ].*) tab="(.+)"\n([\s\S]+?)\n\1\3$', 8 | re.MULTILINE, 9 | ) 10 | _config_example_regex = re.compile( 11 | r'^( *)((`{3,})toml\b.*) config-example\n([\s\S]+?)\n\1\3$', 12 | re.MULTILINE, 13 | ) 14 | 15 | 16 | def _code_tab_replace(m): 17 | indent, fence_start, fence_end, title, content = m.groups() 18 | return f"""\ 19 | {indent}=== ":octicons-file-code-16: {title}" 20 | {indent} {fence_start} 21 | {textwrap.indent(content, ' ')} 22 | {indent} {fence_end} 23 | """ 24 | 25 | 26 | def _config_example_replace(m): 27 | indent, fence_start, fence_end, content = m.groups() 28 | content_without = re.sub(r' *\[tool.hatch\]\n', '', content.replace('[tool.hatch.', '[')) 29 | return f"""\ 30 | {indent}=== ":octicons-file-code-16: pyproject.toml" 31 | {indent} {fence_start} 32 | {textwrap.indent(content, ' ')} 33 | {indent} {fence_end} 34 | 35 | {indent}=== ":octicons-file-code-16: hatch.toml" 36 | {indent} {fence_start} 37 | {textwrap.indent(content_without, ' ')} 38 | {indent} {fence_end} 39 | """ 40 | 41 | 42 | class ExpandedBlocksPreprocessor(Preprocessor): 43 | def run(self, lines): # noqa: PLR6301 44 | markdown = '\n'.join(lines) 45 | markdown = _config_example_regex.sub(_config_example_replace, markdown) 46 | markdown = _code_tab_regex.sub(_code_tab_replace, markdown) 47 | return markdown.splitlines() 48 | -------------------------------------------------------------------------------- /docs/.hooks/inject_version.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | from functools import cache 4 | 5 | from markdown.preprocessors import Preprocessor 6 | 7 | MARKER = '' 8 | SEMVER_PARTS = 3 9 | 10 | 11 | @cache 12 | def get_latest_version(): 13 | env = dict(os.environ) 14 | # Ignore the current documentation environment so that the version 15 | # command can execute as usual in the default build environment 16 | env.pop('HATCH_ENV_ACTIVE', None) 17 | 18 | output = subprocess.check_output(['hatch', '--no-color', 'version'], env=env).decode('utf-8').strip() # noqa: S607 19 | 20 | version = output.replace('dev', '') 21 | parts = list(map(int, version.split('.'))) 22 | major, minor, patch = parts[:SEMVER_PARTS] 23 | if len(parts) > SEMVER_PARTS: 24 | patch -= 1 25 | 26 | return f'{major}.{minor}.{patch}' 27 | 28 | 29 | class VersionInjectionPreprocessor(Preprocessor): 30 | def run(self, lines): # noqa: PLR6301 31 | for i, line in enumerate(lines): 32 | lines[i] = line.replace(MARKER, get_latest_version()) 33 | 34 | return lines 35 | -------------------------------------------------------------------------------- /docs/.hooks/plugin_register.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from markdown.extensions import Extension 5 | 6 | HERE = os.path.dirname(__file__) 7 | 8 | 9 | def on_config( 10 | config, 11 | **kwargs, # noqa: ARG001 12 | ): 13 | config.markdown_extensions.append(GlobalExtension()) 14 | 15 | 16 | class GlobalExtension(Extension): 17 | def extendMarkdown(self, md): # noqa: N802, PLR6301 18 | sys.path.insert(0, HERE) 19 | 20 | from expand_blocks import ExpandedBlocksPreprocessor 21 | from inject_version import VersionInjectionPreprocessor 22 | from render_default_test_env import TestEnvDefaultsPreprocessor 23 | from render_ruff_defaults import RuffDefaultsPreprocessor 24 | 25 | md.preprocessors.register(ExpandedBlocksPreprocessor(), ExpandedBlocksPreprocessor.__name__, 100) 26 | md.preprocessors.register(VersionInjectionPreprocessor(), VersionInjectionPreprocessor.__name__, 101) 27 | md.preprocessors.register(RuffDefaultsPreprocessor(), RuffDefaultsPreprocessor.__name__, 102) 28 | md.preprocessors.register(TestEnvDefaultsPreprocessor(), TestEnvDefaultsPreprocessor.__name__, 103) 29 | 30 | sys.path.pop(0) 31 | -------------------------------------------------------------------------------- /docs/.hooks/title_from_content.py: -------------------------------------------------------------------------------- 1 | def on_page_markdown( 2 | markdown, 3 | page, 4 | **kwargs, # noqa: ARG001 5 | ): 6 | if 'title' in page.meta: 7 | return 8 | 9 | first_line = markdown.strip().splitlines()[0] 10 | if first_line.startswith('# '): 11 | title = first_line[2:].split(' # {:', maxsplit=1)[0].strip() 12 | page.meta['title'] = title 13 | page.meta['social'] = {'cards_layout_options': {'title': title}} 14 | -------------------------------------------------------------------------------- /docs/.overrides/partials/copyright.html: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /docs/.snippets/abbrs.txt: -------------------------------------------------------------------------------- 1 | *[PyPI]: Python Package Index 2 | -------------------------------------------------------------------------------- /docs/.snippets/links.txt: -------------------------------------------------------------------------------- 1 | [PEP 440 version specifiers]: https://peps.python.org/pep-0440/#version-specifiers 2 | [PEP 508]: https://peps.python.org/pep-0508/ 3 | [PEP 517]: https://peps.python.org/pep-0517/ 4 | [PEP 639]: https://peps.python.org/pep-0639/ 5 | [PEP 660]: https://peps.python.org/pep-0660/ 6 | [PEP 665]: https://peps.python.org/pep-0665/ 7 | [project metadata standard]: https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table 8 | -------------------------------------------------------------------------------- /docs/assets/css/custom.css: -------------------------------------------------------------------------------- 1 | :root > * { 2 | /* Use font but disable ligatures, see https://github.com/pypa/hatch/issues/104 */ 3 | font-variant-ligatures: none; 4 | } 5 | 6 | /* Brighter links for dark mode */ 7 | [data-md-color-scheme=slate] { 8 | /* https://github.com/squidfunk/mkdocs-material/blob/9.1.2/src/assets/stylesheets/main/_colors.scss#L91-L92 */ 9 | --md-typeset-a-color: var(--md-primary-fg-color--light); 10 | } 11 | 12 | /* FiraCode https://github.com/tonsky/FiraCode */ 13 | code { font-family: 'Fira Code', monospace; } 14 | @supports (font-variation-settings: normal) { 15 | code { font-family: 'Fira Code VF', monospace; } 16 | } 17 | 18 | /* https://github.com/squidfunk/mkdocs-material/issues/1522 */ 19 | .md-typeset h5 { 20 | color: var(--md-default-fg-color); 21 | text-transform: none; 22 | } 23 | -------------------------------------------------------------------------------- /docs/blog/.authors.yml: -------------------------------------------------------------------------------- 1 | authors: 2 | ofek: 3 | name: Ofek Lev 4 | description: Creator 5 | avatar: https://avatars.githubusercontent.com/u/9677399 6 | flying-sheep: 7 | name: Phil A. 8 | description: Contributor 9 | avatar: https://avatars.githubusercontent.com/u/291575 10 | -------------------------------------------------------------------------------- /docs/blog/index.md: -------------------------------------------------------------------------------- 1 | # Blog 2 | -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-1100/run-script.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/blog/posts/release-hatch-1100/run-script.gif -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-1100/testing-jinja.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/blog/posts/release-hatch-1100/testing-jinja.gif -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-1100/testing-rich.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/blog/posts/release-hatch-1100/testing-rich.gif -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-160/rich-readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/blog/posts/release-hatch-160/rich-readme.png -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-180/available-pythons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/blog/posts/release-hatch-180/available-pythons.png -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-180/install-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/blog/posts/release-hatch-180/install-demo.gif -------------------------------------------------------------------------------- /docs/blog/posts/release-hatch-190.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2023-12-18 3 | authors: [ofek] 4 | description: >- 5 | Hatch v1.9.0 brings improvements to static analysis and important bug fixes. 6 | categories: 7 | - Release 8 | --- 9 | 10 | # Hatch v1.9.0 11 | 12 | Hatch [v1.9.0](https://github.com/pypa/hatch/releases/tag/hatch-v1.9.0) brings improvements to static analysis and important bug fixes. 13 | 14 | 15 | 16 | ## Static analysis 17 | 18 | The default version of Ruff has been increased to [v0.1.8](https://astral.sh/blog/ruff-v0.1.8). This release brings formatting capabilities to docstrings and Hatch enables this by default with line length set to 80. This length was chosen as the default because it plays nicely with the rendering of the most popular themes for Python documentation, such as [Material for MkDocs](https://github.com/squidfunk/mkdocs-material) and [Furo](https://github.com/pradyunsg/furo). 19 | 20 | Additionally, it is now possible for projects to [pin](../../config/internal/static-analysis.md#dependencies) to specific versions of Ruff for upgrading at a later time: 21 | 22 | ```toml config-example 23 | [tool.hatch.envs.hatch-static-analysis] 24 | dependencies = ["ruff==X.Y.Z"] 25 | ``` 26 | 27 | ## Notable fixes 28 | 29 | - Python resolution for environments that do not install the project is no longer bound by the project's [Python requirement](../../config/metadata.md#python-support). 30 | - Fixed an edge case for out-of-the-box static analysis when there was existing configuration. 31 | - Compatibility checks for environments no longer occur if the environment is already created. This significantly increases the responsiveness of environment usage. 32 | -------------------------------------------------------------------------------- /docs/cli/about.md: -------------------------------------------------------------------------------- 1 | # CLI usage 2 | 3 | ----- 4 | 5 | ## Verbosity 6 | 7 | The amount of displayed output is controlled solely by the `-v`/`--verbose` (environment variable `HATCH_VERBOSE`) and `-q`/`--quiet` (environment variable `HATCH_QUIET`) [root options](reference.md#hatch). 8 | 9 | The levels are documented [here](../config/hatch.md#terminal). 10 | 11 | ## Project awareness 12 | 13 | No matter the [mode](../config/hatch.md#mode), Hatch will always change to the project's root directory for [entering](../environment.md#entering-environments) or [running commands](../environment.md#command-execution) in environments. 14 | 15 | ## Tab completion 16 | 17 | Completion is achieved by saving a script and then executing it as a part of your shell's startup sequence. 18 | 19 | Afterward, you'll need to start a new shell in order for the changes to take effect. 20 | 21 | === "Bash" 22 | Save the script somewhere: 23 | 24 | ```console 25 | _HATCH_COMPLETE=bash_source hatch > ~/.hatch-complete.bash 26 | ``` 27 | 28 | Source the file in `~/.bashrc` (or `~/.bash_profile` if on macOS): 29 | 30 | ```bash 31 | . ~/.hatch-complete.bash 32 | ``` 33 | 34 | === "Z shell" 35 | Save the script somewhere: 36 | 37 | ```console 38 | _HATCH_COMPLETE=zsh_source hatch > ~/.hatch-complete.zsh 39 | ``` 40 | 41 | Source the file in `~/.zshrc`: 42 | 43 | ```zsh 44 | . ~/.hatch-complete.zsh 45 | ``` 46 | 47 | === "fish" 48 | Save the script in `~/.config/fish/completions`: 49 | 50 | ```console 51 | _HATCH_COMPLETE=fish_source hatch > ~/.config/fish/completions/hatch.fish 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/cli/reference.md: -------------------------------------------------------------------------------- 1 | ::: mkdocs-click 2 | :module: hatch.cli 3 | :command: hatch 4 | :depth: 0 5 | :style: table 6 | :remove_ascii_art: true 7 | -------------------------------------------------------------------------------- /docs/community/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | The usual process to make a contribution is to: 4 | 5 | 1. Check for existing related issues 6 | 2. Fork the repository and create a new branch 7 | 3. Make your changes 8 | 4. Make sure formatting, linting and tests passes. 9 | 5. Add tests if possible to cover the lines you added. 10 | 6. Commit, and send a Pull Request. 11 | 12 | ## Clone the repository 13 | 14 | Clone the `hatch` repository, `cd` into it, and create a new branch for your contribution: 15 | 16 | ```bash 17 | cd hatch 18 | git switch -c add-my-contribution 19 | ``` 20 | 21 | ## Run the tests 22 | 23 | Run the test suite while developing: 24 | 25 | ```bash 26 | hatch test 27 | ``` 28 | 29 | Run the test suite with coverage report: 30 | 31 | ```bash 32 | hatch test --cover 33 | ``` 34 | 35 | Run the extended test suite with coverage: 36 | 37 | ```bash 38 | hatch test --cover --all 39 | ``` 40 | 41 | ## Lint 42 | 43 | Run automated formatting: 44 | 45 | ```bash 46 | hatch fmt 47 | ``` 48 | 49 | Run full linting and type checking: 50 | 51 | ```bash 52 | hatch fmt --check 53 | hatch run types:check 54 | ``` 55 | 56 | ## Docs 57 | 58 | Start the documentation in development: 59 | 60 | ```bash 61 | hatch run docs:serve 62 | ``` 63 | 64 | Build and validate the documentation website: 65 | 66 | ```bash 67 | hatch run docs:build-check 68 | ``` -------------------------------------------------------------------------------- /docs/community/highlights.md: -------------------------------------------------------------------------------- 1 | # Community highlights 2 | 3 | ----- 4 | 5 | ## Integration 6 | 7 | - Project Jupyter - https://blog.jupyter.org/packaging-for-jupyter-in-2022-c7be64c38926 8 | - Visual Studio Code - https://code.visualstudio.com/updates/v1_88#_hatch-environment-discovery 9 | 10 | ## Adoption 11 | 12 | - Black - https://ichard26.github.io/blog/2022/10/black-22.10.0/#goodbye-python-36-and-hello-hatchling 13 | - "Switching to Hatch" - https://andrich.me/2023/08/switching-to-hatch/ 14 | -------------------------------------------------------------------------------- /docs/config/internal/build.md: -------------------------------------------------------------------------------- 1 | # Build environment configuration 2 | 3 | ----- 4 | 5 | You can fully alter the behavior of the environment used by the [`build`](../../cli/reference.md#hatch-build) command. 6 | 7 | ## Dependencies 8 | 9 | Build environments will always have what is required by the [build system](../build.md#build-system), [targets](../build.md#target-dependencies), and [hooks](../build.md#hook-dependencies). 10 | 11 | You can define [dependencies](../environment/overview.md#dependencies) that your builds may require in the environment as well: 12 | 13 | ```toml config-example 14 | [tool.hatch.envs.hatch-build] 15 | dependencies = [ 16 | "cython", 17 | ] 18 | ``` 19 | 20 | !!! warning "caution" 21 | It's recommended to only use the standard mechanisms to define build dependencies for better compatibility with other tools. 22 | 23 | ## Environment variables 24 | 25 | You can define [environment variables](../environment/overview.md#environment-variables) that will be set during builds: 26 | 27 | ```toml config-example 28 | [tool.hatch.envs.hatch-build.env-vars] 29 | SOURCE_DATE_EPOCH = "1580601600" 30 | ``` 31 | 32 | ## Installer 33 | 34 | By default, [UV is enabled](../../how-to/environment/select-installer.md). You may disable that behavior as follows: 35 | 36 | ```toml config-example 37 | [tool.hatch.envs.hatch-build] 38 | installer = "pip" 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/config/project-templates.md: -------------------------------------------------------------------------------- 1 | # Project templates 2 | 3 | ----- 4 | 5 | You can control how new projects are created by the [new](../cli/reference.md#hatch-new) command using Hatch's [config file](hatch.md). 6 | 7 | ## Author 8 | 9 | ```toml tab="config.toml" 10 | [template] 11 | name = "..." 12 | email = "..." 13 | ``` 14 | 15 | ## Licenses 16 | 17 | ```toml tab="config.toml" 18 | [template.licenses] 19 | headers = true 20 | default = [ 21 | "MIT", 22 | ] 23 | ``` 24 | 25 | The list of licenses should be composed of [SPDX identifiers](https://spdx.org/licenses/). If multiple licenses are specified, then they will be placed in a [LICENSES](https://reuse.software/faq/#multi-licensing) directory. 26 | 27 | ## Options 28 | 29 | ### Tests 30 | 31 | This adds a `tests` directory with environments for testing and linting. 32 | 33 | ```toml tab="config.toml" 34 | [template.plugins.default] 35 | tests = true 36 | ``` 37 | 38 | ### CI 39 | 40 | This adds a [GitHub Actions workflow](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#workflows) that runs tests on all platforms using modern versions of Python. 41 | 42 | ```toml tab="config.toml" 43 | [template.plugins.default] 44 | ci = false 45 | ``` 46 | 47 | ### `src` layout 48 | 49 | See [this blog post](https://blog.ionelmc.ro/2014/05/25/python-packaging/). 50 | 51 | ```toml tab="config.toml" 52 | [template.plugins.default] 53 | src-layout = true 54 | ``` 55 | 56 | ## Feature flags 57 | 58 | ### Command line interface 59 | 60 | The `--cli` flag adds a CLI backed by [Click](https://github.com/pallets/click) that can also be invoked with `python -m `. 61 | -------------------------------------------------------------------------------- /docs/how-to/integrate/vscode/run-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/how-to/integrate/vscode/run-file.png -------------------------------------------------------------------------------- /docs/how-to/integrate/vscode/select-interpreter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/docs/how-to/integrate/vscode/select-interpreter.png -------------------------------------------------------------------------------- /docs/how-to/meta/report-issues.md: -------------------------------------------------------------------------------- 1 | # How to report issues 2 | 3 | ----- 4 | 5 | All reports regarding unexpected behavior should be generated with the [`self report`](../../cli/reference.md#hatch-self-report) command: 6 | 7 | ``` 8 | $ hatch self report 9 | ``` 10 | 11 | By default, this will open a new tab in your default browser with pre-populated information about your environment. 12 | 13 | If Hatch is not installed alongside a web browser, you may also pass the `--no-open`/`-n` command which will output the URL with correct parameters for copying elsewhere: 14 | 15 | ``` 16 | $ hatch self report -n 17 | https://github.com/pypa/hatch/issues/new?body=%23%23+Current+behavior%0A%3C%21--+A+clear+and+concise+description+of+the+behavior.+--%3E%0A%0A%23%23+Expected+behavior%0A%3C%21--+A+clear+and+concise+description+of+what+you+expected+to+happen.+--%3E%0A%0A%23%23+Additional+context%0A%3C%21--+Add+any+other+context+about+the+problem+here.+If+applicable%2C+add+screenshots+to+help+explain.+--%3E%0A%0A%23%23+Debug%0A%0A%23%23%23+Installation%0A%0A-+Source%3A+pip%0A-+Version%3A+1.9.2.dev5%0A-+Platform%3A+Windows%0A-+Python+version%3A%0A++++%60%60%60%0A++++3.11.1+%28tags%2Fv3.11.1%3Aa7a450f%2C+Dec++6+2022%2C+19%3A58%3A39%29+%5BMSC+v.1934+64+bit+%28AMD64%29%5D%0A++++%60%60%60%0A%0A%23%23%23+Configuration%0A%0A%60%60%60toml%0Amode+%3D+%22local%22%0Ashell+%3D+%22nu%22%0A%60%60%60%0A 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/how-to/publish/auth.md: -------------------------------------------------------------------------------- 1 | # How to authenticate for index publishing 2 | 3 | ---- 4 | 5 | The username is derived from the following sources, in order of precedence: 6 | 7 | 1. The `--user` / `-u` cli option. 8 | 2. The `HATCH_INDEX_USER` environment variable. 9 | 3. The [`repos` tables](../../plugins/publisher/package-index.md). 10 | 4. The [`~/.pypirc` file](https://packaging.python.org/en/latest/specifications/pypirc/). 11 | 5. The input to an interactive prompt. 12 | 13 | As a fallback the value `__token__` is applied. 14 | 15 | The password is looked up in these: 16 | 17 | 1. The [`~/.pypirc` file](https://packaging.python.org/en/latest/specifications/pypirc/) 18 | if the username was provided by it. 19 | 2. The `--auth` / `-a` cli option. 20 | 3. The `HATCH_INDEX_AUTH` environment variable. 21 | 4. The [`repos` tables](../../plugins/publisher/package-index.md). 22 | 5. A variety of OS-level credentials services backed by [keyring](https://github.com/jaraco/keyring). 23 | 6. The input to an interactive prompt. 24 | 25 | If interactively provided credentials were used, the username will be stored in 26 | [Hatch's cache](../../config/hatch.md#cache) and the password stored in the available 27 | [keyring](https://github.com/jaraco/keyring) backed credentials stores. 28 | 29 | For automated releasing to PyPI, it is recommended to use ["Trusted Publishing" with OIDC](https://docs.pypi.org/trusted-publishers/) 30 | (e.g. PyPA's [`pypi-publish`](https://github.com/pypa/gh-action-pypi-publish) GitHub Action) 31 | or per-project [API tokens](https://pypi.org/help/#apitoken). 32 | -------------------------------------------------------------------------------- /docs/how-to/publish/repo.md: -------------------------------------------------------------------------------- 1 | # How to configure repositories for index publishing 2 | 3 | ---- 4 | 5 | You can select the repository with which to upload using the `-r`/`--repo` option or by setting the `HATCH_INDEX_REPO` environment variable. 6 | 7 | Rather than specifying the full URL of a repository, you can use a named repository from a `publish.index.repos` table defined in Hatch's [config file](../../config/hatch.md): 8 | 9 | ```toml tab="config.toml" 10 | [publish.index.repos.private] 11 | url = "..." 12 | ... 13 | ``` 14 | 15 | The following repository names are reserved by Hatch and cannot be overridden: 16 | 17 | | Name | Repository | 18 | | --- | --- | 19 | | `main` | https://upload.pypi.org/legacy/ | 20 | | `test` | https://test.pypi.org/legacy/ | 21 | 22 | The `main` repository is used by default. 23 | -------------------------------------------------------------------------------- /docs/meta/authors.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | ----- 4 | 5 | ## Maintainers 6 | 7 | - Ofek Lev [:material-web:](https://ofek.dev) [:material-github:](https://github.com/ofek) [:material-twitter:](https://twitter.com/Ofekmeister) 8 | 9 | ## Contributors 10 | 11 | - Amjith Ramanujam [:material-twitter:](https://twitter.com/amjithr) 12 | - Arnaud Crowther [:material-github:](https://github.com/areknow) 13 | - Chaojie [:material-web:](https://chaojie.fun) [:material-github:](https://github.com/ischaojie) 14 | - Chris Warrick [:material-twitter:](https://twitter.com/Kwpolska) 15 | - Lumír 'Frenzy' Balhar [:material-email:](mailto:frenzy.madness@gmail.com) [:material-twitter:](https://twitter.com/lumirbalhar) 16 | - Ofek Lev [:material-web:](https://ofek.dev) [:material-github:](https://github.com/ofek) [:material-twitter:](https://twitter.com/Ofekmeister) 17 | - Olga Matoula [:material-github:](https://github.com/olgarithms) [:material-twitter:](https://twitter.com/olgarithms_) 18 | - Philip Blair [:material-email:](mailto:philip@pblair.org) 19 | - Robert Rosca [:material-github:](https://github.com/robertrosca) 20 | -------------------------------------------------------------------------------- /docs/next-steps.md: -------------------------------------------------------------------------------- 1 | # Next steps 2 | 3 | ----- 4 | 5 | ## Learn more 6 | 7 | At this point you should have a basic understanding of how to use Hatch. 8 | 9 | Now you may want to check out advanced configuration for [environments](config/environment/overview.md) or [builds](config/build.md), set up your [preferred shell](config/hatch.md#shell), or read more about Hatch's [CLI](cli/about.md). 10 | 11 | After that, check out the [Hatch Showcase](https://github.com/ofek/hatch-showcase) project to see examples of what is possible. 12 | 13 | Finally, if you see a need, feel free to write a [plugin](plugins/about.md) for extended functionality. 14 | 15 | ## Community 16 | 17 | For any projects using Hatch, you may add its official badge somewhere prominent like the README. 18 | 19 | [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg){ loading=lazy .off-glb }](https://github.com/pypa/hatch) 20 | 21 | === "Markdown" 22 | ```md 23 | [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) 24 | ``` 25 | 26 | === "reStructuredText" 27 | ```rst 28 | .. image:: https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg 29 | :alt: Hatch project 30 | :target: https://github.com/pypa/hatch 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/plugins/build-hook/custom.md: -------------------------------------------------------------------------------- 1 | # Custom build hook 2 | 3 | ----- 4 | 5 | This is a custom class in a given Python file that inherits from the [BuildHookInterface](reference.md#hatchling.builders.hooks.plugin.interface.BuildHookInterface). 6 | 7 | ## Configuration 8 | 9 | The build hook plugin name is `custom`. 10 | 11 | ```toml config-example 12 | [tool.hatch.build.hooks.custom] 13 | [tool.hatch.build.targets..hooks.custom] 14 | ``` 15 | 16 | ## Options 17 | 18 | | Option | Default | Description | 19 | | --- | --- | --- | 20 | | `path` | `hatch_build.py` | The path of the Python file | 21 | 22 | ## Example 23 | 24 | ```python tab="hatch_build.py" 25 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 26 | 27 | 28 | class CustomBuildHook(BuildHookInterface): 29 | ... 30 | ``` 31 | 32 | If multiple subclasses are found, you must define a function named `get_build_hook` that returns the desired build hook. 33 | 34 | !!! note 35 | Any defined [PLUGIN_NAME](reference.md#hatchling.builders.hooks.plugin.interface.BuildHookInterface.PLUGIN_NAME) is ignored and will always be `custom`. 36 | -------------------------------------------------------------------------------- /docs/plugins/build-hook/version.md: -------------------------------------------------------------------------------- 1 | # Version build hook 2 | 3 | ----- 4 | 5 | This writes the project's version to a file. 6 | 7 | ## Configuration 8 | 9 | The build hook plugin name is `version`. 10 | 11 | ```toml config-example 12 | [tool.hatch.build.hooks.version] 13 | [tool.hatch.build.targets..hooks.version] 14 | ``` 15 | 16 | ## Options 17 | 18 | | Option | Description | 19 | | --- | --- | 20 | | `path` (required) | A relative path to the desired file | 21 | | `template` | A string representing the entire contents of `path` that will be formatted with a `version` variable | 22 | | `pattern` | Rather than updating the entire file, a regular expression may be used that has a named group called `version` that represents the version. If set to `true`, a pattern will be used that looks for a variable named `__version__` or `VERSION` that is set to a string containing the version, optionally prefixed with the lowercase letter `v`. | 23 | -------------------------------------------------------------------------------- /docs/plugins/builder/custom.md: -------------------------------------------------------------------------------- 1 | # Custom builder 2 | 3 | ----- 4 | 5 | This is a custom class in a given Python file that inherits from the [BuilderInterface](reference.md#hatchling.builders.plugin.interface.BuilderInterface). 6 | 7 | ## Configuration 8 | 9 | The builder plugin name is `custom`. 10 | 11 | ```toml config-example 12 | [tool.hatch.build.targets.custom] 13 | ``` 14 | 15 | ## Options 16 | 17 | | Option | Default | Description | 18 | | --- | --- | --- | 19 | | `path` | `hatch_build.py` | The path of the Python file | 20 | 21 | ## Example 22 | 23 | ```python tab="hatch_build.py" 24 | from hatchling.builders.plugin.interface import BuilderInterface 25 | 26 | 27 | class CustomBuilder(BuilderInterface): 28 | ... 29 | ``` 30 | 31 | If multiple subclasses are found, you must define a function named `get_builder` that returns the desired builder. 32 | 33 | !!! note 34 | Any defined [PLUGIN_NAME](reference.md#hatchling.builders.plugin.interface.BuilderInterface.PLUGIN_NAME) is ignored and will always be `custom`. 35 | -------------------------------------------------------------------------------- /docs/plugins/builder/reference.md: -------------------------------------------------------------------------------- 1 | # Builder plugins 2 | 3 | ----- 4 | 5 | See the documentation for [build configuration](../../config/build.md). 6 | 7 | ## Known third-party 8 | 9 | - [hatch-aws](https://github.com/aka-raccoon/hatch-aws) - used for building AWS Lambda functions with SAM 10 | - [hatch-zipped-directory](https://github.com/dairiki/hatch-zipped-directory) - used for building ZIP archives for installation into various foreign package installation systems 11 | 12 | ::: hatchling.builders.plugin.interface.BuilderInterface 13 | options: 14 | members: 15 | - PLUGIN_NAME 16 | - app 17 | - root 18 | - build_config 19 | - target_config 20 | - config 21 | - get_config_class 22 | - get_version_api 23 | - get_default_versions 24 | - clean 25 | - recurse_included_files 26 | - get_default_build_data 27 | -------------------------------------------------------------------------------- /docs/plugins/environment-collector/custom.md: -------------------------------------------------------------------------------- 1 | # Custom environment collector 2 | 3 | ----- 4 | 5 | This is a custom class in a given Python file that inherits from the [EnvironmentCollectorInterface](reference.md#hatch.env.collectors.plugin.interface.EnvironmentCollectorInterface). 6 | 7 | ## Configuration 8 | 9 | The environment collector plugin name is `custom`. 10 | 11 | ```toml config-example 12 | [tool.hatch.env.collectors.custom] 13 | ``` 14 | 15 | ## Options 16 | 17 | | Option | Default | Description | 18 | | --- | --- | --- | 19 | | `path` | `hatch_plugins.py` | The path of the Python file | 20 | 21 | ## Example 22 | 23 | ```python tab="hatch_plugins.py" 24 | from hatch.env.collectors.plugin.interface import EnvironmentCollectorInterface 25 | 26 | 27 | class CustomEnvironmentCollector(EnvironmentCollectorInterface): 28 | ... 29 | ``` 30 | 31 | If multiple subclasses are found, you must define a function named `get_environment_collector` that returns the desired environment collector. 32 | 33 | !!! note 34 | Any defined [PLUGIN_NAME](reference.md#hatch.env.collectors.plugin.interface.EnvironmentCollectorInterface.PLUGIN_NAME) is ignored and will always be `custom`. 35 | -------------------------------------------------------------------------------- /docs/plugins/environment-collector/default.md: -------------------------------------------------------------------------------- 1 | # Default environment collector 2 | 3 | ----- 4 | 5 | This adds the `default` environment with [type](../../config/environment/overview.md#type) set to [virtual](../environment/virtual.md) and will always be applied. 6 | 7 | ## Configuration 8 | 9 | The environment collector plugin name is `default`. 10 | 11 | ```toml config-example 12 | [tool.hatch.env.collectors.default] 13 | ``` 14 | 15 | ## Options 16 | 17 | There are no options available currently. 18 | -------------------------------------------------------------------------------- /docs/plugins/environment-collector/reference.md: -------------------------------------------------------------------------------- 1 | # Environment collector plugins 2 | 3 | ----- 4 | 5 | Environment collectors allow for dynamically modifying environments or adding environments beyond those defined in config. Users can override default values provided by each environment. 6 | 7 | ## Known third-party 8 | 9 | - [hatch-mkdocs](https://github.com/mkdocs/hatch-mkdocs) - integrate [MkDocs](https://github.com/mkdocs/mkdocs) and infer dependencies into an env 10 | 11 | ## Installation 12 | 13 | Any required environment collectors that are not built-in must be manually installed alongside Hatch or listed in the `tool.hatch.env.requires` array for automatic management: 14 | 15 | ```toml config-example 16 | [tool.hatch.env] 17 | requires = [ 18 | "...", 19 | ] 20 | ``` 21 | 22 | ::: hatch.env.collectors.plugin.interface.EnvironmentCollectorInterface 23 | options: 24 | members: 25 | - PLUGIN_NAME 26 | - root 27 | - config 28 | - get_initial_config 29 | - finalize_config 30 | - finalize_environments 31 | -------------------------------------------------------------------------------- /docs/plugins/metadata-hook/custom.md: -------------------------------------------------------------------------------- 1 | # Custom metadata hook 2 | 3 | ----- 4 | 5 | This is a custom class in a given Python file that inherits from the [MetadataHookInterface](reference.md#hatchling.metadata.plugin.interface.MetadataHookInterface). 6 | 7 | ## Configuration 8 | 9 | The metadata hook plugin name is `custom`. 10 | 11 | ```toml config-example 12 | [tool.hatch.metadata.hooks.custom] 13 | ``` 14 | 15 | ## Options 16 | 17 | | Option | Default | Description | 18 | | --- | --- | --- | 19 | | `path` | `hatch_build.py` | The path of the Python file | 20 | 21 | ## Example 22 | 23 | ```python tab="hatch_build.py" 24 | from hatchling.metadata.plugin.interface import MetadataHookInterface 25 | 26 | 27 | class CustomMetadataHook(MetadataHookInterface): 28 | ... 29 | ``` 30 | 31 | If multiple subclasses are found, you must define a function named `get_metadata_hook` that returns the desired build hook. 32 | 33 | !!! note 34 | Any defined [PLUGIN_NAME](reference.md#hatchling.metadata.plugin.interface.MetadataHookInterface.PLUGIN_NAME) is ignored and will always be `custom`. 35 | -------------------------------------------------------------------------------- /docs/plugins/metadata-hook/reference.md: -------------------------------------------------------------------------------- 1 | # Metadata hook plugins 2 | 3 | ----- 4 | 5 | Metadata hooks allow for the modification of [project metadata](../../config/metadata.md) after it has been loaded. 6 | 7 | ## Known third-party 8 | 9 | - [hatch-docstring-description](https://github.com/flying-sheep/hatch-docstring-description) - set the project description using docstrings 10 | - [hatch-fancy-pypi-readme](https://github.com/hynek/hatch-fancy-pypi-readme) - dynamically construct the README 11 | - [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version) - uses fields from NodeJS `package.json` files 12 | - [hatch-odoo](https://github.com/acsone/hatch-odoo) - determine dependencies based on manifests of Odoo add-ons 13 | - [hatch-requirements-txt](https://github.com/repo-helper/hatch-requirements-txt) - read project dependencies from `requirements.txt` files 14 | - [UniDep](https://github.com/basnijholt/unidep) - for unified `pip` and `conda` dependency management using a single `requirements.yaml` file for both 15 | 16 | ::: hatchling.metadata.plugin.interface.MetadataHookInterface 17 | options: 18 | members: 19 | - PLUGIN_NAME 20 | - root 21 | - config 22 | - update 23 | - get_known_classifiers 24 | -------------------------------------------------------------------------------- /docs/plugins/publisher/reference.md: -------------------------------------------------------------------------------- 1 | # Publisher plugins 2 | 3 | ----- 4 | 5 | ## Known third-party 6 | 7 | - [hatch-aws-publisher](https://github.com/aka-raccoon/hatch-aws-publisher) - publish AWS Lambda functions with SAM 8 | 9 | ::: hatch.publish.plugin.interface.PublisherInterface 10 | options: 11 | members: 12 | - PLUGIN_NAME 13 | - app 14 | - root 15 | - cache_dir 16 | - project_config 17 | - plugin_config 18 | - disable 19 | - publish 20 | -------------------------------------------------------------------------------- /docs/plugins/utilities.md: -------------------------------------------------------------------------------- 1 | # Plugin utilities 2 | 3 | ----- 4 | 5 | ::: hatchling.builders.utils.get_reproducible_timestamp 6 | options: 7 | show_root_full_path: true 8 | 9 | ::: hatchling.builders.config.BuilderConfig 10 | options: 11 | show_source: false 12 | members: 13 | - directory 14 | - ignore_vcs 15 | - reproducible 16 | - dev_mode_dirs 17 | - versions 18 | - dependencies 19 | - default_include 20 | - default_exclude 21 | - default_packages 22 | - default_only_include 23 | 24 | ::: hatchling.bridge.app.Application 25 | options: 26 | show_source: false 27 | members: 28 | - abort 29 | - verbosity 30 | - display_debug 31 | - display_error 32 | - display_info 33 | - display_success 34 | - display_waiting 35 | - display_warning 36 | 37 | ::: hatch.utils.platform.Platform 38 | options: 39 | show_source: false 40 | members: 41 | - format_for_subprocess 42 | - run_command 43 | - check_command 44 | - check_command_output 45 | - capture_process 46 | - exit_with_command 47 | - default_shell 48 | - modules 49 | - home 50 | - name 51 | - display_name 52 | - windows 53 | - macos 54 | - linux 55 | 56 | ::: hatch.env.context.EnvironmentContextFormatter 57 | options: 58 | show_source: false 59 | members: 60 | - formatters 61 | 62 | ::: hatch.env.plugin.interface.FileSystemContext 63 | options: 64 | show_source: false 65 | members: 66 | - env 67 | - sync_local 68 | - sync_env 69 | - local_path 70 | - env_path 71 | - join 72 | -------------------------------------------------------------------------------- /docs/plugins/version-scheme/reference.md: -------------------------------------------------------------------------------- 1 | # Version scheme plugins 2 | 3 | ----- 4 | 5 | ## Known third-party 6 | 7 | - [hatch-semver](https://github.com/Nagidal/hatch-semver) - uses [semantic versioning](https://semver.org) 8 | 9 | ::: hatchling.version.scheme.plugin.interface.VersionSchemeInterface 10 | options: 11 | members: 12 | - PLUGIN_NAME 13 | - root 14 | - config 15 | - validate_bump 16 | - update 17 | -------------------------------------------------------------------------------- /docs/plugins/version-scheme/standard.md: -------------------------------------------------------------------------------- 1 | # Standard version scheme 2 | 3 | ----- 4 | 5 | See the documentation for [versioning](../../version.md#updating). 6 | 7 | ## Configuration 8 | 9 | The version scheme plugin name is `standard`. 10 | 11 | ```toml config-example 12 | [tool.hatch.version] 13 | scheme = "standard" 14 | ``` 15 | 16 | ## Options 17 | 18 | | Option | Description | 19 | | --- | --- | 20 | | `validate-bump` | When setting a specific version, this determines whether to check that the new version is higher than the original. The default is `true`. | 21 | -------------------------------------------------------------------------------- /docs/plugins/version-source/code.md: -------------------------------------------------------------------------------- 1 | # Code version source 2 | 3 | ----- 4 | 5 | ## Updates 6 | 7 | Setting the version is not supported. 8 | 9 | ## Configuration 10 | 11 | The version source plugin name is `code`. 12 | 13 | ```toml config-example 14 | [tool.hatch.version] 15 | source = "code" 16 | ``` 17 | 18 | ## Options 19 | 20 | | Option | Description | 21 | | --- | --- | 22 | | `path` (required) | A relative path to a Python file or extension module that will be loaded | 23 | | `expression` | A Python expression that when evaluated in the context of the loaded file returns the version. The default expression is simply `__version__`. | 24 | | `search-paths` | A list of relative paths to directories that will be prepended to Python's search path | 25 | 26 | ## Missing imports 27 | 28 | If the chosen path imports another module in your project, then you'll need to use absolute imports coupled with the `search-paths` option. For example, say you need to load the following file: 29 | 30 | ```python tab="src/pkg/\_\_init\_\_.py" 31 | from ._version import get_version 32 | 33 | __version__ = get_version() 34 | ``` 35 | 36 | You should change it to: 37 | 38 | ```python tab="src/pkg/\_\_init\_\_.py" 39 | from pkg._version import get_version 40 | 41 | __version__ = get_version() 42 | ``` 43 | 44 | and the configuration would become: 45 | 46 | ```toml config-example 47 | [tool.hatch.version] 48 | source = "code" 49 | path = "src/pkg/__init__.py" 50 | search-paths = ["src"] 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/plugins/version-source/env.md: -------------------------------------------------------------------------------- 1 | # Environment version source 2 | 3 | ----- 4 | 5 | Retrieves the version from an environment variable. This can be useful in build pipelines where the version is set by an external trigger. 6 | 7 | ## Updates 8 | 9 | Setting the version is not supported. 10 | 11 | ## Configuration 12 | 13 | The version source plugin name is `env`. 14 | 15 | ```toml config-example 16 | [tool.hatch.version] 17 | source = "env" 18 | ``` 19 | 20 | ## Options 21 | 22 | | Option | Description | 23 | | --- | --- | 24 | | `variable` (required) | The name of the environment variable | 25 | -------------------------------------------------------------------------------- /docs/plugins/version-source/reference.md: -------------------------------------------------------------------------------- 1 | # Version source plugins 2 | 3 | ----- 4 | 5 | ## Known third-party 6 | 7 | - [hatch-vcs](https://github.com/ofek/hatch-vcs) - uses your preferred version control system (like Git) 8 | - [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version) - uses the `version` field of NodeJS `package.json` files 9 | - [hatch-regex-commit](https://github.com/frankie567/hatch-regex-commit) - automatically creates a Git commit and tag after version bumping 10 | - [versioningit](https://github.com/jwodder/versioningit) - determines version from Git or Mercurial tags, with customizable version formatting 11 | 12 | ::: hatchling.version.source.plugin.interface.VersionSourceInterface 13 | options: 14 | members: 15 | - PLUGIN_NAME 16 | - root 17 | - config 18 | - get_version_data 19 | - set_version 20 | -------------------------------------------------------------------------------- /docs/plugins/version-source/regex.md: -------------------------------------------------------------------------------- 1 | # Regex version source 2 | 3 | ----- 4 | 5 | See the documentation for [versioning](../../version.md). 6 | 7 | ## Updates 8 | 9 | Setting the version is supported. 10 | 11 | ## Configuration 12 | 13 | The version source plugin name is `regex`. 14 | 15 | ```toml config-example 16 | [tool.hatch.version] 17 | source = "regex" 18 | ``` 19 | 20 | ## Options 21 | 22 | | Option | Description | 23 | | --- | --- | 24 | | `path` (required) | A relative path to a file containing the project's version | 25 | | `pattern` | A regular expression that has a named group called `version` that represents the version. The default pattern looks for a variable named `__version__` or `VERSION` that is set to a string containing the version, optionally prefixed with the lowercase letter `v`. | 26 | -------------------------------------------------------------------------------- /docs/publish.md: -------------------------------------------------------------------------------- 1 | # Publishing 2 | 3 | ----- 4 | 5 | After your project is [built](build.md), you can distribute it using the [`publish`](cli/reference.md#hatch-publish) command. 6 | 7 | The `-p`/`--publisher` option controls which publisher to use, with the default being [index](plugins/publisher/package-index.md). 8 | 9 | ## Artifact selection 10 | 11 | By default, the `dist` directory located at the root of your project will be used: 12 | 13 | ```console 14 | $ hatch publish 15 | dist/hatch_demo-1rc0-py3-none-any.whl ... success 16 | dist/hatch_demo-1rc0.tar.gz ... success 17 | 18 | [hatch-demo] 19 | https://pypi.org/project/hatch-demo/1rc0/ 20 | ``` 21 | 22 | You can instead pass specific paths as arguments: 23 | 24 | ``` 25 | hatch publish /path/to/artifacts foo-1.tar.gz 26 | ``` 27 | 28 | Only files ending with `.whl` or `.tar.gz` will be published. 29 | 30 | ## Further resources 31 | 32 | Please refer to the publisher plugin [reference](plugins/publisher/package-index.md) 33 | for configuration options. 34 | 35 | There's a How-To on [authentication](how-to/publish/auth.md) 36 | and on options to select the target [repository](how-to/publish/repo.md). 37 | 38 | The `publish` command is implemented as a built-in plugin, if you're 39 | planning your own plugin, read about the [publisher plugin API](plugins/publisher/reference.md). 40 | -------------------------------------------------------------------------------- /mkdocs.insiders.yml: -------------------------------------------------------------------------------- 1 | INHERIT: mkdocs.yml 2 | 3 | plugins: 4 | git-committers: 5 | repository: pypa/hatch 6 | enabled: !ENV [GITHUB_ACTIONS, false] 7 | token: !ENV [GH_TOKEN] 8 | social: 9 | cards_layout_options: 10 | logo: docs/assets/images/logo.svg 11 | enabled: !ENV [MKDOCS_IMAGE_PROCESSING, false] 12 | material/blog: 13 | categories_allowed: 14 | - General 15 | - News 16 | - Release 17 | - Roadmap 18 | post_excerpt: required 19 | post_slugify: !!python/object/apply:pymdownx.slugs.slugify 20 | kwds: 21 | case: lower 22 | categories_slugify: !!python/object/apply:pymdownx.slugs.slugify 23 | kwds: 24 | case: lower 25 | -------------------------------------------------------------------------------- /release/README.md: -------------------------------------------------------------------------------- 1 | # Release assets 2 | 3 | ----- 4 | 5 | This directory stores files related to building binaries and installers for each platform. 6 | -------------------------------------------------------------------------------- /release/macos/pkg/distribution.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | Hatch 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.python.hatch.pkg 22 | 23 | -------------------------------------------------------------------------------- /release/macos/pkg/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/release/macos/pkg/resources/icon.png -------------------------------------------------------------------------------- /release/unix/make_scripts_portable.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | import sysconfig 5 | from io import BytesIO 6 | from pathlib import Path 7 | 8 | 9 | def main(): 10 | interpreter = Path(sys.executable).resolve() 11 | 12 | # https://github.com/indygreg/python-build-standalone/blob/20240415/cpython-unix/build-cpython.sh#L812-L813 13 | portable_shebang = b'#!/bin/sh\n"exec" "$(dirname $0)/%s" "$0" "$@"\n' % interpreter.name.encode() 14 | 15 | scripts_dir = Path(sysconfig.get_path('scripts')) 16 | for script in scripts_dir.iterdir(): 17 | if not script.is_file(): 18 | continue 19 | 20 | with script.open('rb') as f: 21 | data = BytesIO() 22 | for line in f: 23 | # Ignore leading blank lines 24 | if not line.strip(): 25 | continue 26 | 27 | # Ignore binaries 28 | if not line.startswith(b'#'): 29 | break 30 | 31 | if line.startswith(b'#!%s' % interpreter.parent): 32 | executable = Path(line[2:].rstrip().decode()).resolve() 33 | data.write(portable_shebang if executable == interpreter else line) 34 | else: 35 | data.write(line) 36 | 37 | data.write(f.read()) 38 | break 39 | 40 | contents = data.getvalue() 41 | if not contents: 42 | continue 43 | 44 | with script.open('wb') as f: 45 | f.write(contents) 46 | 47 | 48 | if __name__ == '__main__': 49 | main() 50 | -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | extend = "ruff_defaults.toml" 2 | 3 | # https://github.com/astral-sh/ruff/issues/8627 4 | exclude = [".git", ".mypy_cache", ".ruff_cache", ".venv", "dist"] 5 | 6 | [format] 7 | preview = true 8 | quote-style = "single" 9 | 10 | [lint] 11 | preview = true 12 | ignore = [ 13 | # Allow lazy imports for responsive CLI 14 | "PLC0415", 15 | ] 16 | 17 | [lint.extend-per-file-ignores] 18 | "backend/src/hatchling/bridge/app.py" = ["T201"] 19 | "backend/tests/downstream/integrate.py" = ["INP001", "T201"] 20 | "docs/.hooks/*" = ["INP001", "T201"] 21 | "release/**/*" = ["INP001"] 22 | 23 | [lint.isort] 24 | known-first-party = ["hatch", "hatchling"] 25 | -------------------------------------------------------------------------------- /scripts/install_mkdocs_material_insiders.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | 5 | TOKEN = os.environ.get('GH_TOKEN_MKDOCS_MATERIAL_INSIDERS', '') 6 | DEP_REF = f'git+https://{TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git' 7 | GIT_REF = 'f2d5b41b2e590baf73ae5f51166d88b233ba96aa' 8 | 9 | 10 | def main(): 11 | if not TOKEN: 12 | print('No token is set, skipping') 13 | return 14 | 15 | dependency = f'mkdocs-material[imaging] @ {DEP_REF}@{GIT_REF}' 16 | try: 17 | process = subprocess.Popen( 18 | ['uv', 'pip', 'install', dependency], # noqa: S607 19 | stdout=subprocess.PIPE, 20 | stderr=subprocess.STDOUT, 21 | encoding='utf-8', 22 | ) 23 | except Exception as e: # noqa: BLE001 24 | print(str(e).replace(TOKEN, '*****')) 25 | sys.exit(1) 26 | 27 | with process: 28 | for line in iter(process.stdout.readline, ''): 29 | print(line.replace(TOKEN, '*****'), end='') 30 | 31 | sys.exit(process.returncode) 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /scripts/release_github.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import webbrowser 3 | from urllib.parse import urlencode 4 | 5 | from utils import get_latest_release 6 | 7 | 8 | def main(): 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument('project', choices=['hatch', 'hatchling']) 11 | args = parser.parse_args() 12 | 13 | version, notes = get_latest_release(args.project) 14 | 15 | params = urlencode({ 16 | 'title': f'{args.project.capitalize()} v{version}', 17 | 'tag': f'{args.project}-v{version}', 18 | 'body': notes, 19 | }) 20 | 21 | url = f'https://github.com/pypa/hatch/releases/new?{params}' 22 | webbrowser.open_new_tab(url) 23 | 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /scripts/set_release_version.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from utils import get_latest_release 4 | 5 | 6 | def main(): 7 | version, _ = get_latest_release('hatch') 8 | parts = version.split('.') 9 | 10 | with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as f: 11 | f.write(f'HATCH_DOCS_VERSION={parts[0]}.{parts[1]}\n') 12 | 13 | 14 | if __name__ == '__main__': 15 | main() 16 | -------------------------------------------------------------------------------- /scripts/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pathlib import Path 3 | 4 | ROOT = Path(__file__).resolve().parent.parent 5 | 6 | 7 | def get_latest_release(project): 8 | history_file = ROOT / 'docs' / 'history' / f'{project}.md' 9 | 10 | release_headers = 0 11 | history_file_lines = [] 12 | with history_file.open(encoding='utf-8') as f: 13 | for line in f: 14 | history_file_lines.append(line.rstrip()) 15 | 16 | if line.startswith('## '): 17 | release_headers += 1 18 | 19 | if release_headers == 3: # noqa: PLR2004 20 | break 21 | 22 | release_lines = history_file_lines[history_file_lines.index('## Unreleased') + 1 : -1] 23 | while True: 24 | release_header = release_lines.pop(0) 25 | if release_header.startswith('## '): 26 | break 27 | 28 | return re.search(r'\[(.+)\]', release_header).group(1), '\n'.join(release_lines).strip() 29 | -------------------------------------------------------------------------------- /scripts/validate_history.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | from utils import ROOT 5 | 6 | HEADER_PATTERN = ( 7 | r'^\[([a-z0-9.]+)\]\(https://github\.com/pypa/hatch/releases/tag/({package}-v\1)\)' 8 | r' - [0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}} ## \{{: #\2 \}}$' 9 | ) 10 | 11 | 12 | def main(): 13 | for package in ('hatch', 'hatchling'): 14 | history_file = ROOT / 'docs' / 'history' / f'{package}.md' 15 | current_pattern = HEADER_PATTERN.format(package=package) 16 | 17 | with history_file.open('r', encoding='utf-8') as f: 18 | for raw_line in f: 19 | line = raw_line.strip() 20 | if not line: 21 | continue 22 | 23 | if line.startswith('## '): 24 | _, _, header = line.partition(' ') 25 | if header == 'Unreleased': 26 | continue 27 | 28 | if not re.search(current_pattern, header): 29 | print('Invalid header:') 30 | print(header) 31 | sys.exit(1) 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /src/hatch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/__init__.py -------------------------------------------------------------------------------- /src/hatch/__main__.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | from hatch.cli import main 3 | 4 | main() 5 | -------------------------------------------------------------------------------- /src/hatch/cli/clean/__init__.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | 4 | @click.command(short_help='Remove build artifacts') 5 | @click.argument('location', required=False) 6 | @click.option( 7 | '--target', 8 | '-t', 9 | 'targets', 10 | multiple=True, 11 | help=( 12 | 'The target with which to remove artifacts, overriding project defaults. ' 13 | 'This may be selected multiple times e.g. `-t sdist -t wheel`' 14 | ), 15 | ) 16 | @click.option( 17 | '--hooks-only', 18 | is_flag=True, 19 | help='Whether or not to only remove artifacts from build hooks [env var: `HATCH_BUILD_HOOKS_ONLY`]', 20 | ) 21 | @click.option( 22 | '--no-hooks', 23 | is_flag=True, 24 | help='Whether or not to ignore artifacts from build hooks [env var: `HATCH_BUILD_NO_HOOKS`]', 25 | ) 26 | @click.option( 27 | '--ext', 28 | is_flag=True, 29 | help=( 30 | 'Whether or not to only remove artifacts from build hooks for distributing binary Python packages, such as ' 31 | 'compiled extensions. Equivalent to `--hooks-only -t wheel`' 32 | ), 33 | ) 34 | @click.pass_context 35 | def clean(ctx, location, targets, hooks_only, no_hooks, ext): 36 | """Remove build artifacts.""" 37 | from hatch.cli.build import build 38 | 39 | ctx.invoke( 40 | build, clean_only=True, location=location, targets=targets, hooks_only=hooks_only, no_hooks=no_hooks, ext=ext 41 | ) 42 | -------------------------------------------------------------------------------- /src/hatch/cli/env/__init__.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from hatch.cli.env.create import create 4 | from hatch.cli.env.find import find 5 | from hatch.cli.env.prune import prune 6 | from hatch.cli.env.remove import remove 7 | from hatch.cli.env.run import run 8 | from hatch.cli.env.show import show 9 | 10 | 11 | @click.group(short_help='Manage project environments') 12 | def env(): 13 | pass 14 | 15 | 16 | env.add_command(create) 17 | env.add_command(find) 18 | env.add_command(prune) 19 | env.add_command(remove) 20 | env.add_command(run) 21 | env.add_command(show) 22 | -------------------------------------------------------------------------------- /src/hatch/cli/env/create.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Create environments') 12 | @click.argument('env_name', default='default') 13 | @click.pass_obj 14 | def create(app: Application, env_name: str): 15 | """Create environments.""" 16 | app.ensure_environment_plugin_dependencies() 17 | 18 | environments = app.project.expand_environments(env_name) 19 | if not environments: 20 | app.abort(f'Environment `{env_name}` is not defined by project config') 21 | 22 | incompatible = {} 23 | for env in environments: 24 | environment = app.project.get_environment(env) 25 | if environment.exists(): 26 | app.display_warning(f'Environment `{env}` already exists') 27 | continue 28 | 29 | try: 30 | environment.check_compatibility() 31 | except Exception as e: # noqa: BLE001 32 | if env_name in app.project.config.matrices: 33 | incompatible[env] = str(e) 34 | continue 35 | 36 | app.abort(f'Environment `{env}` is incompatible: {e}') 37 | 38 | app.project.prepare_environment(environment) 39 | 40 | if incompatible: 41 | num_incompatible = len(incompatible) 42 | app.display_warning( 43 | f'Skipped {num_incompatible} incompatible environment{"s" if num_incompatible > 1 else ""}:' 44 | ) 45 | for env, reason in incompatible.items(): 46 | app.display_warning(f'{env} -> {reason}') 47 | -------------------------------------------------------------------------------- /src/hatch/cli/env/find.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Locate environments') 12 | @click.argument('env_name', default='default') 13 | @click.pass_obj 14 | def find(app: Application, env_name: str): 15 | """Locate environments.""" 16 | app.ensure_environment_plugin_dependencies() 17 | 18 | environments = app.project.expand_environments(env_name) 19 | if not environments: 20 | app.abort(f'Environment `{env_name}` is not defined by project config') 21 | 22 | for env in environments: 23 | environment = app.project.get_environment(env) 24 | app.display(environment.find()) 25 | -------------------------------------------------------------------------------- /src/hatch/cli/env/prune.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Remove all environments') 12 | @click.pass_obj 13 | def prune(app: Application): 14 | """Remove all environments.""" 15 | app.ensure_environment_plugin_dependencies() 16 | 17 | environment_types = app.plugins.environment.collect() 18 | 19 | for environments in (app.project.config.envs, app.project.config.internal_envs): 20 | for env_name in environments: 21 | if env_name == app.env_active: 22 | app.abort(f'Cannot remove active environment: {env_name}') 23 | 24 | for env_name, config in environments.items(): 25 | environment_type = config['type'] 26 | if environment_type not in environment_types: 27 | app.abort(f'Environment `{env_name}` has unknown type: {environment_type}') 28 | 29 | environment = environment_types[environment_type]( 30 | app.project.location, 31 | app.project.metadata, 32 | env_name, 33 | config, 34 | app.project.config.matrix_variables.get(env_name, {}), 35 | app.get_env_directory(environment_type), 36 | app.data_dir / 'env' / environment_type, 37 | app.platform, 38 | app.verbosity, 39 | app, 40 | ) 41 | if environment.exists(): 42 | with app.status(f'Removing environment: {env_name}'): 43 | environment.remove() 44 | -------------------------------------------------------------------------------- /src/hatch/cli/env/remove.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Remove environments') 12 | @click.argument('env_name', default='default') 13 | @click.pass_context 14 | def remove(ctx: click.Context, env_name: str): 15 | """Remove environments.""" 16 | app: Application = ctx.obj 17 | app.ensure_environment_plugin_dependencies() 18 | 19 | if (parameter_source := ctx.get_parameter_source('env_name')) is not None and parameter_source.name == 'DEFAULT': 20 | env_name = app.env 21 | 22 | environments = app.project.expand_environments(env_name) 23 | if not environments: 24 | app.abort(f'Environment `{env_name}` is not defined by project config') 25 | 26 | for env_name in environments: 27 | if env_name == app.env_active: 28 | app.abort(f'Cannot remove active environment: {env_name}') 29 | 30 | for env_name in environments: 31 | environment = app.project.get_environment(env_name) 32 | if environment.exists(): 33 | with app.status(f'Removing environment: {env_name}'): 34 | environment.remove() 35 | -------------------------------------------------------------------------------- /src/hatch/cli/project/__init__.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from hatch.cli.project.metadata import metadata 4 | 5 | 6 | @click.group(short_help='View project information') 7 | def project(): 8 | pass 9 | 10 | 11 | project.add_command(metadata) 12 | -------------------------------------------------------------------------------- /src/hatch/cli/python/__init__.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from hatch.cli.python.find import find 4 | from hatch.cli.python.install import install 5 | from hatch.cli.python.remove import remove 6 | from hatch.cli.python.show import show 7 | from hatch.cli.python.update import update 8 | 9 | 10 | @click.group(short_help='Manage Python installations') 11 | def python(): 12 | pass 13 | 14 | 15 | python.add_command(find) 16 | python.add_command(install) 17 | python.add_command(remove) 18 | python.add_command(show) 19 | python.add_command(update) 20 | -------------------------------------------------------------------------------- /src/hatch/cli/python/find.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Locate Python binaries') 12 | @click.argument('name') 13 | @click.option('-p', '--parent', is_flag=True, help='Show the parent directory of the Python binary') 14 | @click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') 15 | @click.pass_obj 16 | def find(app: Application, *, name: str, parent: bool, directory: str | None): 17 | """Locate Python binaries.""" 18 | manager = app.get_python_manager(directory) 19 | installed = manager.get_installed() 20 | if name not in installed: 21 | app.abort(f'Distribution not installed: {name}') 22 | 23 | dist = installed[name] 24 | app.display(str(dist.python_path.parent if parent else dist.python_path)) 25 | -------------------------------------------------------------------------------- /src/hatch/cli/python/remove.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Remove Python distributions') 12 | @click.argument('names', required=True, nargs=-1) 13 | @click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') 14 | @click.pass_obj 15 | def remove(app: Application, *, names: tuple[str, ...], directory: str | None): 16 | """ 17 | Remove Python distributions. 18 | 19 | You may select `all` to remove all installed distributions: 20 | 21 | \b 22 | ``` 23 | hatch python remove all 24 | ``` 25 | """ 26 | manager = app.get_python_manager(directory) 27 | installed = manager.get_installed() 28 | selection = tuple(installed) if 'all' in names else names 29 | for name in selection: 30 | if name not in installed: 31 | app.display_warning(f'Distribution is not installed: {name}') 32 | continue 33 | 34 | dist = installed[name] 35 | with app.status(f'Removing {name}'): 36 | manager.remove(dist) 37 | -------------------------------------------------------------------------------- /src/hatch/cli/python/show.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command(short_help='Show the available Python distributions') 12 | @click.option('--ascii', 'force_ascii', is_flag=True, help='Whether or not to only use ASCII characters') 13 | @click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') 14 | @click.pass_obj 15 | def show(app: Application, *, force_ascii: bool, directory: str | None): 16 | """Show the available Python distributions.""" 17 | from hatch.python.resolve import get_compatible_distributions 18 | 19 | manager = app.get_python_manager(directory) 20 | installed = manager.get_installed() 21 | 22 | installed_columns: dict[str, dict[int, str]] = {'Name': {}, 'Version': {}, 'Status': {}} 23 | for i, (name, installed_dist) in enumerate(installed.items()): 24 | installed_columns['Name'][i] = name 25 | installed_columns['Version'][i] = installed_dist.version 26 | if installed_dist.needs_update(): 27 | installed_columns['Status'][i] = 'Update available' 28 | 29 | available_columns: dict[str, dict[int, str]] = {'Name': {}, 'Version': {}} 30 | for i, (name, dist) in enumerate(get_compatible_distributions().items()): 31 | if name in installed: 32 | continue 33 | 34 | available_columns['Name'][i] = name 35 | available_columns['Version'][i] = dist.version.base_version 36 | 37 | app.display_table('Installed', installed_columns, show_lines=True, force_ascii=force_ascii) 38 | app.display_table('Available', available_columns, show_lines=True, force_ascii=force_ascii) 39 | -------------------------------------------------------------------------------- /src/hatch/cli/python/update.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | from hatch.cli.python.install import install 8 | 9 | if TYPE_CHECKING: 10 | from hatch.cli.application import Application 11 | 12 | 13 | @click.command(short_help='Update Python distributions') 14 | @click.argument('names', required=True, nargs=-1) 15 | @click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') 16 | @click.pass_context 17 | def update(ctx: click.Context, *, names: tuple[str, ...], directory: str | None): 18 | """ 19 | Update Python distributions. 20 | 21 | You may select `all` to update all installed distributions: 22 | 23 | \b 24 | ``` 25 | hatch python update all 26 | ``` 27 | """ 28 | app: Application = ctx.obj 29 | 30 | manager = app.get_python_manager(directory) 31 | installed = manager.get_installed() 32 | selection = tuple(installed) if 'all' in names else names 33 | 34 | not_installed = [name for name in selection if name not in installed] 35 | if not_installed: 36 | app.abort(f'Distributions not installed: {", ".join(not_installed)}') 37 | 38 | ctx.invoke(install, names=selection, directory=directory, private=True, update=True) 39 | -------------------------------------------------------------------------------- /src/hatch/cli/self/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import click 4 | 5 | from hatch.cli.self.report import report 6 | 7 | __management_command = os.environ.get('PYAPP_COMMAND_NAME', 'self') 8 | 9 | 10 | @click.group(name=__management_command, short_help='Manage Hatch') 11 | def self_command(): 12 | pass 13 | 14 | 15 | self_command.add_command(report) 16 | 17 | if __management_command: 18 | from hatch.cli.self.restore import restore 19 | from hatch.cli.self.update import update 20 | 21 | self_command.add_command(restore) 22 | self_command.add_command(update) 23 | -------------------------------------------------------------------------------- /src/hatch/cli/self/restore.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command( 12 | short_help='Restore the installation', context_settings={'help_option_names': [], 'ignore_unknown_options': True} 13 | ) 14 | @click.argument('args', nargs=-1) 15 | @click.pass_obj 16 | def restore(app: Application, *, args: tuple[str, ...]): # noqa: ARG001 17 | app.abort('Hatch is not installed as a binary') 18 | -------------------------------------------------------------------------------- /src/hatch/cli/self/update.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import click 6 | 7 | if TYPE_CHECKING: 8 | from hatch.cli.application import Application 9 | 10 | 11 | @click.command( 12 | short_help='Install the latest version', context_settings={'help_option_names': [], 'ignore_unknown_options': True} 13 | ) 14 | @click.argument('args', nargs=-1) 15 | @click.pass_obj 16 | def update(app: Application, *, args: tuple[str, ...]): # noqa: ARG001 17 | app.abort('Hatch is not installed as a binary') 18 | -------------------------------------------------------------------------------- /src/hatch/cli/status/__init__.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | 4 | @click.command(short_help='Show information about the current environment') 5 | @click.pass_obj 6 | def status(app): 7 | """Show information about the current environment.""" 8 | 9 | def display_pair(key, value, display_func=None, link=None): 10 | app.display_info('[', end='') 11 | app.display_success(key, end='') 12 | app.display_info(']', end='') 13 | app.display_info(' - ', end='') 14 | (display_func or app.display_info)(value, link=link) 15 | 16 | if app.project.root is None: 17 | if app.project.chosen_name is None: 18 | display_pair('Project', '', app.display_warning) 19 | else: 20 | display_pair('Project', f'{app.project.chosen_name} (not a project)', app.display_warning) 21 | elif app.project.chosen_name is None: 22 | display_pair('Project', f'{app.project.root.name} (current directory)') 23 | else: 24 | display_pair('Project', app.project.chosen_name) 25 | 26 | display_pair('Location', str(app.project.location)) 27 | display_pair('Config', str(app.config_file.path), link=f'file:///{app.config_file.path}') 28 | -------------------------------------------------------------------------------- /src/hatch/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/config/__init__.py -------------------------------------------------------------------------------- /src/hatch/config/constants.py: -------------------------------------------------------------------------------- 1 | class AppEnvVars: 2 | ENV = 'HATCH_ENV' 3 | ENV_ACTIVE = 'HATCH_ENV_ACTIVE' 4 | ENV_OPTION_PREFIX = 'HATCH_ENV_TYPE_' 5 | QUIET = 'HATCH_QUIET' 6 | VERBOSE = 'HATCH_VERBOSE' 7 | INTERACTIVE = 'HATCH_INTERACTIVE' 8 | PYTHON = 'HATCH_PYTHON' 9 | # https://no-color.org 10 | NO_COLOR = 'NO_COLOR' 11 | FORCE_COLOR = 'FORCE_COLOR' 12 | 13 | 14 | class ConfigEnvVars: 15 | PROJECT = 'HATCH_PROJECT' 16 | DATA = 'HATCH_DATA_DIR' 17 | CACHE = 'HATCH_CACHE_DIR' 18 | CONFIG = 'HATCH_CONFIG' 19 | 20 | 21 | class PublishEnvVars: 22 | USER = 'HATCH_INDEX_USER' 23 | AUTH = 'HATCH_INDEX_AUTH' 24 | REPO = 'HATCH_INDEX_REPO' 25 | CA_CERT = 'HATCH_INDEX_CA_CERT' 26 | CLIENT_CERT = 'HATCH_INDEX_CLIENT_CERT' 27 | CLIENT_KEY = 'HATCH_INDEX_CLIENT_KEY' 28 | PUBLISHER = 'HATCH_PUBLISHER' 29 | OPTIONS = 'HATCH_PUBLISHER_OPTIONS' 30 | 31 | 32 | class PythonEnvVars: 33 | CUSTOM_SOURCE_PREFIX = 'HATCH_PYTHON_CUSTOM_SOURCE_' 34 | CUSTOM_PATH_PREFIX = 'HATCH_PYTHON_CUSTOM_PATH_' 35 | CUSTOM_VERSION_PREFIX = 'HATCH_PYTHON_CUSTOM_VERSION_' 36 | 37 | 38 | class VersionEnvVars: 39 | VALIDATE_BUMP = 'HATCH_VERSION_VALIDATE_BUMP' 40 | -------------------------------------------------------------------------------- /src/hatch/config/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | import tomlkit 6 | 7 | if TYPE_CHECKING: 8 | from tomlkit.items import InlineTable 9 | from tomlkit.toml_document import TOMLDocument 10 | 11 | from hatch.utils.fs import Path 12 | 13 | 14 | def save_toml_document(document: TOMLDocument, path: Path): 15 | path.ensure_parent_dir_exists() 16 | path.write_atomic(tomlkit.dumps(document), 'w', encoding='utf-8') 17 | 18 | 19 | def create_toml_document(config: dict) -> InlineTable: 20 | return tomlkit.item(config) 21 | -------------------------------------------------------------------------------- /src/hatch/dep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/dep/__init__.py -------------------------------------------------------------------------------- /src/hatch/env/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/env/__init__.py -------------------------------------------------------------------------------- /src/hatch/env/collectors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/env/collectors/__init__.py -------------------------------------------------------------------------------- /src/hatch/env/collectors/custom.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | from typing import Any 5 | 6 | from hatch.env.collectors.plugin.interface import EnvironmentCollectorInterface 7 | from hatch.plugin.constants import DEFAULT_CUSTOM_SCRIPT 8 | from hatch.plugin.utils import load_plugin_from_script 9 | 10 | 11 | class CustomEnvironmentCollector: 12 | PLUGIN_NAME = 'custom' 13 | 14 | def __new__( # type: ignore[misc] 15 | cls, 16 | root: str, 17 | config: dict[str, Any], 18 | *args: Any, 19 | **kwargs: Any, 20 | ) -> EnvironmentCollectorInterface: 21 | custom_script = config.get('path', DEFAULT_CUSTOM_SCRIPT) 22 | if not isinstance(custom_script, str): 23 | message = f'Option `path` for environment collector `{cls.PLUGIN_NAME}` must be a string' 24 | raise TypeError(message) 25 | 26 | if not custom_script: 27 | message = f'Option `path` for environment collector `{cls.PLUGIN_NAME}` must not be empty if defined' 28 | raise ValueError(message) 29 | 30 | path = os.path.normpath(os.path.join(root, custom_script)) 31 | if not os.path.isfile(path): 32 | message = f'Plugin script does not exist: {custom_script}' 33 | raise OSError(message) 34 | 35 | hook_class = load_plugin_from_script( 36 | path, custom_script, EnvironmentCollectorInterface, 'environment_collector' 37 | ) 38 | hook = hook_class(root, config, *args, **kwargs) 39 | 40 | # Always keep the name to avoid confusion 41 | hook.PLUGIN_NAME = cls.PLUGIN_NAME 42 | 43 | return hook 44 | -------------------------------------------------------------------------------- /src/hatch/env/collectors/default.py: -------------------------------------------------------------------------------- 1 | from hatch.env.collectors.plugin.interface import EnvironmentCollectorInterface 2 | from hatch.env.internal import get_internal_env_config 3 | from hatch.env.utils import ensure_valid_environment 4 | 5 | 6 | class DefaultEnvironmentCollector(EnvironmentCollectorInterface): 7 | PLUGIN_NAME = 'default' 8 | 9 | def get_initial_config(self): # noqa: PLR6301 10 | default_config = {} 11 | ensure_valid_environment(default_config) 12 | 13 | return {'default': default_config, **get_internal_env_config()} 14 | -------------------------------------------------------------------------------- /src/hatch/env/collectors/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/env/collectors/plugin/__init__.py -------------------------------------------------------------------------------- /src/hatch/env/collectors/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | from hatch.env.collectors.custom import CustomEnvironmentCollector 6 | from hatch.env.collectors.default import DefaultEnvironmentCollector 7 | from hatchling.plugin import hookimpl 8 | 9 | if TYPE_CHECKING: 10 | from hatch.env.collectors.plugin.interface import EnvironmentCollectorInterface 11 | 12 | 13 | @hookimpl 14 | def hatch_register_environment_collector() -> list[type[EnvironmentCollectorInterface]]: 15 | return [CustomEnvironmentCollector, DefaultEnvironmentCollector] # type: ignore[list-item] 16 | -------------------------------------------------------------------------------- /src/hatch/env/internal/build.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | 5 | 6 | def get_default_config() -> dict[str, Any]: 7 | return { 8 | 'skip-install': True, 9 | 'builder': True, 10 | 'installer': 'uv', 11 | } 12 | -------------------------------------------------------------------------------- /src/hatch/env/internal/static_analysis.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | 5 | 6 | def get_default_config() -> dict[str, Any]: 7 | return { 8 | 'skip-install': True, 9 | 'installer': 'uv', 10 | 'dependencies': [f'ruff=={RUFF_DEFAULT_VERSION}'], 11 | 'scripts': { 12 | 'format-check': 'ruff format{env:HATCH_FMT_ARGS:} --check --diff {args:.}', 13 | 'format-fix': 'ruff format{env:HATCH_FMT_ARGS:} {args:.}', 14 | 'lint-check': 'ruff check{env:HATCH_FMT_ARGS:} {args:.}', 15 | 'lint-fix': 'ruff check{env:HATCH_FMT_ARGS:} --fix {args:.}', 16 | }, 17 | } 18 | 19 | 20 | RUFF_DEFAULT_VERSION: str = '0.6.8' 21 | -------------------------------------------------------------------------------- /src/hatch/env/internal/test.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | 5 | 6 | def get_default_config() -> dict[str, Any]: 7 | return { 8 | 'installer': 'uv', 9 | 'dependencies': [ 10 | 'coverage-enable-subprocess==1.0', 11 | 'coverage[toml]~=7.4', 12 | 'pytest~=8.1', 13 | 'pytest-mock~=3.12', 14 | 'pytest-randomly~=3.15', 15 | 'pytest-rerunfailures~=14.0', 16 | 'pytest-xdist[psutil]~=3.5', 17 | ], 18 | 'scripts': { 19 | 'run': 'pytest{env:HATCH_TEST_ARGS:} {args}', 20 | 'run-cov': 'coverage run -m pytest{env:HATCH_TEST_ARGS:} {args}', 21 | 'cov-combine': 'coverage combine', 22 | 'cov-report': 'coverage report', 23 | }, 24 | 'matrix': [{'python': ['3.13', '3.12', '3.11', '3.10', '3.9', '3.8']}], 25 | } 26 | -------------------------------------------------------------------------------- /src/hatch/env/internal/uv.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | 5 | 6 | def get_default_config() -> dict[str, Any]: 7 | return { 8 | 'skip-install': True, 9 | 'installer': 'uv', 10 | } 11 | -------------------------------------------------------------------------------- /src/hatch/env/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/env/plugin/__init__.py -------------------------------------------------------------------------------- /src/hatch/env/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from hatch.env.system import SystemEnvironment 2 | from hatch.env.virtual import VirtualEnvironment 3 | from hatchling.plugin import hookimpl 4 | 5 | 6 | @hookimpl 7 | def hatch_register_environment(): 8 | return [SystemEnvironment, VirtualEnvironment] 9 | -------------------------------------------------------------------------------- /src/hatch/env/system.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from hatch.env.plugin.interface import EnvironmentInterface 4 | from hatch.utils.env import PythonInfo 5 | 6 | 7 | class SystemEnvironment(EnvironmentInterface): 8 | PLUGIN_NAME = 'system' 9 | 10 | def __init__(self, *args, **kwargs): 11 | super().__init__(*args, **kwargs) 12 | 13 | self.python_info = PythonInfo(self.platform) 14 | self.install_indicator = self.data_directory / str(self.root).encode('utf-8').hex() 15 | 16 | def find(self): 17 | return os.path.dirname(os.path.dirname(self.system_python)) 18 | 19 | def create(self): 20 | self.install_indicator.touch() 21 | 22 | def remove(self): 23 | self.install_indicator.remove() 24 | 25 | def exists(self): 26 | return self.install_indicator.is_file() 27 | 28 | def install_project(self): 29 | self.platform.check_command(self.construct_pip_install_command([self.apply_features(str(self.root))])) 30 | 31 | def install_project_dev_mode(self): 32 | self.platform.check_command( 33 | self.construct_pip_install_command(['--editable', self.apply_features(str(self.root))]) 34 | ) 35 | 36 | def dependencies_in_sync(self): 37 | if not self.dependencies: 38 | return True 39 | 40 | from hatch.dep.sync import dependencies_in_sync 41 | 42 | return dependencies_in_sync( 43 | self.dependencies_complex, sys_path=self.python_info.sys_path, environment=self.python_info.environment 44 | ) 45 | 46 | def sync_dependencies(self): 47 | self.platform.check_command(self.construct_pip_install_command(self.dependencies)) 48 | -------------------------------------------------------------------------------- /src/hatch/env/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | 5 | from hatch.config.constants import AppEnvVars 6 | 7 | 8 | def get_env_var(*, plugin_name: str, option: str) -> str: 9 | return f'{AppEnvVars.ENV_OPTION_PREFIX}{plugin_name}_{option.replace("-", "_")}'.upper() 10 | 11 | 12 | def get_env_var_option(*, plugin_name: str, option: str, default: str = '') -> str: 13 | return os.environ.get(get_env_var(plugin_name=plugin_name, option=option), default) 14 | 15 | 16 | def ensure_valid_environment(env_config: dict): 17 | env_config.setdefault('type', 'virtual') 18 | 19 | 20 | def get_verbosity_flag(verbosity: int, *, adjustment=0) -> str: 21 | verbosity += adjustment 22 | if not verbosity: 23 | return '' 24 | 25 | if verbosity > 0: 26 | return f'-{"v" * abs(min(verbosity, 3))}' 27 | 28 | return f'-{"q" * abs(max(verbosity, -3))}' 29 | 30 | 31 | def add_verbosity_flag(command: list[str], verbosity: int, *, adjustment=0): 32 | flag = get_verbosity_flag(verbosity, adjustment=adjustment) 33 | if flag: 34 | command.append(flag) 35 | -------------------------------------------------------------------------------- /src/hatch/errors/__init__.py: -------------------------------------------------------------------------------- 1 | class HatchError(Exception): 2 | pass 3 | 4 | 5 | class PythonDistributionUnknownError(HatchError): 6 | pass 7 | 8 | 9 | class PythonDistributionResolutionError(HatchError): 10 | pass 11 | -------------------------------------------------------------------------------- /src/hatch/index/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/index/__init__.py -------------------------------------------------------------------------------- /src/hatch/index/errors.py: -------------------------------------------------------------------------------- 1 | class ArtifactMetadataError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /src/hatch/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/plugin/__init__.py -------------------------------------------------------------------------------- /src/hatch/plugin/constants.py: -------------------------------------------------------------------------------- 1 | DEFAULT_CUSTOM_SCRIPT = 'hatch_plugins.py' 2 | -------------------------------------------------------------------------------- /src/hatch/plugin/manager.py: -------------------------------------------------------------------------------- 1 | from hatchling.plugin.manager import PluginManager as _PluginManager 2 | 3 | 4 | class PluginManager(_PluginManager): 5 | def initialize(self): 6 | super().initialize() 7 | 8 | from hatch.plugin import specs 9 | 10 | self.manager.add_hookspecs(specs) 11 | 12 | def hatch_register_environment(self): 13 | from hatch.env.plugin import hooks 14 | 15 | self.manager.register(hooks) 16 | 17 | def hatch_register_environment_collector(self): 18 | from hatch.env.collectors.plugin import hooks 19 | 20 | self.manager.register(hooks) 21 | 22 | def hatch_register_publisher(self): 23 | from hatch.publish.plugin import hooks 24 | 25 | self.manager.register(hooks) 26 | 27 | def hatch_register_template(self): 28 | from hatch.template.plugin import hooks 29 | 30 | self.manager.register(hooks) 31 | -------------------------------------------------------------------------------- /src/hatch/plugin/specs.py: -------------------------------------------------------------------------------- 1 | from hatchling.plugin.specs import hookspec 2 | 3 | 4 | @hookspec 5 | def hatch_register_environment(): 6 | """Register new classes that adhere to the environment interface.""" 7 | 8 | 9 | @hookspec 10 | def hatch_register_environment_collector(): 11 | """Register new classes that adhere to the environment collector interface.""" 12 | 13 | 14 | @hookspec 15 | def hatch_register_version_scheme(): 16 | """Register new classes that adhere to the version scheme interface.""" 17 | 18 | 19 | @hookspec 20 | def hatch_register_publisher(): 21 | """Register new classes that adhere to the publisher interface.""" 22 | 23 | 24 | @hookspec 25 | def hatch_register_template(): 26 | """Register new classes that adhere to the template interface.""" 27 | -------------------------------------------------------------------------------- /src/hatch/plugin/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | if TYPE_CHECKING: 6 | from hatch.env.collectors.plugin.interface import EnvironmentCollectorInterface 7 | 8 | 9 | def load_plugin_from_script( 10 | path: str, script_name: str, plugin_class: type[EnvironmentCollectorInterface], plugin_id: str 11 | ) -> type[EnvironmentCollectorInterface]: 12 | from importlib.util import module_from_spec, spec_from_file_location 13 | 14 | spec = spec_from_file_location(script_name, path) 15 | module = module_from_spec(spec) # type: ignore[arg-type] 16 | spec.loader.exec_module(module) # type: ignore[union-attr] 17 | 18 | plugin_finder = f'get_{plugin_id}' 19 | names = dir(module) 20 | if plugin_finder in names: 21 | return getattr(module, plugin_finder)() 22 | 23 | subclasses = [] 24 | for name in names: 25 | obj = getattr(module, name) 26 | if obj is plugin_class: 27 | continue 28 | 29 | try: 30 | if issubclass(obj, plugin_class): 31 | subclasses.append(obj) 32 | except TypeError: 33 | continue 34 | 35 | if not subclasses: 36 | message = f'Unable to find a subclass of `{plugin_class.__name__}` in `{script_name}`: {path}' 37 | raise ValueError(message) 38 | 39 | if len(subclasses) > 1: 40 | message = ( 41 | f'Multiple subclasses of `{plugin_class.__name__}` found in `{script_name}`, ' 42 | f'select one by defining a function named `{plugin_finder}`: {path}' 43 | ) 44 | raise ValueError(message) 45 | 46 | return subclasses[0] 47 | -------------------------------------------------------------------------------- /src/hatch/project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/project/__init__.py -------------------------------------------------------------------------------- /src/hatch/project/constants.py: -------------------------------------------------------------------------------- 1 | BUILD_BACKEND = 'hatchling.build' 2 | DEFAULT_BUILD_DIRECTORY = 'dist' 3 | DEFAULT_BUILD_SCRIPT = 'hatch_build.py' 4 | DEFAULT_CONFIG_FILE = 'hatch.toml' 5 | 6 | 7 | class BuildEnvVars: 8 | LOCATION = 'HATCH_BUILD_LOCATION' 9 | HOOKS_ONLY = 'HATCH_BUILD_HOOKS_ONLY' 10 | NO_HOOKS = 'HATCH_BUILD_NO_HOOKS' 11 | HOOKS_ENABLE = 'HATCH_BUILD_HOOKS_ENABLE' 12 | HOOK_ENABLE_PREFIX = 'HATCH_BUILD_HOOK_ENABLE_' 13 | CLEAN = 'HATCH_BUILD_CLEAN' 14 | CLEAN_HOOKS_AFTER = 'HATCH_BUILD_CLEAN_HOOKS_AFTER' 15 | -------------------------------------------------------------------------------- /src/hatch/project/frontend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/project/frontend/__init__.py -------------------------------------------------------------------------------- /src/hatch/project/frontend/scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/project/frontend/scripts/__init__.py -------------------------------------------------------------------------------- /src/hatch/project/frontend/scripts/build_deps.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json 4 | import os 5 | 6 | from hatchling.bridge.app import Application 7 | from hatchling.metadata.core import ProjectMetadata 8 | from hatchling.plugin.manager import PluginManager 9 | 10 | RUNNER: dict = {} 11 | 12 | 13 | def main() -> None: 14 | project_root: str = RUNNER['project_root'] 15 | output_dir: str = RUNNER['output_dir'] 16 | targets: list[str] = RUNNER['targets'] 17 | 18 | app = Application() 19 | plugin_manager = PluginManager() 20 | metadata = ProjectMetadata(project_root, plugin_manager) 21 | 22 | dependencies: dict[str, None] = {} 23 | for target_name in targets: 24 | builder_class = plugin_manager.builder.get(target_name) 25 | if builder_class is None: 26 | continue 27 | 28 | builder = builder_class( 29 | project_root, plugin_manager=plugin_manager, metadata=metadata, app=app.get_safe_application() 30 | ) 31 | for dependency in builder.config.dependencies: 32 | dependencies[dependency] = None 33 | 34 | output = json.dumps(list(dependencies)) 35 | with open(os.path.join(output_dir, 'output.json'), 'w', encoding='utf-8') as f: 36 | f.write(output) 37 | 38 | 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /src/hatch/project/frontend/scripts/core_metadata.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json 4 | import os 5 | 6 | from hatchling.metadata.core import ProjectMetadata 7 | from hatchling.metadata.utils import resolve_metadata_fields 8 | from hatchling.plugin.manager import PluginManager 9 | 10 | RUNNER: dict = {} 11 | 12 | 13 | def main() -> None: 14 | project_root: str = RUNNER['project_root'] 15 | output_dir: str = RUNNER['output_dir'] 16 | 17 | project_metadata = ProjectMetadata(project_root, PluginManager()) 18 | core_metadata = resolve_metadata_fields(project_metadata) 19 | for key, value in list(core_metadata.items()): 20 | if not value: 21 | core_metadata.pop(key) 22 | 23 | output = json.dumps(core_metadata) 24 | with open(os.path.join(output_dir, 'output.json'), 'w', encoding='utf-8') as f: 25 | f.write(output) 26 | 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /src/hatch/project/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any, Iterable 4 | 5 | 6 | def parse_script_command(command: str) -> tuple[str, str, bool]: 7 | possible_script, _, args = command.partition(' ') 8 | if possible_script == '-': 9 | ignore_exit_code = True 10 | possible_script, _, args = args.partition(' ') 11 | else: 12 | ignore_exit_code = False 13 | 14 | return possible_script, args, ignore_exit_code 15 | 16 | 17 | def format_script_commands(*, commands: list[str], args: str, ignore_exit_code: bool) -> Iterable[str]: 18 | for command in commands: 19 | if args: 20 | yield f'{command} {args}' 21 | elif ignore_exit_code and not command.startswith('- '): 22 | yield f'- {command}' 23 | else: 24 | yield command 25 | 26 | 27 | def parse_inline_script_metadata(script: str) -> dict[str, Any] | None: 28 | """ 29 | https://peps.python.org/pep-0723/#reference-implementation 30 | """ 31 | import re 32 | 33 | from hatch.utils.toml import load_toml_data 34 | 35 | block_type = 'script' 36 | pattern = re.compile(r'(?m)^# /// (?P[a-zA-Z0-9-]+)$\s(?P(^#(| .*)$\s)+)^# ///$') 37 | matches = list(filter(lambda m: m.group('type') == block_type, pattern.finditer(script))) 38 | if len(matches) > 1: 39 | message = f'Multiple inline metadata blocks found for type: {block_type}' 40 | raise ValueError(message) 41 | 42 | if len(matches) == 1: 43 | content = ''.join( 44 | line[2:] if line.startswith('# ') else line[1:] 45 | for line in matches[0].group('content').splitlines(keepends=True) 46 | ) 47 | return load_toml_data(content) 48 | 49 | return None 50 | -------------------------------------------------------------------------------- /src/hatch/publish/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/publish/__init__.py -------------------------------------------------------------------------------- /src/hatch/publish/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/publish/plugin/__init__.py -------------------------------------------------------------------------------- /src/hatch/publish/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from hatch.publish.index import IndexPublisher 2 | from hatchling.plugin import hookimpl 3 | 4 | 5 | @hookimpl 6 | def hatch_register_publisher(): 7 | return IndexPublisher 8 | -------------------------------------------------------------------------------- /src/hatch/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/py.typed -------------------------------------------------------------------------------- /src/hatch/python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/python/__init__.py -------------------------------------------------------------------------------- /src/hatch/template/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from contextlib import suppress 4 | from typing import TYPE_CHECKING 5 | 6 | if TYPE_CHECKING: 7 | from hatch.utils.fs import Path 8 | 9 | 10 | class File: 11 | def __init__(self, path: Path | None, contents: str = ''): 12 | self.path = path 13 | self.contents = contents 14 | self.feature = None 15 | 16 | def write(self, root): 17 | if self.path is None: # no cov 18 | return 19 | 20 | path = root / self.path 21 | path.ensure_parent_dir_exists() 22 | path.write_text(self.contents, encoding='utf-8') 23 | 24 | 25 | def find_template_files(module): 26 | for name in dir(module): 27 | obj = getattr(module, name) 28 | if obj is File: 29 | continue 30 | 31 | with suppress(TypeError): 32 | if issubclass(obj, File): 33 | yield obj 34 | -------------------------------------------------------------------------------- /src/hatch/template/files_feature_ci.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | 4 | 5 | class CommandLinePackage(File): 6 | TEMPLATE = """\ 7 | name: test 8 | 9 | on: 10 | push: 11 | branches: [main, master] 12 | pull_request: 13 | branches: [main, master] 14 | 15 | concurrency: 16 | group: test-${{ github.head_ref }} 17 | cancel-in-progress: true 18 | 19 | env: 20 | PYTHONUNBUFFERED: "1" 21 | FORCE_COLOR: "1" 22 | 23 | jobs: 24 | run: 25 | name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} 26 | runs-on: ${{ matrix.os }} 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | os: [ubuntu-latest, windows-latest, macos-latest] 31 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] 32 | 33 | steps: 34 | - uses: actions/checkout@v3 35 | 36 | - name: Set up Python ${{ matrix.python-version }} 37 | uses: actions/setup-python@v4 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | 41 | - name: Install Hatch 42 | run: pip install --upgrade hatch 43 | 44 | - name: Run static analysis 45 | run: hatch fmt --check 46 | 47 | - name: Run tests 48 | run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1 49 | """ 50 | 51 | def __init__( 52 | self, 53 | template_config: dict, # noqa: ARG002 54 | plugin_config: dict, # noqa: ARG002 55 | ): 56 | super().__init__(Path('.github', 'workflows', 'test.yml'), self.TEMPLATE) 57 | -------------------------------------------------------------------------------- /src/hatch/template/files_feature_cli.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | 4 | 5 | class PackageEntryPoint(File): 6 | TEMPLATE = """\ 7 | import sys 8 | 9 | if __name__ == "__main__": 10 | from {package_name}.cli import {package_name} 11 | 12 | sys.exit({package_name}()) 13 | """ 14 | 15 | def __init__( 16 | self, 17 | template_config: dict, 18 | plugin_config: dict, # noqa: ARG002 19 | ): 20 | super().__init__(Path(template_config['package_name'], '__main__.py'), self.TEMPLATE.format(**template_config)) 21 | 22 | 23 | class CommandLinePackage(File): 24 | TEMPLATE = """\ 25 | import click 26 | 27 | from {package_name}.__about__ import __version__ 28 | 29 | 30 | @click.group(context_settings={{"help_option_names": ["-h", "--help"]}}, invoke_without_command=True) 31 | @click.version_option(version=__version__, prog_name="{project_name}") 32 | def {package_name}(): 33 | click.echo("Hello world!") 34 | """ 35 | 36 | def __init__( 37 | self, 38 | template_config: dict, 39 | plugin_config: dict, # noqa: ARG002 40 | ): 41 | super().__init__( 42 | Path(template_config['package_name'], 'cli', '__init__.py'), self.TEMPLATE.format(**template_config) 43 | ) 44 | -------------------------------------------------------------------------------- /src/hatch/template/files_feature_tests.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | 4 | 5 | class TestsPackageRoot(File): 6 | def __init__( 7 | self, 8 | template_config: dict, # noqa: ARG002 9 | plugin_config: dict, # noqa: ARG002 10 | ): 11 | super().__init__(Path('tests', '__init__.py')) 12 | -------------------------------------------------------------------------------- /src/hatch/template/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/template/plugin/__init__.py -------------------------------------------------------------------------------- /src/hatch/template/plugin/hooks.py: -------------------------------------------------------------------------------- 1 | from hatch.template.default import DefaultTemplate 2 | from hatchling.plugin import hookimpl 3 | 4 | 5 | @hookimpl 6 | def hatch_register_template(): 7 | return DefaultTemplate 8 | -------------------------------------------------------------------------------- /src/hatch/template/plugin/interface.py: -------------------------------------------------------------------------------- 1 | class TemplateInterface: 2 | PLUGIN_NAME = '' 3 | PRIORITY = 100 4 | 5 | def __init__(self, plugin_config: dict, cache_dir, creation_time): 6 | self.plugin_config = plugin_config 7 | self.cache_dir = cache_dir 8 | self.creation_time = creation_time 9 | 10 | def initialize_config(self, config): 11 | """ 12 | Allow modification of the configuration passed to every file for new projects 13 | before the list of files are determined. 14 | """ 15 | 16 | def get_files(self, config): # noqa: ARG002, PLR6301 17 | """Add to the list of files for new projects that are written to the file system.""" 18 | return [] 19 | 20 | def finalize_files(self, config, files): 21 | """Allow modification of files for new projects before they are written to the file system.""" 22 | -------------------------------------------------------------------------------- /src/hatch/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/utils/__init__.py -------------------------------------------------------------------------------- /src/hatch/utils/ci.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def running_in_ci() -> bool: 5 | return any(os.environ.get(env_var) in {'true', '1'} for env_var in ('CI', 'GITHUB_ACTIONS')) 6 | -------------------------------------------------------------------------------- /src/hatch/utils/network.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import time 4 | from contextlib import contextmanager 5 | from typing import TYPE_CHECKING, Any, Generator 6 | 7 | if TYPE_CHECKING: 8 | import httpx 9 | 10 | from hatch.utils.fs import Path 11 | 12 | MINIMUM_SLEEP = 2 13 | MAXIMUM_SLEEP = 20 14 | # The timeout should be slightly larger than a multiple of 3, 15 | # which is the default TCP packet retransmission window. See: 16 | # https://tools.ietf.org/html/rfc2988 17 | DEFAULT_TIMEOUT = 10 18 | 19 | 20 | @contextmanager 21 | def streaming_response(*args: Any, **kwargs: Any) -> Generator[httpx.Response, None, None]: 22 | from secrets import choice 23 | 24 | import httpx 25 | 26 | attempts = 0 27 | while True: 28 | attempts += 1 29 | try: 30 | with httpx.stream(*args, **kwargs) as response: 31 | response.raise_for_status() 32 | yield response 33 | 34 | break 35 | except httpx.HTTPError: 36 | sleep = min(MAXIMUM_SLEEP, MINIMUM_SLEEP * 2**attempts) 37 | if sleep == MAXIMUM_SLEEP: 38 | raise 39 | 40 | time.sleep(choice(range(sleep + 1))) 41 | 42 | 43 | def download_file(path: Path, *args: Any, **kwargs: Any) -> None: 44 | kwargs.setdefault('timeout', DEFAULT_TIMEOUT) 45 | 46 | with path.open(mode='wb', buffering=0) as f, streaming_response('GET', *args, **kwargs) as response: 47 | for chunk in response.iter_bytes(16384): 48 | f.write(chunk) 49 | -------------------------------------------------------------------------------- /src/hatch/utils/structures.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | from fnmatch import fnmatch 5 | from typing import TYPE_CHECKING 6 | 7 | if TYPE_CHECKING: 8 | from types import TracebackType 9 | 10 | 11 | class EnvVars(dict): 12 | def __init__( 13 | self, env_vars: dict | None = None, include: list[str] | None = None, exclude: list[str] | None = None 14 | ) -> None: 15 | super().__init__(os.environ) 16 | self.old_env = dict(self) 17 | 18 | if include: 19 | self.clear() 20 | for env_var, value in self.old_env.items(): 21 | for pattern in include: 22 | if fnmatch(env_var, pattern): 23 | self[env_var] = value 24 | break 25 | 26 | if exclude: 27 | for env_var in list(self): 28 | for pattern in exclude: 29 | if fnmatch(env_var, pattern): 30 | self.pop(env_var) 31 | break 32 | 33 | if env_vars: 34 | self.update(env_vars) 35 | 36 | def __enter__(self) -> None: 37 | os.environ.clear() 38 | os.environ.update(self) 39 | 40 | def __exit__( 41 | self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None 42 | ) -> None: 43 | os.environ.clear() 44 | os.environ.update(self.old_env) 45 | -------------------------------------------------------------------------------- /src/hatch/utils/toml.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | from typing import Any 5 | 6 | if sys.version_info >= (3, 11): 7 | import tomllib 8 | else: 9 | import tomli as tomllib 10 | 11 | 12 | def load_toml_data(data: str) -> dict[str, Any]: 13 | return tomllib.loads(data) 14 | 15 | 16 | def load_toml_file(path: str) -> dict[str, Any]: 17 | with open(path, encoding='utf-8') as f: 18 | return tomllib.loads(f.read()) 19 | -------------------------------------------------------------------------------- /src/hatch/venv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/src/hatch/venv/__init__.py -------------------------------------------------------------------------------- /src/hatch/venv/utils.py: -------------------------------------------------------------------------------- 1 | from base64 import urlsafe_b64encode 2 | from os import urandom 3 | 4 | 5 | def get_random_venv_name(): 6 | # Will be length 4 7 | return urlsafe_b64encode(urandom(3)).decode('ascii') 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/__init__.py -------------------------------------------------------------------------------- /tests/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/__init__.py -------------------------------------------------------------------------------- /tests/backend/builders/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/builders/__init__.py -------------------------------------------------------------------------------- /tests/backend/builders/hooks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/builders/hooks/__init__.py -------------------------------------------------------------------------------- /tests/backend/builders/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/builders/plugin/__init__.py -------------------------------------------------------------------------------- /tests/backend/builders/utils.py: -------------------------------------------------------------------------------- 1 | from hatchling.builders.plugin.interface import BuilderInterface 2 | 3 | 4 | class MockBuilder(BuilderInterface): # no cov 5 | def get_version_api(self): 6 | return {} 7 | -------------------------------------------------------------------------------- /tests/backend/metadata/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/metadata/__init__.py -------------------------------------------------------------------------------- /tests/backend/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/utils/__init__.py -------------------------------------------------------------------------------- /tests/backend/utils/test_fs.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from hatchling.utils.fs import path_to_uri 4 | 5 | 6 | class TestPathToURI: 7 | def test_unix(self, isolation, uri_slash_prefix): 8 | bad_path = f'{isolation}{os.sep}' 9 | normalized_path = str(isolation).replace(os.sep, '/') 10 | assert path_to_uri(bad_path) == f'file:{uri_slash_prefix}{normalized_path}' 11 | 12 | def test_character_escaping(self, temp_dir, uri_slash_prefix): 13 | path = temp_dir / 'foo bar' 14 | normalized_path = str(path).replace(os.sep, '/').replace(' ', '%20') 15 | assert path_to_uri(path) == f'file:{uri_slash_prefix}{normalized_path}' 16 | -------------------------------------------------------------------------------- /tests/backend/version/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/version/__init__.py -------------------------------------------------------------------------------- /tests/backend/version/scheme/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/version/scheme/__init__.py -------------------------------------------------------------------------------- /tests/backend/version/source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/backend/version/source/__init__.py -------------------------------------------------------------------------------- /tests/backend/version/source/test_env.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from hatch.utils.structures import EnvVars 4 | from hatchling.version.source.env import EnvSource 5 | 6 | 7 | def test_no_variable(isolation): 8 | source = EnvSource(str(isolation), {}) 9 | 10 | with pytest.raises(ValueError, match='option `variable` must be specified'): 11 | source.get_version_data() 12 | 13 | 14 | def test_variable_not_string(isolation): 15 | source = EnvSource(str(isolation), {'variable': 1}) 16 | 17 | with pytest.raises(TypeError, match='option `variable` must be a string'): 18 | source.get_version_data() 19 | 20 | 21 | def test_variable_not_available(isolation): 22 | source = EnvSource(str(isolation), {'variable': 'ENV_VERSION'}) 23 | 24 | with EnvVars(exclude=['ENV_VERSION']), pytest.raises( 25 | RuntimeError, match='environment variable `ENV_VERSION` is not set' 26 | ): 27 | source.get_version_data() 28 | 29 | 30 | def test_variable_contains_version(isolation): 31 | source = EnvSource(str(isolation), {'variable': 'ENV_VERSION'}) 32 | 33 | with EnvVars({'ENV_VERSION': '0.0.1'}): 34 | assert source.get_version_data()['version'] == '0.0.1' 35 | -------------------------------------------------------------------------------- /tests/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/__init__.py -------------------------------------------------------------------------------- /tests/cli/build/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/build/__init__.py -------------------------------------------------------------------------------- /tests/cli/clean/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/clean/__init__.py -------------------------------------------------------------------------------- /tests/cli/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/config/__init__.py -------------------------------------------------------------------------------- /tests/cli/config/test_explore.py: -------------------------------------------------------------------------------- 1 | def test_call(hatch, config_file, mocker): 2 | mock = mocker.patch('click.launch') 3 | result = hatch('config', 'explore') 4 | 5 | assert result.exit_code == 0, result.output 6 | mock.assert_called_once_with(str(config_file.path), locate=True) 7 | -------------------------------------------------------------------------------- /tests/cli/config/test_find.py: -------------------------------------------------------------------------------- 1 | def test(hatch, config_file, helpers): 2 | result = hatch('config', 'find') 3 | 4 | assert result.exit_code == 0, result.output 5 | assert result.output == helpers.dedent( 6 | f""" 7 | {config_file.path} 8 | """ 9 | ) 10 | -------------------------------------------------------------------------------- /tests/cli/config/test_restore.py: -------------------------------------------------------------------------------- 1 | def test_standard(hatch, config_file): 2 | config_file.model.project = 'foo' 3 | config_file.save() 4 | 5 | result = hatch('config', 'restore') 6 | 7 | assert result.exit_code == 0, result.output 8 | assert result.output == 'Settings were successfully restored.\n' 9 | 10 | config_file.load() 11 | assert config_file.model.project == '' 12 | 13 | 14 | def test_allow_invalid_config(hatch, config_file, helpers): 15 | config_file.model.project = ['foo'] 16 | config_file.save() 17 | 18 | result = hatch('config', 'restore') 19 | 20 | assert result.exit_code == 0, result.output 21 | assert result.output == helpers.dedent( 22 | """ 23 | Settings were successfully restored. 24 | """ 25 | ) 26 | -------------------------------------------------------------------------------- /tests/cli/dep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/dep/__init__.py -------------------------------------------------------------------------------- /tests/cli/dep/show/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/dep/show/__init__.py -------------------------------------------------------------------------------- /tests/cli/env/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/env/__init__.py -------------------------------------------------------------------------------- /tests/cli/fmt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/fmt/__init__.py -------------------------------------------------------------------------------- /tests/cli/new/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/new/__init__.py -------------------------------------------------------------------------------- /tests/cli/project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/project/__init__.py -------------------------------------------------------------------------------- /tests/cli/publish/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/publish/__init__.py -------------------------------------------------------------------------------- /tests/cli/python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/python/__init__.py -------------------------------------------------------------------------------- /tests/cli/python/conftest.py: -------------------------------------------------------------------------------- 1 | import secrets 2 | 3 | import pytest 4 | 5 | from hatch.utils.shells import detect_shell 6 | 7 | 8 | @pytest.fixture(autouse=True) 9 | def default_shells(platform): 10 | return [] if platform.windows else [detect_shell(platform)[0]] 11 | 12 | 13 | @pytest.fixture(autouse=True) 14 | def isolated_python_directory(config_file): 15 | config_file.model.dirs.python = 'isolated' 16 | config_file.save() 17 | 18 | 19 | @pytest.fixture(autouse=True) 20 | def path_append(mocker): 21 | return mocker.patch('userpath.append') 22 | 23 | 24 | @pytest.fixture(autouse=True) 25 | def disable_path_detectors(mocker): 26 | mocker.patch('userpath.in_current_path', return_value=False) 27 | mocker.patch('userpath.in_new_path', return_value=False) 28 | 29 | 30 | @pytest.fixture 31 | def dist_name(compatible_python_distributions): 32 | return secrets.choice(compatible_python_distributions) 33 | -------------------------------------------------------------------------------- /tests/cli/python/test_find.py: -------------------------------------------------------------------------------- 1 | def test_not_installed(hatch, helpers): 2 | name = '3.10' 3 | result = hatch('python', 'find', name) 4 | 5 | assert result.exit_code == 1, result.output 6 | assert result.output == helpers.dedent( 7 | f""" 8 | Distribution not installed: {name} 9 | """ 10 | ) 11 | 12 | 13 | def test_binary(hatch, helpers, temp_dir_data, dist_name): 14 | install_dir = temp_dir_data / 'data' / 'pythons' 15 | dist = helpers.write_distribution(install_dir, dist_name) 16 | 17 | result = hatch('python', 'find', dist_name) 18 | 19 | assert result.exit_code == 0, result.output 20 | assert result.output == helpers.dedent( 21 | f""" 22 | {dist.python_path} 23 | """ 24 | ) 25 | 26 | 27 | def test_parent(hatch, helpers, temp_dir_data, dist_name): 28 | install_dir = temp_dir_data / 'data' / 'pythons' 29 | dist = helpers.write_distribution(install_dir, dist_name) 30 | 31 | result = hatch('python', 'find', dist_name, '--parent') 32 | 33 | assert result.exit_code == 0, result.output 34 | assert result.output == helpers.dedent( 35 | f""" 36 | {dist.python_path.parent} 37 | """ 38 | ) 39 | -------------------------------------------------------------------------------- /tests/cli/run/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/run/__init__.py -------------------------------------------------------------------------------- /tests/cli/self/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/self/__init__.py -------------------------------------------------------------------------------- /tests/cli/self/test_self.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def test(hatch): 5 | result = hatch(os.environ['PYAPP_COMMAND_NAME']) 6 | 7 | assert result.exit_code == 0, result.output 8 | -------------------------------------------------------------------------------- /tests/cli/status/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/status/__init__.py -------------------------------------------------------------------------------- /tests/cli/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/test/__init__.py -------------------------------------------------------------------------------- /tests/cli/test_root.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from hatch.config.constants import ConfigEnvVars 4 | from hatch.config.user import ConfigFile 5 | from hatch.utils.structures import EnvVars 6 | 7 | 8 | class TestFreshInstallation: 9 | INSTALL_MESSAGE = """\ 10 | No config file found, creating one with default settings now... 11 | Success! Please see `hatch config`. 12 | """ 13 | 14 | def test_config_file_creation_default(self, hatch): 15 | with EnvVars(): 16 | os.environ.pop(ConfigEnvVars.CONFIG, None) 17 | with ConfigFile.get_default_location().temp_hide(): 18 | result = hatch() 19 | assert self.INSTALL_MESSAGE not in result.output 20 | 21 | def test_config_file_creation_verbose(self, hatch): 22 | with EnvVars(): 23 | os.environ.pop(ConfigEnvVars.CONFIG, None) 24 | with ConfigFile.get_default_location().temp_hide(): 25 | result = hatch('-v') 26 | assert self.INSTALL_MESSAGE in result.output 27 | 28 | result = hatch('-v') 29 | assert self.INSTALL_MESSAGE not in result.output 30 | 31 | 32 | def test_no_subcommand_shows_help(hatch): 33 | assert hatch().output == hatch('--help').output 34 | 35 | 36 | def test_no_config_file(hatch, config_file): 37 | config_file.path.remove() 38 | result = hatch() 39 | 40 | assert result.exit_code == 1 41 | assert result.output == f'The selected config file `{config_file.path}` does not exist.\n' 42 | -------------------------------------------------------------------------------- /tests/cli/version/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/cli/version/__init__.py -------------------------------------------------------------------------------- /tests/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/config/__init__.py -------------------------------------------------------------------------------- /tests/dep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/dep/__init__.py -------------------------------------------------------------------------------- /tests/env/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/env/__init__.py -------------------------------------------------------------------------------- /tests/env/collectors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/env/collectors/__init__.py -------------------------------------------------------------------------------- /tests/env/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/env/plugin/__init__.py -------------------------------------------------------------------------------- /tests/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/helpers/__init__.py -------------------------------------------------------------------------------- /tests/helpers/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/helpers/templates/__init__.py -------------------------------------------------------------------------------- /tests/helpers/templates/new/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/helpers/templates/new/__init__.py -------------------------------------------------------------------------------- /tests/helpers/templates/new/feature_ci.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | 4 | from .default import get_files as get_template_files 5 | 6 | 7 | def get_files(**kwargs): 8 | files = [File(Path(f.path), f.contents) for f in get_template_files(**kwargs)] 9 | files.append( 10 | File( 11 | Path('.github', 'workflows', 'test.yml'), 12 | """\ 13 | name: test 14 | 15 | on: 16 | push: 17 | branches: [main, master] 18 | pull_request: 19 | branches: [main, master] 20 | 21 | concurrency: 22 | group: test-${{ github.head_ref }} 23 | cancel-in-progress: true 24 | 25 | env: 26 | PYTHONUNBUFFERED: "1" 27 | FORCE_COLOR: "1" 28 | 29 | jobs: 30 | run: 31 | name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} 32 | runs-on: ${{ matrix.os }} 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | os: [ubuntu-latest, windows-latest, macos-latest] 37 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | 42 | - name: Set up Python ${{ matrix.python-version }} 43 | uses: actions/setup-python@v4 44 | with: 45 | python-version: ${{ matrix.python-version }} 46 | 47 | - name: Install Hatch 48 | run: pip install --upgrade hatch 49 | 50 | - name: Run static analysis 51 | run: hatch fmt --check 52 | 53 | - name: Run tests 54 | run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1 55 | """, 56 | ) 57 | ) 58 | 59 | return files 60 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/helpers/templates/sdist/__init__.py -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_default.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | 5 | from ..new.feature_no_src_layout import get_files as get_template_files 6 | 7 | 8 | def get_files(**kwargs): 9 | relative_root = kwargs.get('relative_root', '') 10 | 11 | files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] 12 | files.append( 13 | File( 14 | Path(relative_root, 'PKG-INFO'), 15 | f"""\ 16 | Metadata-Version: {DEFAULT_METADATA_VERSION} 17 | Name: {kwargs['project_name']} 18 | Version: 0.0.1 19 | License-File: LICENSE.txt 20 | """, 21 | ) 22 | ) 23 | 24 | return files 25 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_default_build_script_artifacts.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | 8 | 9 | def get_files(**kwargs): 10 | relative_root = kwargs.get('relative_root', '') 11 | 12 | files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] 13 | files.extend(( 14 | File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), 15 | File( 16 | Path(relative_root, '.gitignore'), 17 | """\ 18 | *.pyc 19 | *.so 20 | *.h 21 | """, 22 | ), 23 | File( 24 | Path(relative_root, DEFAULT_BUILD_SCRIPT), 25 | """\ 26 | import pathlib 27 | 28 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 29 | 30 | class CustomHook(BuildHookInterface): 31 | def initialize(self, version, build_data): 32 | pathlib.Path('my_app', 'lib.so').touch() 33 | pathlib.Path('my_app', 'lib.h').touch() 34 | """, 35 | ), 36 | File( 37 | Path(relative_root, 'PKG-INFO'), 38 | f"""\ 39 | Metadata-Version: {DEFAULT_METADATA_VERSION} 40 | Name: {kwargs['project_name']} 41 | Version: 0.0.1 42 | License-File: LICENSE.txt 43 | """, 44 | ), 45 | )) 46 | 47 | return files 48 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_default_build_script_extra_dependencies.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | 8 | 9 | def get_files(**kwargs): 10 | relative_root = kwargs.get('relative_root', '') 11 | 12 | files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] 13 | files.extend(( 14 | File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), 15 | File( 16 | Path(relative_root, '.gitignore'), 17 | """\ 18 | *.pyc 19 | *.so 20 | *.h 21 | """, 22 | ), 23 | File( 24 | Path(relative_root, DEFAULT_BUILD_SCRIPT), 25 | """\ 26 | import pathlib 27 | 28 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 29 | 30 | class CustomHook(BuildHookInterface): 31 | def initialize(self, version, build_data): 32 | pathlib.Path('my_app', 'lib.so').touch() 33 | pathlib.Path('my_app', 'lib.h').touch() 34 | build_data['dependencies'].append('binary') 35 | """, 36 | ), 37 | File( 38 | Path(relative_root, 'PKG-INFO'), 39 | f"""\ 40 | Metadata-Version: {DEFAULT_METADATA_VERSION} 41 | Name: {kwargs['project_name']} 42 | Version: 0.0.1 43 | License-File: LICENSE.txt 44 | Requires-Dist: binary 45 | """, 46 | ), 47 | )) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_default_support_legacy.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | 5 | from ..new.feature_no_src_layout import get_files as get_template_files 6 | 7 | 8 | def get_files(**kwargs): 9 | relative_root = kwargs.get('relative_root', '') 10 | 11 | files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] 12 | files.extend(( 13 | File( 14 | Path(relative_root, 'PKG-INFO'), 15 | f"""\ 16 | Metadata-Version: {DEFAULT_METADATA_VERSION} 17 | Name: {kwargs['project_name']} 18 | Version: 0.0.1 19 | License-File: LICENSE.txt 20 | """, 21 | ), 22 | File( 23 | Path(relative_root, 'setup.py'), 24 | f"""\ 25 | # -*- coding: utf-8 -*- 26 | from setuptools import setup 27 | 28 | setup( 29 | name='{kwargs['project_name_normalized']}', 30 | version='0.0.1', 31 | packages=[ 32 | '{kwargs['package_name']}', 33 | 'tests', 34 | ], 35 | ) 36 | """, 37 | ), 38 | )) 39 | 40 | return files 41 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_default_vcs_git_exclusion_files.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | 5 | from ..new.feature_no_src_layout import get_files as get_template_files 6 | 7 | 8 | def get_files(**kwargs): 9 | relative_root = kwargs.get('relative_root', '') 10 | 11 | files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] 12 | files.extend(( 13 | File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), 14 | File( 15 | Path(relative_root, '.gitignore'), 16 | """\ 17 | *.pyc 18 | *.so 19 | *.h 20 | """, 21 | ), 22 | File( 23 | Path(relative_root, 'PKG-INFO'), 24 | f"""\ 25 | Metadata-Version: {DEFAULT_METADATA_VERSION} 26 | Name: {kwargs['project_name']} 27 | Version: 0.0.1 28 | License-File: LICENSE.txt 29 | """, 30 | ), 31 | )) 32 | 33 | return files 34 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_default_vcs_mercurial_exclusion_files.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | 5 | from ..new.feature_no_src_layout import get_files as get_template_files 6 | 7 | 8 | def get_files(**kwargs): 9 | relative_root = kwargs.get('relative_root', '') 10 | 11 | files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] 12 | files.extend(( 13 | File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), 14 | File( 15 | Path(relative_root, '.hgignore'), 16 | """\ 17 | syntax: glob 18 | *.pyc 19 | 20 | syntax: foo 21 | README.md 22 | 23 | syntax: glob 24 | *.so 25 | *.h 26 | """, 27 | ), 28 | File( 29 | Path(relative_root, 'PKG-INFO'), 30 | f"""\ 31 | Metadata-Version: {DEFAULT_METADATA_VERSION} 32 | Name: {kwargs['project_name']} 33 | Version: 0.0.1 34 | License-File: LICENSE.txt 35 | """, 36 | ), 37 | )) 38 | 39 | return files 40 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_include.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | 5 | from ..new.feature_no_src_layout import get_files as get_template_files 6 | 7 | 8 | def get_files(**kwargs): 9 | relative_root = kwargs.get('relative_root', '') 10 | 11 | files = [] 12 | for f in get_template_files(**kwargs): 13 | part = f.path.parts[0] 14 | if part in {'my_app', 'pyproject.toml', 'README.md', 'LICENSE.txt'}: 15 | files.append(File(Path(relative_root, f.path), f.contents)) 16 | 17 | files.append( 18 | File( 19 | Path(relative_root, 'PKG-INFO'), 20 | f"""\ 21 | Metadata-Version: {DEFAULT_METADATA_VERSION} 22 | Name: {kwargs['project_name']} 23 | Version: 0.0.1 24 | License-File: LICENSE.txt 25 | Description-Content-Type: text/markdown 26 | 27 | # My.App 28 | 29 | [![PyPI - Version](https://img.shields.io/pypi/v/my-app.svg)](https://pypi.org/project/my-app) 30 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/my-app.svg)](https://pypi.org/project/my-app) 31 | 32 | ----- 33 | 34 | ## Table of Contents 35 | 36 | - [Installation](#installation) 37 | - [License](#license) 38 | 39 | ## Installation 40 | 41 | ```console 42 | pip install my-app 43 | ``` 44 | 45 | ## License 46 | 47 | `my-app` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. 48 | """, 49 | ) 50 | ) 51 | 52 | return files 53 | -------------------------------------------------------------------------------- /tests/helpers/templates/sdist/standard_include_config_file.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 4 | 5 | from ..new.feature_no_src_layout import get_files as get_template_files 6 | 7 | 8 | def get_files(**kwargs): 9 | relative_root = kwargs.get('relative_root', '') 10 | 11 | files = [] 12 | for f in get_template_files(**kwargs): 13 | part = f.path.parts[0] 14 | if part in {'my_app', 'pyproject.toml', 'README.md', 'LICENSE.txt'}: 15 | files.append(File(Path(relative_root, f.path), f.contents)) 16 | 17 | files.extend(( 18 | File(Path(relative_root, 'hatch.toml'), ''), 19 | File( 20 | Path(relative_root, 'PKG-INFO'), 21 | f"""\ 22 | Metadata-Version: {DEFAULT_METADATA_VERSION} 23 | Name: {kwargs['project_name']} 24 | Version: 0.0.1 25 | License-File: LICENSE.txt 26 | Description-Content-Type: text/markdown 27 | 28 | # My.App 29 | 30 | [![PyPI - Version](https://img.shields.io/pypi/v/my-app.svg)](https://pypi.org/project/my-app) 31 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/my-app.svg)](https://pypi.org/project/my-app) 32 | 33 | ----- 34 | 35 | ## Table of Contents 36 | 37 | - [Installation](#installation) 38 | - [License](#license) 39 | 40 | ## Installation 41 | 42 | ```console 43 | pip install my-app 44 | ``` 45 | 46 | ## License 47 | 48 | `my-app` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. 49 | """, 50 | ), 51 | )) 52 | 53 | return files 54 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/helpers/templates/wheel/__init__.py -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: {kwargs.get('tag', '')} 31 | """, 32 | ), 33 | File( 34 | Path(metadata_directory, 'METADATA'), 35 | f"""\ 36 | Metadata-Version: {DEFAULT_METADATA_VERSION} 37 | Name: {kwargs['project_name']} 38 | Version: 0.0.1 39 | License-File: LICENSE.txt 40 | Requires-Python: >3 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script_artifacts.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'lib.so'), ''), 25 | File( 26 | Path(metadata_directory, 'WHEEL'), 27 | f"""\ 28 | Wheel-Version: 1.0 29 | Generator: hatchling {__version__} 30 | Root-Is-Purelib: false 31 | Tag: {kwargs.get('tag', '')} 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | Requires-Python: >3 42 | """, 43 | ), 44 | )) 45 | 46 | record_file = File(Path(metadata_directory, 'RECORD'), '') 47 | update_record_file_contents(record_file, files) 48 | files.append(record_file) 49 | 50 | return files 51 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script_artifacts_with_src_layout.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.default import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != 'src': 19 | continue 20 | 21 | files.append(File(Path(*f.path.parts[1:]), f.contents)) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'lib.so'), ''), 25 | File(Path('zlib.pyd'), ''), 26 | File( 27 | Path(metadata_directory, 'WHEEL'), 28 | f"""\ 29 | Wheel-Version: 1.0 30 | Generator: hatchling {__version__} 31 | Root-Is-Purelib: false 32 | Tag: {kwargs.get('tag', '')} 33 | """, 34 | ), 35 | File( 36 | Path(metadata_directory, 'METADATA'), 37 | f"""\ 38 | Metadata-Version: {DEFAULT_METADATA_VERSION} 39 | Name: {kwargs['project_name']} 40 | Version: 0.0.1 41 | License-File: LICENSE.txt 42 | Requires-Python: >3 43 | """, 44 | ), 45 | )) 46 | 47 | record_file = File(Path(metadata_directory, 'RECORD'), '') 48 | update_record_file_contents(record_file, files) 49 | files.append(record_file) 50 | 51 | return files 52 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script_configured_build_hooks.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'lib.so'), 'custom'), 25 | File( 26 | Path(metadata_directory, 'WHEEL'), 27 | f"""\ 28 | Wheel-Version: 1.0 29 | Generator: hatchling {__version__} 30 | Root-Is-Purelib: false 31 | Tag: {kwargs.get('tag', '')} 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | Requires-Python: >3 42 | """, 43 | ), 44 | )) 45 | 46 | record_file = File(Path(metadata_directory, 'RECORD'), '') 47 | update_record_file_contents(record_file, files) 48 | files.append(record_file) 49 | 50 | return files 51 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script_extra_dependencies.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'lib.so'), ''), 25 | File( 26 | Path(metadata_directory, 'WHEEL'), 27 | f"""\ 28 | Wheel-Version: 1.0 29 | Generator: hatchling {__version__} 30 | Root-Is-Purelib: false 31 | Tag: {kwargs.get('tag', '')} 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | Requires-Python: >3 42 | Requires-Dist: binary 43 | """, 44 | ), 45 | )) 46 | 47 | record_file = File(Path(metadata_directory, 'RECORD'), '') 48 | update_record_file_contents(record_file, files) 49 | files.append(record_file) 50 | 51 | return files 52 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script_force_include.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'lib.so'), ''), 25 | File(Path(kwargs['package_name'], 'lib.h'), ''), 26 | File( 27 | Path(metadata_directory, 'WHEEL'), 28 | f"""\ 29 | Wheel-Version: 1.0 30 | Generator: hatchling {__version__} 31 | Root-Is-Purelib: false 32 | Tag: {kwargs.get('tag', '')} 33 | """, 34 | ), 35 | File( 36 | Path(metadata_directory, 'METADATA'), 37 | f"""\ 38 | Metadata-Version: {DEFAULT_METADATA_VERSION} 39 | Name: {kwargs['project_name']} 40 | Version: 0.0.1 41 | License-File: LICENSE.txt 42 | Requires-Python: >3 43 | """, 44 | ), 45 | )) 46 | 47 | record_file = File(Path(metadata_directory, 'RECORD'), '') 48 | update_record_file_contents(record_file, files) 49 | files.append(record_file) 50 | 51 | return files 52 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_build_script_force_include_no_duplication.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'z.py'), 'print("hello world")'), 25 | File( 26 | Path(metadata_directory, 'WHEEL'), 27 | f"""\ 28 | Wheel-Version: 1.0 29 | Generator: hatchling {__version__} 30 | Root-Is-Purelib: false 31 | Tag: {kwargs.get('tag', '')} 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | Requires-Python: >3 42 | """, 43 | ), 44 | )) 45 | 46 | record_file = File(Path(metadata_directory, 'RECORD'), '') 47 | update_record_file_contents(record_file, files) 48 | files.append(record_file) 49 | 50 | return files 51 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_extra_metadata.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(metadata_directory, 'extra_metadata', 'foo.txt'), ''), 25 | File(Path(metadata_directory, 'extra_metadata', 'nested', 'bar.txt'), ''), 26 | File( 27 | Path(metadata_directory, 'WHEEL'), 28 | f"""\ 29 | Wheel-Version: 1.0 30 | Generator: hatchling {__version__} 31 | Root-Is-Purelib: true 32 | Tag: py3-none-any 33 | """, 34 | ), 35 | File( 36 | Path(metadata_directory, 'METADATA'), 37 | f"""\ 38 | Metadata-Version: {DEFAULT_METADATA_VERSION} 39 | Name: {kwargs['project_name']} 40 | Version: 0.0.1 41 | License-File: LICENSE.txt 42 | Requires-Python: >3 43 | """, 44 | ), 45 | )) 46 | 47 | record_file = File(Path(metadata_directory, 'RECORD'), '') 48 | update_record_file_contents(record_file, files) 49 | files.append(record_file) 50 | 51 | return files 52 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_license_multiple.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.licenses_multiple import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | first_part = f.path.parts[0] 16 | 17 | if first_part == 'LICENSES': 18 | files.append(File(Path(metadata_directory, 'licenses', 'LICENSES', f.path.parts[1]), f.contents)) 19 | 20 | if f.path.parts[0] != 'src': 21 | continue 22 | 23 | files.append(File(Path(*f.path.parts[1:]), f.contents)) 24 | 25 | files.extend(( 26 | File( 27 | Path(metadata_directory, 'WHEEL'), 28 | f"""\ 29 | Wheel-Version: 1.0 30 | Generator: hatchling {__version__} 31 | Root-Is-Purelib: true 32 | Tag: py2-none-any 33 | Tag: py3-none-any 34 | """, 35 | ), 36 | File( 37 | Path(metadata_directory, 'METADATA'), 38 | f"""\ 39 | Metadata-Version: {DEFAULT_METADATA_VERSION} 40 | Name: {kwargs['project_name']} 41 | Version: 0.0.1 42 | License-File: LICENSES/Apache-2.0.txt 43 | License-File: LICENSES/MIT.txt 44 | """, 45 | ), 46 | )) 47 | 48 | record_file = File(Path(metadata_directory, 'RECORD'), '') 49 | update_record_file_contents(record_file, files) 50 | files.append(record_file) 51 | 52 | return files 53 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_license_single.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.default import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != 'src': 19 | continue 20 | 21 | files.append(File(Path(*f.path.parts[1:]), f.contents)) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: py2-none-any 31 | Tag: py3-none-any 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_namespace_package.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | namespace_package = kwargs['namespace'] 13 | 14 | files = [] 15 | for f in get_template_files(**kwargs): 16 | if str(f.path) == 'LICENSE.txt': 17 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 18 | 19 | if f.path.parts[0] != kwargs['package_name']: 20 | continue 21 | 22 | f.path = Path(namespace_package, f.path) 23 | files.append(f) 24 | 25 | files.extend(( 26 | File( 27 | Path(metadata_directory, 'WHEEL'), 28 | f"""\ 29 | Wheel-Version: 1.0 30 | Generator: hatchling {__version__} 31 | Root-Is-Purelib: true 32 | Tag: py2-none-any 33 | Tag: py3-none-any 34 | """, 35 | ), 36 | File( 37 | Path(metadata_directory, 'METADATA'), 38 | f"""\ 39 | Metadata-Version: {DEFAULT_METADATA_VERSION} 40 | Name: {kwargs['project_name']} 41 | Version: 0.0.1 42 | License-File: LICENSE.txt 43 | """, 44 | ), 45 | )) 46 | 47 | record_file = File(Path(metadata_directory, 'RECORD'), '') 48 | update_record_file_contents(record_file, files) 49 | files.append(record_file) 50 | 51 | return files 52 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_python_constraint.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: py3-none-any 31 | """, 32 | ), 33 | File( 34 | Path(metadata_directory, 'METADATA'), 35 | f"""\ 36 | Metadata-Version: {DEFAULT_METADATA_VERSION} 37 | Name: {kwargs['project_name']} 38 | Version: 0.0.1 39 | License-File: LICENSE.txt 40 | Requires-Python: >3 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_python_constraint_three_components.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: py3-none-any 31 | """, 32 | ), 33 | File( 34 | Path(metadata_directory, 'METADATA'), 35 | f"""\ 36 | Metadata-Version: {DEFAULT_METADATA_VERSION} 37 | Name: {kwargs['project_name']} 38 | Version: 0.0.1 39 | License-File: LICENSE.txt 40 | Requires-Python: ==3.11.4 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_shared_data.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | shared_data_directory = kwargs.get('shared_data_directory', '') 13 | 14 | files = [] 15 | for f in get_template_files(**kwargs): 16 | if str(f.path) == 'LICENSE.txt': 17 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 18 | 19 | if f.path.parts[0] != kwargs['package_name']: 20 | continue 21 | 22 | files.append(f) 23 | 24 | files.extend(( 25 | File(Path(shared_data_directory, 'data', 'foo.txt'), ''), 26 | File(Path(shared_data_directory, 'data', 'nested', 'bar.txt'), ''), 27 | File( 28 | Path(metadata_directory, 'WHEEL'), 29 | f"""\ 30 | Wheel-Version: 1.0 31 | Generator: hatchling {__version__} 32 | Root-Is-Purelib: true 33 | Tag: py3-none-any 34 | """, 35 | ), 36 | File( 37 | Path(metadata_directory, 'METADATA'), 38 | f"""\ 39 | Metadata-Version: {DEFAULT_METADATA_VERSION} 40 | Name: {kwargs['project_name']} 41 | Version: 0.0.1 42 | License-File: LICENSE.txt 43 | Requires-Python: >3 44 | """, 45 | ), 46 | )) 47 | 48 | record_file = File(Path(metadata_directory, 'RECORD'), '') 49 | update_record_file_contents(record_file, files) 50 | files.append(record_file) 51 | 52 | return files 53 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_single_module.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [ 14 | File(Path(metadata_directory, 'licenses', f.path), f.contents) 15 | for f in get_template_files(**kwargs) 16 | if str(f.path) == 'LICENSE.txt' 17 | ] 18 | 19 | files.extend(( 20 | File(Path('my_app.py'), ''), 21 | File( 22 | Path(metadata_directory, 'WHEEL'), 23 | f"""\ 24 | Wheel-Version: 1.0 25 | Generator: hatchling {__version__} 26 | Root-Is-Purelib: true 27 | Tag: py2-none-any 28 | Tag: py3-none-any 29 | """, 30 | ), 31 | File( 32 | Path(metadata_directory, 'METADATA'), 33 | f"""\ 34 | Metadata-Version: {DEFAULT_METADATA_VERSION} 35 | Name: {kwargs['project_name']} 36 | Version: 0.0.1 37 | License-File: LICENSE.txt 38 | """, 39 | ), 40 | )) 41 | 42 | record_file = File(Path(metadata_directory, 'RECORD'), '') 43 | update_record_file_contents(record_file, files) 44 | files.append(record_file) 45 | 46 | return files 47 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_default_symlink.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File(Path(kwargs['package_name'], 'lib.so'), 'data'), 25 | File( 26 | Path(metadata_directory, 'WHEEL'), 27 | f"""\ 28 | Wheel-Version: 1.0 29 | Generator: hatchling {__version__} 30 | Root-Is-Purelib: false 31 | Tag: {kwargs.get('tag', '')} 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | Requires-Python: >3 42 | """, 43 | ), 44 | )) 45 | 46 | record_file = File(Path(metadata_directory, 'RECORD'), '') 47 | update_record_file_contents(record_file, files) 48 | files.append(record_file) 49 | 50 | return files 51 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_editable_pth.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.default import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | package_paths = kwargs.get('package_paths', []) 13 | 14 | files = [ 15 | File(Path(metadata_directory, 'licenses', f.path), f.contents) 16 | for f in get_template_files(**kwargs) 17 | if str(f.path) == 'LICENSE.txt' 18 | ] 19 | 20 | pth_file_name = f"_{kwargs['package_name']}.pth" 21 | files.extend(( 22 | File(Path(pth_file_name), '\n'.join(package_paths)), 23 | File( 24 | Path(metadata_directory, 'WHEEL'), 25 | f"""\ 26 | Wheel-Version: 1.0 27 | Generator: hatchling {__version__} 28 | Root-Is-Purelib: true 29 | Tag: py2-none-any 30 | Tag: py3-none-any 31 | """, 32 | ), 33 | File( 34 | Path(metadata_directory, 'METADATA'), 35 | f"""\ 36 | Metadata-Version: {DEFAULT_METADATA_VERSION} 37 | Name: {kwargs['project_name']} 38 | Version: 0.0.1 39 | License-File: LICENSE.txt 40 | """, 41 | ), 42 | )) 43 | 44 | record_file = File(Path(metadata_directory, 'RECORD'), '') 45 | update_record_file_contents(record_file, files, generated_files={pth_file_name}) 46 | files.append(record_file) 47 | 48 | return files 49 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_editable_pth_extra_dependencies.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.default import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | package_paths = kwargs.get('package_paths', []) 13 | 14 | files = [ 15 | File(Path(metadata_directory, 'licenses', f.path), f.contents) 16 | for f in get_template_files(**kwargs) 17 | if str(f.path) == 'LICENSE.txt' 18 | ] 19 | 20 | pth_file_name = f"_{kwargs['package_name']}.pth" 21 | files.extend(( 22 | File(Path(pth_file_name), '\n'.join(package_paths)), 23 | File( 24 | Path(metadata_directory, 'WHEEL'), 25 | f"""\ 26 | Wheel-Version: 1.0 27 | Generator: hatchling {__version__} 28 | Root-Is-Purelib: true 29 | Tag: py2-none-any 30 | Tag: py3-none-any 31 | """, 32 | ), 33 | File( 34 | Path(metadata_directory, 'METADATA'), 35 | f"""\ 36 | Metadata-Version: {DEFAULT_METADATA_VERSION} 37 | Name: {kwargs['project_name']} 38 | Version: 0.0.1 39 | License-File: LICENSE.txt 40 | Requires-Dist: binary 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files, generated_files={pth_file_name}) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_editable_pth_force_include.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.default import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | package_paths = kwargs.get('package_paths', []) 13 | 14 | files = [] 15 | for f in get_template_files(**kwargs): 16 | if str(f.path) == 'LICENSE.txt': 17 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 18 | elif f.path.parts[-1] == '__about__.py': 19 | files.append(File(Path('zfoo.py'), f.contents)) 20 | 21 | pth_file_name = f"_{kwargs['package_name']}.pth" 22 | files.extend(( 23 | File(Path(pth_file_name), '\n'.join(package_paths)), 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: py2-none-any 31 | Tag: py3-none-any 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files, generated_files={pth_file_name}) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_entry_points.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'entry_points.txt'), 26 | """\ 27 | [console_scripts] 28 | bar = pkg:foo 29 | foo = pkg:bar 30 | """, 31 | ), 32 | File( 33 | Path(metadata_directory, 'WHEEL'), 34 | f"""\ 35 | Wheel-Version: 1.0 36 | Generator: hatchling {__version__} 37 | Root-Is-Purelib: true 38 | Tag: py2-none-any 39 | Tag: py3-none-any 40 | """, 41 | ), 42 | File( 43 | Path(metadata_directory, 'METADATA'), 44 | f"""\ 45 | Metadata-Version: {DEFAULT_METADATA_VERSION} 46 | Name: {kwargs['project_name']} 47 | Version: 0.0.1 48 | License-File: LICENSE.txt 49 | """, 50 | ), 51 | )) 52 | 53 | record_file = File(Path(metadata_directory, 'RECORD'), '') 54 | update_record_file_contents(record_file, files) 55 | files.append(record_file) 56 | 57 | return files 58 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_no_strict_naming.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] != kwargs['package_name']: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: py2-none-any 31 | Tag: py3-none-any 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_only_packages_artifact_override.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] not in {kwargs['package_name'], 'tests'}: 19 | continue 20 | 21 | if f.path == Path('tests', '__init__.py'): 22 | f.path = Path('tests', 'foo.py') 23 | 24 | files.append(f) 25 | 26 | files.extend(( 27 | File( 28 | Path(metadata_directory, 'WHEEL'), 29 | f"""\ 30 | Wheel-Version: 1.0 31 | Generator: hatchling {__version__} 32 | Root-Is-Purelib: true 33 | Tag: py2-none-any 34 | Tag: py3-none-any 35 | """, 36 | ), 37 | File( 38 | Path(metadata_directory, 'METADATA'), 39 | f"""\ 40 | Metadata-Version: {DEFAULT_METADATA_VERSION} 41 | Name: {kwargs['project_name']} 42 | Version: 0.0.1 43 | License-File: LICENSE.txt 44 | """, 45 | ), 46 | )) 47 | 48 | record_file = File(Path(metadata_directory, 'RECORD'), '') 49 | update_record_file_contents(record_file, files) 50 | files.append(record_file) 51 | 52 | return files 53 | -------------------------------------------------------------------------------- /tests/helpers/templates/wheel/standard_tests.py: -------------------------------------------------------------------------------- 1 | from hatch.template import File 2 | from hatch.utils.fs import Path 3 | from hatchling.__about__ import __version__ 4 | from hatchling.metadata.spec import DEFAULT_METADATA_VERSION 5 | 6 | from ..new.feature_no_src_layout import get_files as get_template_files 7 | from .utils import update_record_file_contents 8 | 9 | 10 | def get_files(**kwargs): 11 | metadata_directory = kwargs.get('metadata_directory', '') 12 | 13 | files = [] 14 | for f in get_template_files(**kwargs): 15 | if str(f.path) == 'LICENSE.txt': 16 | files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) 17 | 18 | if f.path.parts[0] not in {kwargs['package_name'], 'tests'}: 19 | continue 20 | 21 | files.append(f) 22 | 23 | files.extend(( 24 | File( 25 | Path(metadata_directory, 'WHEEL'), 26 | f"""\ 27 | Wheel-Version: 1.0 28 | Generator: hatchling {__version__} 29 | Root-Is-Purelib: true 30 | Tag: py2-none-any 31 | Tag: py3-none-any 32 | """, 33 | ), 34 | File( 35 | Path(metadata_directory, 'METADATA'), 36 | f"""\ 37 | Metadata-Version: {DEFAULT_METADATA_VERSION} 38 | Name: {kwargs['project_name']} 39 | Version: 0.0.1 40 | License-File: LICENSE.txt 41 | """, 42 | ), 43 | )) 44 | 45 | record_file = File(Path(metadata_directory, 'RECORD'), '') 46 | update_record_file_contents(record_file, files) 47 | files.append(record_file) 48 | 49 | return files 50 | -------------------------------------------------------------------------------- /tests/index/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/index/__init__.py -------------------------------------------------------------------------------- /tests/index/server/devpi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-alpine 2 | 3 | RUN apk add --update build-base && \ 4 | pip install -U devpi-server devpi-client devpi-web 5 | 6 | EXPOSE 3141 7 | 8 | COPY entrypoint.sh / 9 | ENTRYPOINT ["/bin/ash", "/entrypoint.sh"] 10 | -------------------------------------------------------------------------------- /tests/index/server/devpi/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 3 | IFS=$'\n\t' 4 | set -euo pipefail 5 | 6 | echo "==:> Initializing server" 7 | devpi-init --no-root-pypi 8 | 9 | echo "==:> Starting server" 10 | devpi-server --host 0.0.0.0 --port 3141 & 11 | 12 | echo "==:> Waiting on server" 13 | sleep 5 14 | 15 | echo "==:> Setting up index" 16 | devpi use http://localhost:3141 17 | devpi user -c $DEVPI_USERNAME password=$DEVPI_PASSWORD 18 | devpi login $DEVPI_USERNAME --password=$DEVPI_PASSWORD 19 | devpi index -c $DEVPI_INDEX_NAME volatile=True mirror_whitelist="*" 20 | devpi use $DEVPI_USERNAME/$DEVPI_INDEX_NAME 21 | devpi logoff 22 | 23 | echo "==:> Serving index $DEVPI_USERNAME/$DEVPI_INDEX_NAME" 24 | sleep infinity 25 | -------------------------------------------------------------------------------- /tests/index/server/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | devpi: 3 | container_name: hatch-devpi 4 | build: 5 | context: devpi 6 | ports: 7 | - "3141:3141" 8 | environment: 9 | - DEVPI_INDEX_NAME 10 | - DEVPI_USERNAME 11 | - DEVPI_PASSWORD 12 | 13 | nginx: 14 | container_name: hatch-nginx 15 | image: nginx:alpine 16 | ports: 17 | - "8080:80" 18 | - "8443:443" 19 | volumes: 20 | - ./nginx:/etc/nginx 21 | depends_on: 22 | - devpi 23 | -------------------------------------------------------------------------------- /tests/index/server/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | # Redirect HTTP to HTTPS 9 | server { 10 | listen 80; 11 | listen [::]:80; 12 | server_name _; 13 | return 301 https://$host$request_uri; 14 | } 15 | 16 | server { 17 | listen 443 ssl; 18 | server_name localhost; 19 | 20 | gzip on; 21 | gzip_min_length 2000; 22 | gzip_proxied any; 23 | 24 | proxy_read_timeout 60s; 25 | client_max_body_size 64M; 26 | 27 | ssl_certificate server.pem; 28 | ssl_certificate_key server.key; 29 | 30 | ssl_session_cache builtin:1000 shared:SSL:10m; 31 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 32 | ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; 33 | ssl_prefer_server_ciphers on; 34 | 35 | location / { 36 | error_page 418 = @proxy_to_app; 37 | return 418; 38 | } 39 | 40 | location @proxy_to_app { 41 | proxy_pass http://devpi:3141; 42 | proxy_pass_header Authorization; 43 | proxy_redirect off; 44 | proxy_set_header Host $host; 45 | proxy_set_header X-Real-IP $remote_addr; 46 | proxy_set_header X-Outside-URL $scheme://$host:$server_port; 47 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 48 | proxy_set_header X-Forwarded-Host $server_name; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/project/__init__.py -------------------------------------------------------------------------------- /tests/project/test_utils.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from hatch.project.utils import parse_inline_script_metadata 4 | 5 | 6 | class TestParseInlineScriptMetadata: 7 | def test_no_metadata(self): 8 | assert parse_inline_script_metadata('') is None 9 | 10 | def test_too_many_blocks(self, helpers): 11 | script = helpers.dedent( 12 | """ 13 | # /// script 14 | # dependencies = ["foo"] 15 | # /// 16 | 17 | # /// script 18 | # dependencies = ["foo"] 19 | # /// 20 | """ 21 | ) 22 | with pytest.raises(ValueError, match='^Multiple inline metadata blocks found for type: script$'): 23 | parse_inline_script_metadata(script) 24 | 25 | def test_correct(self, helpers): 26 | script = helpers.dedent( 27 | """ 28 | # /// script 29 | # embedded-csharp = ''' 30 | # /// 31 | # /// text 32 | # /// 33 | # /// 34 | # public class MyClass { } 35 | # ''' 36 | # /// 37 | """ 38 | ) 39 | assert parse_inline_script_metadata(script) == { 40 | 'embedded-csharp': helpers.dedent( 41 | """ 42 | /// 43 | /// text 44 | /// 45 | /// 46 | public class MyClass { } 47 | """ 48 | ), 49 | } 50 | -------------------------------------------------------------------------------- /tests/publish/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/publish/__init__.py -------------------------------------------------------------------------------- /tests/publish/plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/publish/plugin/__init__.py -------------------------------------------------------------------------------- /tests/python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/python/__init__.py -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/utils/__init__.py -------------------------------------------------------------------------------- /tests/utils/test_auth.py: -------------------------------------------------------------------------------- 1 | from hatch.publish.auth import AuthenticationCredentials 2 | from hatch.utils.fs import Path 3 | 4 | 5 | def test_pypirc(fs): 6 | fs.create_file( 7 | Path.home() / '.pypirc', 8 | contents="""\ 9 | [other] 10 | username: guido 11 | password: gat 12 | repository: https://kaashandel.nl/ 13 | 14 | [pypi] 15 | username: guido 16 | password: sprscrt 17 | """, 18 | ) 19 | 20 | credentials = AuthenticationCredentials( 21 | app=None, cache_dir=Path('/none'), options={}, repo='', repo_config={'url': ''} 22 | ) 23 | assert credentials.username == 'guido' 24 | assert credentials.password == 'sprscrt' 25 | 26 | credentials = AuthenticationCredentials( 27 | app=None, 28 | cache_dir=Path('/none'), 29 | options={}, 30 | repo='other', 31 | repo_config={'url': ''}, 32 | ) 33 | assert credentials.username == 'guido' 34 | assert credentials.password == 'gat' 35 | 36 | credentials = AuthenticationCredentials( 37 | app=None, 38 | cache_dir=Path('/none'), 39 | options={}, 40 | repo='arbritrary', 41 | repo_config={'url': 'https://kaashandel.nl/'}, 42 | ) 43 | assert credentials.username == 'guido' 44 | assert credentials.password == 'gat' 45 | -------------------------------------------------------------------------------- /tests/venv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypa/hatch/4ebce0e1fe8bf0fcdef587a704c207a063d72575/tests/venv/__init__.py -------------------------------------------------------------------------------- /tests/venv/test_utils.py: -------------------------------------------------------------------------------- 1 | from hatch.venv.utils import get_random_venv_name 2 | 3 | 4 | class TestGetRandomVenvName: 5 | def test_length(self): 6 | assert len(get_random_venv_name()) == 4 7 | 8 | def test_different(self): 9 | assert get_random_venv_name() != get_random_venv_name() 10 | --------------------------------------------------------------------------------