From be42841ec6068d509b293390682cdc15ba190075 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 26 May 2024 22:43:44 -0400 Subject: [PATCH] Add tutorial for managing Python distributions --- docs/tutorials/python/manage.md | 136 +++++++++++++++++++++++++++++++ docs/why.md | 2 +- mkdocs.yml | 2 + src/hatch/cli/python/find.py | 2 +- src/hatch/cli/python/install.py | 5 +- tests/cli/python/test_find.py | 7 +- tests/cli/python/test_install.py | 4 +- 7 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 docs/tutorials/python/manage.md diff --git a/docs/tutorials/python/manage.md b/docs/tutorials/python/manage.md new file mode 100644 index 000000000..257c69f01 --- /dev/null +++ b/docs/tutorials/python/manage.md @@ -0,0 +1,136 @@ +# Managing Python distributions + +----- + +The [`python`](../../cli/reference.md#hatch-python) command group provides a set of commands to manage Python distributions that may be used by other tools. + +!!! note + When using environments, manual management is not necessary since by default Hatch will [automatically](../../plugins/environment/virtual.md#python-resolution) download and manage Python distributions internally when a requested version cannot be found. + +## Location + +There are two ways to control where Python distributions are installed. Both methods make it so that each installed distribution is placed in a subdirectory of the configured location named after the distribution. + +1. The globally configured [default directory](../../config/hatch.md#python-installations) for Python installations. +2. The `-d`/`--dir` option of every [`python`](../../cli/reference.md#hatch-python) subcommand, which takes precedence over the default directory. + +## Installation + +To install a Python distribution, use the [`python install`](../../cli/reference.md#hatch-python-install) command. For example: + +``` +hatch python install 3.12 +``` + +This will: + +1. Download the `3.12` Python distribution +2. Unpack it into a directory named `3.12` within the configured [default directory](../../config/hatch.md#python-installations) for Python installations +3. Add the installation to the user PATH + +Now its `python` executable can be used by you or other tools. + +!!! note + For PATH changes to take effect in the current shell, you will need to restart it. + +### Multiple + +You can install multiple Python distributions at once by providing multiple distribution names. For example: + +``` +hatch python install 3.12 3.11 pypy3.10 +``` + +If you would like to install all available Python distributions that are compatible with your system, use `all` as the distribution name: + +``` +hatch python install all +``` + +!!! tip + The commands for [updating](#updates) and [removing](#removal) also support this functionality. + +### Private + +By default, installing Python distributions will add them to the user PATH. To disable this behavior, use the `--private` flag like so: + +``` +hatch python install 3.12 --private +``` + +This when combined with the [directory option](#location) can be used to create private, isolated installations. + +## Listing distributions + +You can see all of the available and installed Python distributions by using the [`python show`](../../cli/reference.md#hatch-python-show) command. For example, if you already installed the `3.12` distribution you may see something like this: + +``` +$ hatch python show + Installed +┏━━━━━━┳━━━━━━━━━┓ +┃ Name ┃ Version ┃ +┡━━━━━━╇━━━━━━━━━┩ +│ 3.12 │ 3.12.3 │ +└──────┴─────────┘ + Available +┏━━━━━━━━━━┳━━━━━━━━━┓ +┃ Name ┃ Version ┃ +┡━━━━━━━━━━╇━━━━━━━━━┩ +│ 3.7 │ 3.7.9 │ +├──────────┼─────────┤ +│ 3.8 │ 3.8.19 │ +├──────────┼─────────┤ +│ 3.9 │ 3.9.19 │ +├──────────┼─────────┤ +│ 3.10 │ 3.10.14 │ +├──────────┼─────────┤ +│ 3.11 │ 3.11.9 │ +├──────────┼─────────┤ +│ pypy2.7 │ 7.3.15 │ +├──────────┼─────────┤ +│ pypy3.9 │ 7.3.15 │ +├──────────┼─────────┤ +│ pypy3.10 │ 7.3.15 │ +└──────────┴─────────┘ +``` + +## Finding installations + +The Python executable of an installed distribution can be found by using the [`python find`](../../cli/reference.md#hatch-python-find) command. For example: + +``` +$ hatch python find 3.12 +/home/.local/share/hatch/pythons/3.12/python/bin/python3 +``` + +You can instead output its parent directory by using the `-p`/`--parent` flag: + +``` +$ hatch python find 3.12 --parent +/home/.local/share/hatch/pythons/3.12/python/bin +``` + +This is useful when other tools do not need to use the executable directly but require knowing the directory containing it. + +## Updates + +To update installed Python distributions, use the [`python update`](../../cli/reference.md#hatch-python-update) command. For example: + +``` +hatch python update 3.12 3.11 pypy3.10 +``` + +When there are no updates available for a distribution, a warning will be displayed: + +``` +$ hatch python update 3.12 +The latest version is already installed: 3.12.3 +``` + +## Removal + +To remove installed Python distributions, use the [`python remove`](../../cli/reference.md#hatch-python-remove) command. For example: + +``` +hatch python remove 3.12 3.11 pypy3.10 +``` diff --git a/docs/why.md b/docs/why.md index a7e4874a9..95e7cd57c 100644 --- a/docs/why.md +++ b/docs/why.md @@ -45,7 +45,7 @@ If you are using `nox` and you wish to migrate, and for some reason you [notify] ## Python management -Here we compare [Python management](cli/reference.md#hatch-python) to that of [pyenv](https://github.com/pyenv/pyenv). +Here we compare [Python management](tutorials/python/manage.md) to that of [pyenv](https://github.com/pyenv/pyenv). - ***Cross-platform:*** Hatch allows for the same experience no matter the system whereas `pyenv` does not support Windows so you must use an [entirely different project](https://github.com/pyenv-win/pyenv-win) that tries to emulate the functionality. - ***No build dependencies:*** Hatch guarantees that every [available distribution](cli/reference.md#hatch-python-show) is prebuilt whereas the alternative requires one to maintain a precise [build environment](https://github.com/pyenv/pyenv/wiki#suggested-build-environment) which differs by platform and potentially Python version. Another benefit to this is extremely fast installations since the distributions are simply downloaded and unpacked. diff --git a/mkdocs.yml b/mkdocs.yml index 39ec3645e..28b8d2074 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -137,6 +137,8 @@ nav: - Authentication: how-to/publish/auth.md - Repository selection: how-to/publish/repo.md - Tutorials: + - Python: + - Management: tutorials/python/manage.md - Environments: - Basic usage: tutorials/environment/basic-usage.md - Testing: diff --git a/src/hatch/cli/python/find.py b/src/hatch/cli/python/find.py index 5810cbca8..c23b6948c 100644 --- a/src/hatch/cli/python/find.py +++ b/src/hatch/cli/python/find.py @@ -18,7 +18,7 @@ def find(app: Application, *, name: str, parent: bool, directory: str | None): manager = app.get_python_manager(directory) installed = manager.get_installed() if name not in installed: - app.abort('Distribution not installed') + app.abort(f'Distribution not installed: {name}') dist = installed[name] app.display(str(dist.python_path.parent if parent else dist.python_path)) diff --git a/src/hatch/cli/python/install.py b/src/hatch/cli/python/install.py index f4e772be1..bd7e8adcd 100644 --- a/src/hatch/cli/python/install.py +++ b/src/hatch/cli/python/install.py @@ -79,9 +79,10 @@ def install(app: Application, *, names: tuple[str, ...], private: bool, update: for name in compatible: needs_update = False if name in installed: - needs_update = installed[name].needs_update() + installed_dist = installed[name] + needs_update = installed_dist.needs_update() if not needs_update: - app.display_warning(f'The latest version is already installed: {name}') + app.display_warning(f'The latest version is already installed: {installed_dist.version}') continue if not (update or app.confirm(f'Update {name}?')): diff --git a/tests/cli/python/test_find.py b/tests/cli/python/test_find.py index 5ac8e0d17..aa03418c4 100644 --- a/tests/cli/python/test_find.py +++ b/tests/cli/python/test_find.py @@ -1,10 +1,11 @@ def test_not_installed(hatch, helpers): - result = hatch('python', 'find', '3.10') + name = '3.10' + result = hatch('python', 'find', name) assert result.exit_code == 1, result.output assert result.output == helpers.dedent( - """ - Distribution not installed + f""" + Distribution not installed: {name} """ ) diff --git a/tests/cli/python/test_install.py b/tests/cli/python/test_install.py index 7d27abc92..f8a462771 100644 --- a/tests/cli/python/test_install.py +++ b/tests/cli/python/test_install.py @@ -97,14 +97,14 @@ def test_installation( def test_already_installed_latest(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): install = mocker.patch('hatch.python.core.PythonManager.install') install_dir = temp_dir_data / 'data' / 'pythons' - helpers.write_distribution(install_dir, dist_name) + installed_dist = helpers.write_distribution(install_dir, dist_name) result = hatch('python', 'install', dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( f""" - The latest version is already installed: {dist_name} + The latest version is already installed: {installed_dist.version} """ )