uv-pack 0.0.1__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
uv_pack-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.3
2
+ Name: uv-pack
3
+ Version: 0.1.0
4
+ Summary: Pack you uv environment for offline usage.
5
+ Author: David Muhr
6
+ Author-email: David Muhr <muhrdavid+github@gmail.com>
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Topic :: Software Development :: Libraries
10
+ Classifier: Topic :: Scientific/Engineering
11
+ Classifier: Topic :: Utilities
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Operating System :: Microsoft :: Windows
21
+ Classifier: Operating System :: POSIX
22
+ Classifier: Operating System :: Unix
23
+ Classifier: Operating System :: MacOS
24
+ Classifier: Typing :: Typed
25
+ Requires-Dist: packaging>=24.0
26
+ Requires-Dist: requests>=2
27
+ Requires-Dist: typer>=0.19
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+
31
+ uv-pack
32
+ =======
33
+
34
+ [![Check Status](https://github.com/davnn/uv-pack/actions/workflows/check.yml/badge.svg)](https://github.com/davnn/uv-pack/actions?query=workflow%3Acheck)
35
+ [![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/davnn/uv-pack/releases)
36
+
37
+ Bundle a locked `uv` environment into a self-contained, offline-installable directory.
38
+ The output includes pinned requirements, third-party wheels, locally built wheels,
39
+ and a portable Python interpreter.
40
+
41
+ What it does
42
+ ------------
43
+ - Exports locked requirements from your `uv` lock file.
44
+ - Downloads third-party wheels into `pack/wheels/`.
45
+ - Builds local workspace packages into `pack/vendor/`.
46
+ - Downloads a python-build-standalone archive into `pack/python/` (unless you skip the `python` step).
47
+ - Writes `unpack.sh`, `unpack.ps1`, and `unpack.cmd` to unpack the resulting venv offline.
48
+
49
+ Install
50
+ -------
51
+
52
+ Install `uv-pack` as a dev-dependency.
53
+
54
+ ```bash
55
+ uv add --dev uv-pack
56
+ ```
57
+
58
+ Once installed, run using:
59
+
60
+ ```bash
61
+ uv run uv-pack --help
62
+ ```
63
+
64
+ You can also use ``uv-pack`` as a tool.
65
+
66
+ ```bash
67
+ # specify the python version!
68
+ uv tool run --python 3.12 uv-pack --help
69
+ # or using uvx (equivalent)
70
+ uvx --python 3.12 uv-pack --help
71
+ ```
72
+
73
+ CLI
74
+ ---
75
+
76
+ ```bash
77
+ uv-pack [STEPS...]
78
+ ```
79
+
80
+ Options:
81
+ - `STEPS`: subset of pipeline steps (default: `clean export download build python`)
82
+ - `-s, --skip`: skip a pipeline step (can be supplied multiple times)
83
+ - `-o, --output-directory`: path to output directory (default: `./pack`)
84
+ - `-v, --verbose`: show more detailed pack progress logging
85
+ - `--uv-build`: extra args passed to `uv build`
86
+ - `--uv-export`: extra args passed to `uv export`
87
+ - `--pip-download`: extra args passed to `pip download`
88
+
89
+ Notes:
90
+ - The CLI is structured into five pipeline steps, see description below.
91
+ - Extra args are split on whitespace (for example: `--uv-export "--dev --all-extras"`).
92
+
93
+ Pipeline steps:
94
+ - `clean`: remove the output directory
95
+ - `export`: write `requirements.txt` files for third-party and local packages
96
+ - `download`: download third-party wheels
97
+ - `build`: build local wheels and compile the combined requirements file
98
+ - `python`: download a python-build-standalone archive for the current Python version and platform
99
+
100
+ Example
101
+ -------
102
+ ```bash
103
+ # run the entire pipeline (default) with verbose outputs
104
+ uv-pack --verbose
105
+ # only clean and export the requirements
106
+ uv-pack clean export
107
+ ```
108
+
109
+ Output layout
110
+ -------------
111
+ ```
112
+ pack/
113
+ requirements.txt
114
+ wheels/
115
+ requirements.txt
116
+ vendor/
117
+ requirements.txt
118
+ python/ # (omitted when the python step is skipped)
119
+ unpack.sh
120
+ unpack.ps1
121
+ unpack.cmd
122
+ .gitignore
123
+ README.md
124
+ ```
125
+
126
+ Unpack and install offline
127
+ --------------------------
128
+
129
+ POSIX (sh/bash/zsh):
130
+
131
+ ```bash
132
+ ./pack/unpack.sh
133
+ ```
134
+
135
+ PowerShell:
136
+
137
+ ```powershell
138
+ .\pack\unpack.ps1
139
+ ```
140
+
141
+ Windows cmd:
142
+
143
+ ```cmd
144
+ .\pack\unpack.cmd
145
+ ```
146
+
147
+ All scripts also accept `VENV_DIR`, `PY_DEST` and `BASE_PY` environment variables.
148
+ Use `BASE_PY` when you skipped the `python` step during packing to provide a system
149
+ python interpreter. `VENV_DIR` (default = `.venv`) and `PY_DEST` (default = `.python`)
150
+ can be used to customize the target python and venv directory.
151
+
152
+ Configuration
153
+ -------------
154
+
155
+ `UV_PYTHON_INSTALL_MIRROR` can override the GitHub API endpoint to retrieve the
156
+ Python releases, default is:
157
+ <https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest>.
158
+
159
+ `GITHUB_TOKEN` can be used to authenticate requests to the GitHub API to
160
+ prevent possible rate-limiting.
161
+
162
+ Limitations
163
+ -----------
164
+
165
+ - The pack process must happen in the ``pyproject.toml`` or ``uv.toml`` directory, typically the repository root,
166
+ because ``uv`` exports relative paths to the project root.
167
+ - The build platform is expected to equal the usage platform; it is currently not possible to pack an environment
168
+ for a different platform.
169
+ - The project Python version is ignored when running `uv-pack` as a tool (`uv tool run` or `uvx`) and should be
170
+ specified using `uv tool run --python 3.11 uv-pack` or `uvx --python 3.11 uv-pack`, see
171
+ [uv#uv5951](https://github.com/astral-sh/uv/issues/5951) and [uv#8206](https://github.com/astral-sh/uv/issues/8206).
172
+ - The download process can be slow because ``pip download`` is used as there is no native (parallel) uv download
173
+ option available for wheels, see [uv#3163](https://github.com/astral-sh/uv/issues/3163).
174
+
175
+ FAQ
176
+ -----------
177
+
178
+ #### How do I pass extra options to `uv export` or another command?
179
+
180
+ Use `--uv-export` to forward arguments, for example:
181
+
182
+ - ``uv-pack --uv-export "--package $MY_PACKAGE"`` to export only a specific workspace package
183
+ - ``uv-pack --uv-export "--locked --dev"`` to include dev-deps and ensure an up-to-date lock file
184
+ - ``uv-pack --uv-export "--all-extras"`` to include all extra dependencies
185
+
186
+ The same is true for ``--uv-build`` and ``--pip-download`` arguments.
187
+
188
+ #### How do I specify index-urls and extra-index-urls?
189
+
190
+ The index urls set in ``pyproject.toml`` and ``uv.toml`` are not configured by default for the wheel
191
+ download (``pip download``), you can specify them as:
192
+
193
+ - ``uv-pack --pip-download "--index-url $MY_INDEX --extra-index-url $MY_EXTRA_INDEX"``
194
+
195
+ #### How do I skip bundling Python?
196
+
197
+ Skip the `python` step: ``uv-pack --skip python``. When unpacking, set `BASE_PY` to a system Python path.
198
+
199
+ #### How do I rerun without deleting the existing pack directory?
200
+
201
+ Skip the `clean` step: ``uv-pack --skip clean``. Note that this automatically re-uses downloaded wheels
202
+ and the downloaded Python interpreter.
203
+
204
+ #### How do I only re-build my package if my pack is already complete?
205
+
206
+ Run only ``uv-pack build``.
@@ -0,0 +1,176 @@
1
+ uv-pack
2
+ =======
3
+
4
+ [![Check Status](https://github.com/davnn/uv-pack/actions/workflows/check.yml/badge.svg)](https://github.com/davnn/uv-pack/actions?query=workflow%3Acheck)
5
+ [![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/davnn/uv-pack/releases)
6
+
7
+ Bundle a locked `uv` environment into a self-contained, offline-installable directory.
8
+ The output includes pinned requirements, third-party wheels, locally built wheels,
9
+ and a portable Python interpreter.
10
+
11
+ What it does
12
+ ------------
13
+ - Exports locked requirements from your `uv` lock file.
14
+ - Downloads third-party wheels into `pack/wheels/`.
15
+ - Builds local workspace packages into `pack/vendor/`.
16
+ - Downloads a python-build-standalone archive into `pack/python/` (unless you skip the `python` step).
17
+ - Writes `unpack.sh`, `unpack.ps1`, and `unpack.cmd` to unpack the resulting venv offline.
18
+
19
+ Install
20
+ -------
21
+
22
+ Install `uv-pack` as a dev-dependency.
23
+
24
+ ```bash
25
+ uv add --dev uv-pack
26
+ ```
27
+
28
+ Once installed, run using:
29
+
30
+ ```bash
31
+ uv run uv-pack --help
32
+ ```
33
+
34
+ You can also use ``uv-pack`` as a tool.
35
+
36
+ ```bash
37
+ # specify the python version!
38
+ uv tool run --python 3.12 uv-pack --help
39
+ # or using uvx (equivalent)
40
+ uvx --python 3.12 uv-pack --help
41
+ ```
42
+
43
+ CLI
44
+ ---
45
+
46
+ ```bash
47
+ uv-pack [STEPS...]
48
+ ```
49
+
50
+ Options:
51
+ - `STEPS`: subset of pipeline steps (default: `clean export download build python`)
52
+ - `-s, --skip`: skip a pipeline step (can be supplied multiple times)
53
+ - `-o, --output-directory`: path to output directory (default: `./pack`)
54
+ - `-v, --verbose`: show more detailed pack progress logging
55
+ - `--uv-build`: extra args passed to `uv build`
56
+ - `--uv-export`: extra args passed to `uv export`
57
+ - `--pip-download`: extra args passed to `pip download`
58
+
59
+ Notes:
60
+ - The CLI is structured into five pipeline steps, see description below.
61
+ - Extra args are split on whitespace (for example: `--uv-export "--dev --all-extras"`).
62
+
63
+ Pipeline steps:
64
+ - `clean`: remove the output directory
65
+ - `export`: write `requirements.txt` files for third-party and local packages
66
+ - `download`: download third-party wheels
67
+ - `build`: build local wheels and compile the combined requirements file
68
+ - `python`: download a python-build-standalone archive for the current Python version and platform
69
+
70
+ Example
71
+ -------
72
+ ```bash
73
+ # run the entire pipeline (default) with verbose outputs
74
+ uv-pack --verbose
75
+ # only clean and export the requirements
76
+ uv-pack clean export
77
+ ```
78
+
79
+ Output layout
80
+ -------------
81
+ ```
82
+ pack/
83
+ requirements.txt
84
+ wheels/
85
+ requirements.txt
86
+ vendor/
87
+ requirements.txt
88
+ python/ # (omitted when the python step is skipped)
89
+ unpack.sh
90
+ unpack.ps1
91
+ unpack.cmd
92
+ .gitignore
93
+ README.md
94
+ ```
95
+
96
+ Unpack and install offline
97
+ --------------------------
98
+
99
+ POSIX (sh/bash/zsh):
100
+
101
+ ```bash
102
+ ./pack/unpack.sh
103
+ ```
104
+
105
+ PowerShell:
106
+
107
+ ```powershell
108
+ .\pack\unpack.ps1
109
+ ```
110
+
111
+ Windows cmd:
112
+
113
+ ```cmd
114
+ .\pack\unpack.cmd
115
+ ```
116
+
117
+ All scripts also accept `VENV_DIR`, `PY_DEST` and `BASE_PY` environment variables.
118
+ Use `BASE_PY` when you skipped the `python` step during packing to provide a system
119
+ python interpreter. `VENV_DIR` (default = `.venv`) and `PY_DEST` (default = `.python`)
120
+ can be used to customize the target python and venv directory.
121
+
122
+ Configuration
123
+ -------------
124
+
125
+ `UV_PYTHON_INSTALL_MIRROR` can override the GitHub API endpoint to retrieve the
126
+ Python releases, default is:
127
+ <https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest>.
128
+
129
+ `GITHUB_TOKEN` can be used to authenticate requests to the GitHub API to
130
+ prevent possible rate-limiting.
131
+
132
+ Limitations
133
+ -----------
134
+
135
+ - The pack process must happen in the ``pyproject.toml`` or ``uv.toml`` directory, typically the repository root,
136
+ because ``uv`` exports relative paths to the project root.
137
+ - The build platform is expected to equal the usage platform; it is currently not possible to pack an environment
138
+ for a different platform.
139
+ - The project Python version is ignored when running `uv-pack` as a tool (`uv tool run` or `uvx`) and should be
140
+ specified using `uv tool run --python 3.11 uv-pack` or `uvx --python 3.11 uv-pack`, see
141
+ [uv#uv5951](https://github.com/astral-sh/uv/issues/5951) and [uv#8206](https://github.com/astral-sh/uv/issues/8206).
142
+ - The download process can be slow because ``pip download`` is used as there is no native (parallel) uv download
143
+ option available for wheels, see [uv#3163](https://github.com/astral-sh/uv/issues/3163).
144
+
145
+ FAQ
146
+ -----------
147
+
148
+ #### How do I pass extra options to `uv export` or another command?
149
+
150
+ Use `--uv-export` to forward arguments, for example:
151
+
152
+ - ``uv-pack --uv-export "--package $MY_PACKAGE"`` to export only a specific workspace package
153
+ - ``uv-pack --uv-export "--locked --dev"`` to include dev-deps and ensure an up-to-date lock file
154
+ - ``uv-pack --uv-export "--all-extras"`` to include all extra dependencies
155
+
156
+ The same is true for ``--uv-build`` and ``--pip-download`` arguments.
157
+
158
+ #### How do I specify index-urls and extra-index-urls?
159
+
160
+ The index urls set in ``pyproject.toml`` and ``uv.toml`` are not configured by default for the wheel
161
+ download (``pip download``), you can specify them as:
162
+
163
+ - ``uv-pack --pip-download "--index-url $MY_INDEX --extra-index-url $MY_EXTRA_INDEX"``
164
+
165
+ #### How do I skip bundling Python?
166
+
167
+ Skip the `python` step: ``uv-pack --skip python``. When unpacking, set `BASE_PY` to a system Python path.
168
+
169
+ #### How do I rerun without deleting the existing pack directory?
170
+
171
+ Skip the `clean` step: ``uv-pack --skip clean``. Note that this automatically re-uses downloaded wheels
172
+ and the downloaded Python interpreter.
173
+
174
+ #### How do I only re-build my package if my pack is already complete?
175
+
176
+ Run only ``uv-pack build``.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "uv-pack"
3
- version = "0.0.1"
3
+ version = "0.1.0"
4
4
  description = "Pack you uv environment for offline usage."
5
5
  readme = "README.md"
6
6
  homepage = "https://github.com/davnn/uv-pack"
@@ -28,14 +28,15 @@ classifiers = [
28
28
  "Typing :: Typed"
29
29
  ]
30
30
  dependencies = [
31
- "packaging>=26.0",
32
- "requests>=2.32.5",
33
- "typer>=0.21.1",
31
+ "packaging>=24.0",
32
+ "requests>=2",
33
+ "typer>=0.19",
34
34
  ]
35
35
 
36
36
  [dependency-groups]
37
37
  dev = [
38
38
  "bandit>=1.9.3",
39
+ "black>=26.1.0",
39
40
  "notebook>=7.5.2",
40
41
  "pre-commit>=4.5.1",
41
42
  "pyright>=1.1.408",
@@ -59,4 +60,18 @@ extend-exclude = [".github"]
59
60
  target-version = "py310"
60
61
  line-length = 120
61
62
  lint.select = ["ALL"]
62
- lint.ignore = ["D401", "D213", "D211", "S603", "PLR0913", "PERF402", "FBT001", "FBT003", "B008", "PGH003"]
63
+ lint.ignore = [
64
+ "ANN401",
65
+ "B008",
66
+ "D203",
67
+ "D211",
68
+ "D213",
69
+ "D401",
70
+ "E731",
71
+ "FBT001",
72
+ "FBT003",
73
+ "PERF402",
74
+ "PGH003",
75
+ "PLR0913",
76
+ "S603",
77
+ ]
@@ -0,0 +1,258 @@
1
+ """uv-pack: Bundle a locked uv environment into an offline-installable bundle.
2
+
3
+ Pipeline:
4
+ 1. Clean the output directory
5
+ 2. Export locked requirements via uv
6
+ 3. Download third-party wheels into ./wheels
7
+ 4. Build local workspace packages into ./vendor
8
+ 5. Download a python interpreter to ./python
9
+
10
+ Result:
11
+ pack/
12
+ ├── requirements.txt
13
+ ├── wheels/ # third-party wheels
14
+ │ └── requirements.txt # index packages
15
+ ├── vendor/ # locally built wheels
16
+ │ └── requirements.txt # local packages
17
+ ├── python/ # python interpreter
18
+ ├── unpack.sh
19
+ ├── unpack.ps1
20
+ ├── unpack.bat
21
+ ├── .gitignore
22
+ └── README.md
23
+ """
24
+
25
+ import shutil
26
+ from collections.abc import Iterable
27
+ from enum import Enum
28
+ from pathlib import Path
29
+
30
+ import typer
31
+
32
+ from uv_pack._build import build_requirements, build_src_wheel
33
+ from uv_pack._download import download_third_party_wheels
34
+ from uv_pack._export import export_local_requirements, export_requirements
35
+ from uv_pack._files import PackLayout
36
+ from uv_pack._logging import ConsoleError, Verbosity, console_print, set_verbosity
37
+ from uv_pack._process import run_step
38
+ from uv_pack._python import download_latest_python_build
39
+ from uv_pack._scripts import copy_unpack_scripts
40
+
41
+ # -----------------------------------------------------------------------------
42
+ # CLI setup
43
+ # -----------------------------------------------------------------------------
44
+
45
+ app: typer.Typer = typer.Typer(add_completion=True)
46
+
47
+
48
+ def main() -> None:
49
+ """Main entry point for uv-pack CLI."""
50
+ try:
51
+ app()
52
+ except ConsoleError:
53
+ raise # already formatted for user
54
+ except KeyboardInterrupt as err:
55
+ console_print("[yellow]Interrupted by user[/yellow]")
56
+ raise typer.Exit(130) from err
57
+ except Exception as err:
58
+ msg = (
59
+ "[bold red]✘ An unexpected internal error occurred, please try again or"
60
+ " open an issue https://github.com/davnn/uv-pack/issues.[/bold red]"
61
+ )
62
+ raise ConsoleError(msg) from err
63
+
64
+
65
+ # -----------------------------------------------------------------------------
66
+ # Step model
67
+ # -----------------------------------------------------------------------------
68
+
69
+
70
+ class Step(str, Enum):
71
+ """Steps to be performed in the pack pipeline."""
72
+
73
+ clean = "clean"
74
+ export = "export"
75
+ download = "download"
76
+ build = "build"
77
+ python = "python"
78
+
79
+
80
+ PIPELINE_ORDER: tuple[Step, ...] = (
81
+ Step.clean,
82
+ Step.export,
83
+ Step.download,
84
+ Step.build,
85
+ Step.python,
86
+ )
87
+
88
+
89
+ # -----------------------------------------------------------------------------
90
+ # Helper operations
91
+ # -----------------------------------------------------------------------------
92
+
93
+
94
+ def _additional_cli_args(cmd_name: str) -> str:
95
+ return f"Additional command line arguments to be provided to '{cmd_name}'"
96
+
97
+
98
+ def _normalize_steps(
99
+ steps: Iterable[Step] | None,
100
+ skip: Iterable[Step] | None,
101
+ ) -> list[Step]:
102
+ selected = set(PIPELINE_ORDER) if steps is None else set(steps)
103
+ selected = selected.difference([] if skip is None else set(skip))
104
+ return [step for step in PIPELINE_ORDER if step in selected]
105
+
106
+
107
+ def _raise_requirement_txt_missing(path: Path) -> None:
108
+ if not path.exists():
109
+ msg = f"[bold red]✘ No requirements file found:[/bold red] '{path}', did you skip the 'export' step?"
110
+ raise ConsoleError(msg)
111
+
112
+
113
+ # -----------------------------------------------------------------------------
114
+ # Orchestration command
115
+ # -----------------------------------------------------------------------------
116
+
117
+
118
+ @app.command()
119
+ def pack(
120
+ *,
121
+ steps: list[Step] | None = typer.Argument(
122
+ PIPELINE_ORDER,
123
+ help="Pipeline steps to run (multiple can be whitespace-separated)",
124
+ ),
125
+ skip: list[Step] | None = typer.Option(
126
+ None,
127
+ "--skip",
128
+ "-s",
129
+ help="Pipeline steps to skip (can be supplied multiple times)",
130
+ ),
131
+ output_directory: Path = typer.Option(
132
+ Path("./pack"),
133
+ "--output-directory",
134
+ "-o",
135
+ help="Path to output directory",
136
+ ),
137
+ uv_export: str = typer.Option(
138
+ default="",
139
+ help=_additional_cli_args("uv export"),
140
+ ),
141
+ pip_download: str = typer.Option(
142
+ default="",
143
+ help=_additional_cli_args("pip download"),
144
+ ),
145
+ uv_build: str = typer.Option(
146
+ default="",
147
+ help=_additional_cli_args("uv build"),
148
+ ),
149
+ verbose: bool = typer.Option(
150
+ False,
151
+ "--verbose",
152
+ "-v",
153
+ help="Enable verbose output",
154
+ ),
155
+ ) -> None:
156
+ """Pack a locked uv environment into an offline-installable bundle."""
157
+ set_verbosity(Verbosity.verbose if verbose else Verbosity.normal)
158
+ selected_steps = _normalize_steps(steps, skip)
159
+ console_print(
160
+ f"[dim]Running steps:[/dim] {[step.value for step in selected_steps]}",
161
+ )
162
+
163
+ if Step.clean in selected_steps:
164
+ with run_step("clean"):
165
+ shutil.rmtree(output_directory, ignore_errors=True)
166
+
167
+ console_print(
168
+ f"[green]✔ Cleaned[/green] output directory '{output_directory}'",
169
+ level=Verbosity.verbose,
170
+ )
171
+
172
+ # initialize pack directory structure and copy unpack scripts
173
+ pack = PackLayout.create(output_directory=output_directory)
174
+ copy_unpack_scripts(output_directory=output_directory)
175
+
176
+ if Step.export in selected_steps:
177
+ with run_step("export"):
178
+ export_requirements(
179
+ requirements_file=pack.requirements_export_txt,
180
+ other_args=uv_export,
181
+ )
182
+ export_local_requirements(
183
+ requirements_file=pack.requirements_local_txt,
184
+ other_args=uv_export,
185
+ )
186
+ console_print(
187
+ f"[green]✔ Exported[/green] requirements '{pack.requirements_export_txt}'",
188
+ level=Verbosity.verbose,
189
+ )
190
+ console_print(
191
+ f"[green]✔ Exported[/green] requirements '{pack.requirements_local_txt}'",
192
+ level=Verbosity.verbose,
193
+ )
194
+
195
+ if Step.download in selected_steps:
196
+ _raise_requirement_txt_missing(pack.requirements_export_txt)
197
+
198
+ with run_step("download"):
199
+ download_third_party_wheels(
200
+ requirements_file=pack.requirements_export_txt,
201
+ wheels_directory=pack.wheels_dir,
202
+ other_args=pip_download,
203
+ )
204
+
205
+ if Step.build in selected_steps:
206
+ _raise_requirement_txt_missing(pack.requirements_local_txt)
207
+ _raise_requirement_txt_missing(pack.requirements_export_txt)
208
+
209
+ # show the progress for each package in verbose mode, otherwise a single progress report is shown
210
+ with run_step("build", should_run=not verbose):
211
+ for line in pack.requirements_local_txt.read_text(
212
+ encoding="utf-8",
213
+ ).splitlines():
214
+ with run_step("build", should_run=verbose):
215
+ build_src_wheel(
216
+ source_path=Path(line),
217
+ out_path=pack.vendor_dir,
218
+ other_args=uv_build,
219
+ )
220
+ console_print(
221
+ f"[green]✔ Built[/green] wheel: '{line}'",
222
+ level=Verbosity.verbose,
223
+ )
224
+
225
+ for sdist in pack.wheels_dir.glob("*.tar.gz"):
226
+ with run_step("build", should_run=verbose):
227
+ build_src_wheel(
228
+ source_path=sdist,
229
+ out_path=pack.wheels_dir,
230
+ other_args=uv_build,
231
+ )
232
+ sdist.unlink(missing_ok=True)
233
+ console_print(
234
+ f"[green]✔ Built[/green] wheel: '{sdist}'",
235
+ level=Verbosity.verbose,
236
+ )
237
+
238
+ build_requirements(
239
+ requirements_txt=pack.requirements_txt,
240
+ requirements_export_txt=pack.requirements_export_txt,
241
+ vendor_directory=pack.vendor_dir,
242
+ )
243
+ console_print(
244
+ f"[green]✔ Built[/green] requirements: '{pack.requirements_txt}'",
245
+ level=Verbosity.verbose,
246
+ )
247
+
248
+ if Step.python in selected_steps:
249
+ pack.python_dir.mkdir(exist_ok=True)
250
+ python_path = download_latest_python_build(
251
+ dest_dir=pack.python_dir,
252
+ )
253
+ console_print(
254
+ f"[green]✔ Python[/green] archive: '{python_path}'",
255
+ level=Verbosity.verbose,
256
+ )
257
+
258
+ console_print("[green]✔ Done[/green]")