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 +206 -0
- uv_pack-0.1.0/README.md +176 -0
- {uv_pack-0.0.1 → uv_pack-0.1.0}/pyproject.toml +20 -5
- uv_pack-0.1.0/src/uv_pack/__init__.py +258 -0
- uv_pack-0.1.0/src/uv_pack/_build.py +46 -0
- uv_pack-0.1.0/src/uv_pack/_download.py +52 -0
- uv_pack-0.1.0/src/uv_pack/_export.py +48 -0
- uv_pack-0.1.0/src/uv_pack/_files.py +56 -0
- uv_pack-0.1.0/src/uv_pack/_logging.py +46 -0
- uv_pack-0.1.0/src/uv_pack/_process.py +75 -0
- uv_pack-0.0.1/src/uv_pack/_download.py → uv_pack-0.1.0/src/uv_pack/_python.py +58 -13
- uv_pack-0.0.1/src/uv_pack/_unpack.py → uv_pack-0.1.0/src/uv_pack/_scripts.py +5 -1
- uv_pack-0.0.1/PKG-INFO +0 -140
- uv_pack-0.0.1/README.md +0 -110
- uv_pack-0.0.1/src/uv_pack/__init__.py +0 -302
- uv_pack-0.0.1/src/uv_pack/_process.py +0 -59
- {uv_pack-0.0.1 → uv_pack-0.1.0}/src/uv_pack/py.typed +0 -0
- {uv_pack-0.0.1 → uv_pack-0.1.0}/src/uv_pack/scripts/README.md +0 -0
- {uv_pack-0.0.1 → uv_pack-0.1.0}/src/uv_pack/scripts/SPEC.md +0 -0
- {uv_pack-0.0.1 → uv_pack-0.1.0}/src/uv_pack/scripts/unpack.cmd +0 -0
- {uv_pack-0.0.1 → uv_pack-0.1.0}/src/uv_pack/scripts/unpack.ps1 +0 -0
- {uv_pack-0.0.1 → uv_pack-0.1.0}/src/uv_pack/scripts/unpack.sh +0 -0
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
|
+
[](https://github.com/davnn/uv-pack/actions?query=workflow%3Acheck)
|
|
35
|
+
[](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``.
|
uv_pack-0.1.0/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
uv-pack
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
[](https://github.com/davnn/uv-pack/actions?query=workflow%3Acheck)
|
|
5
|
+
[](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
|
|
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>=
|
|
32
|
-
"requests>=2
|
|
33
|
-
"typer>=0.
|
|
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 = [
|
|
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]")
|