pex 2.59.5__py2.py3-none-any.whl → 2.60.1__py2.py3-none-any.whl
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.
Potentially problematic release.
This version of pex might be problematic. Click here for more details.
- pex/cache/dirs.py +14 -4
- pex/cli/commands/lock.py +8 -5
- pex/common.py +57 -7
- pex/dist_metadata.py +48 -6
- pex/docs/html/_pagefind/fragment/en_52292af.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_6190e2d.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_8d14bba.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_9ba8f7b.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_c350870.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_cb99877.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_cf3d25b.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_df34874.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/{en_974dc5a.pf_index → en_853e43e.pf_index} +0 -0
- pex/docs/html/_pagefind/pagefind-entry.json +1 -1
- pex/docs/html/_pagefind/pagefind.en_71c76562fd.pf_meta +0 -0
- pex/docs/html/_static/documentation_options.js +1 -1
- pex/docs/html/api/vars.html +5 -5
- pex/docs/html/buildingpex.html +5 -5
- pex/docs/html/genindex.html +5 -5
- pex/docs/html/index.html +5 -5
- pex/docs/html/recipes.html +5 -5
- pex/docs/html/scie.html +5 -5
- pex/docs/html/search.html +5 -5
- pex/docs/html/whatispex.html +5 -5
- pex/entry_points_txt.py +98 -0
- pex/environment.py +13 -10
- pex/finders.py +1 -1
- pex/installed_wheel.py +127 -0
- pex/interpreter.py +17 -5
- pex/interpreter_constraints.py +4 -4
- pex/pep_376.py +37 -380
- pex/pep_427.py +757 -246
- pex/pex_builder.py +4 -4
- pex/pex_info.py +8 -3
- pex/resolve/venv_resolver.py +46 -34
- pex/resolver.py +10 -3
- pex/sysconfig.py +5 -3
- pex/third_party/__init__.py +1 -1
- pex/tools/commands/repository.py +47 -24
- pex/vendor/__init__.py +1 -8
- pex/vendor/__main__.py +62 -41
- pex/vendor/_vendored/ansicolors/.layout.json +1 -1
- pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/RECORD +11 -0
- pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/appdirs/.layout.json +1 -1
- pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/RECORD +7 -0
- pex/vendor/_vendored/appdirs/appdirs-1.4.4.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/attrs/.layout.json +1 -1
- pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/RECORD +37 -0
- pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_20_9/.layout.json +1 -1
- pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/RECORD +20 -0
- pex/vendor/_vendored/packaging_20_9/packaging-20.9.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/RECORD +7 -0
- pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_21_3/.layout.json +1 -1
- pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/RECORD +20 -0
- pex/vendor/_vendored/packaging_21_3/packaging-21.3.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/RECORD +18 -0
- pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_24_0/.layout.json +1 -1
- pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/RECORD +22 -0
- pex/vendor/_vendored/packaging_24_0/packaging-24.0.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/packaging_25_0/.layout.json +1 -1
- pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/RECORD +24 -0
- pex/vendor/_vendored/packaging_25_0/packaging-25.0.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/pip/.layout.json +1 -1
- pex/vendor/_vendored/pip/pip-20.3.4.dist-info/RECORD +388 -0
- pex/vendor/_vendored/pip/pip-20.3.4.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/setuptools/.layout.json +1 -1
- pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/RECORD +107 -0
- pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/toml/.layout.json +1 -1
- pex/vendor/_vendored/toml/toml-0.10.2.dist-info/RECORD +11 -0
- pex/vendor/_vendored/toml/toml-0.10.2.pex-info/original-whl-info.json +1 -0
- pex/vendor/_vendored/tomli/.layout.json +1 -1
- pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/RECORD +10 -0
- pex/vendor/_vendored/tomli/tomli-2.0.1.pex-info/original-whl-info.json +1 -0
- pex/venv/installer.py +9 -5
- pex/version.py +1 -1
- pex/wheel.py +79 -15
- pex/whl.py +67 -0
- pex/windows/__init__.py +14 -11
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/METADATA +4 -4
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/RECORD +90 -74
- pex/docs/html/_pagefind/fragment/en_34b3bf8.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_3cefc8e.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_44ba8a7.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_8eb9a56.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_db171fd.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_ecf679c.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_fb971c7.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_fd8f242.pf_fragment +0 -0
- pex/docs/html/_pagefind/pagefind.en_3549188bce.pf_meta +0 -0
- pex/vendor/_vendored/ansicolors/ansicolors-1.1.8.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/appdirs/appdirs-1.4.4.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/attrs/attrs-21.5.0.dev0.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_20_9/packaging-20.9.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_20_9/pyparsing-2.4.7.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_21_3/packaging-21.3.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_21_3/pyparsing-3.0.7.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_24_0/packaging-24.0.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/packaging_25_0/packaging-25.0.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/pip/pip-20.3.4.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/setuptools/setuptools-44.0.0+3acb925dd708430aeaf197ea53ac8a752f7c1863.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/toml/toml-0.10.2.dist-info/INSTALLER +0 -1
- pex/vendor/_vendored/tomli/tomli-2.0.1.dist-info/INSTALLER +0 -1
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/WHEEL +0 -0
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/entry_points.txt +0 -0
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/licenses/LICENSE +0 -0
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/pylock/pylock.toml +0 -0
- {pex-2.59.5.dist-info → pex-2.60.1.dist-info}/top_level.txt +0 -0
pex/docs/html/search.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="prefetch" href="_static/pex-logo-light.png" as="image">
|
|
7
7
|
<link rel="prefetch" href="_static/pex-logo-dark.png" as="image">
|
|
8
8
|
|
|
9
|
-
<link rel="shortcut icon" href="_static/pex-icon.png"><!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 --><title>Search - Pex Docs (v2.
|
|
9
|
+
<link rel="shortcut icon" href="_static/pex-icon.png"><!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 --><title>Search - Pex Docs (v2.60.1)</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
|
10
10
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
|
11
11
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
|
12
12
|
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
</label>
|
|
177
177
|
</div>
|
|
178
178
|
<div class="header-center">
|
|
179
|
-
<a href="index.html"><div class="brand">Pex Docs (v2.
|
|
179
|
+
<a href="index.html"><div class="brand">Pex Docs (v2.60.1)</div></a>
|
|
180
180
|
</div>
|
|
181
181
|
<div class="header-right">
|
|
182
182
|
<div class="theme-toggle-container theme-toggle-header">
|
|
@@ -273,12 +273,12 @@
|
|
|
273
273
|
</div>
|
|
274
274
|
<div class="right-details">
|
|
275
275
|
<div class="icons">
|
|
276
|
-
<a class="muted-link " href="https://pypi.org/project/pex/2.
|
|
276
|
+
<a class="muted-link " href="https://pypi.org/project/pex/2.60.1/" aria-label="PyPI"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
|
277
277
|
<path d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z">
|
|
278
278
|
</path>
|
|
279
279
|
</svg>
|
|
280
280
|
</a>
|
|
281
|
-
<a class="muted-link " href="https://github.com/pex-tool/pex/releases/download/v2.
|
|
281
|
+
<a class="muted-link " href="https://github.com/pex-tool/pex/releases/download/v2.60.1/pex" aria-label="Download"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 640 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
|
282
282
|
<path d="M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zm-132.9 88.7L299.3 420.7c-6.2 6.2-16.4 6.2-22.6 0L171.3 315.3c-10.1-10.1-2.9-27.3 11.3-27.3H248V176c0-8.8 7.2-16 16-16h48c8.8 0 16 7.2 16 16v112h65.4c14.2 0 21.4 17.2 11.3 27.3z">
|
|
283
283
|
</path>
|
|
284
284
|
</svg>
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
|
|
302
302
|
</aside>
|
|
303
303
|
</div>
|
|
304
|
-
</div><script src="_static/documentation_options.js?v=
|
|
304
|
+
</div><script src="_static/documentation_options.js?v=bfb83a69"></script>
|
|
305
305
|
<script src="_static/doctools.js?v=9bcbadda"></script>
|
|
306
306
|
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
|
307
307
|
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
pex/docs/html/whatispex.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="prefetch" href="_static/pex-logo-dark.png" as="image">
|
|
9
9
|
|
|
10
10
|
<link rel="shortcut icon" href="_static/pex-icon.png"><!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
|
|
11
|
-
<title>What are .pex files? - Pex Docs (v2.
|
|
11
|
+
<title>What are .pex files? - Pex Docs (v2.60.1)</title>
|
|
12
12
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
|
13
13
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
|
|
14
14
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
</label>
|
|
164
164
|
</div>
|
|
165
165
|
<div class="header-center">
|
|
166
|
-
<a href="index.html"><div class="brand">Pex Docs (v2.
|
|
166
|
+
<a href="index.html"><div class="brand">Pex Docs (v2.60.1)</div></a>
|
|
167
167
|
</div>
|
|
168
168
|
<div class="header-right">
|
|
169
169
|
<div class="theme-toggle-container theme-toggle-header">
|
|
@@ -322,12 +322,12 @@ build executable .pex files. This is described more thoroughly in
|
|
|
322
322
|
</div>
|
|
323
323
|
<div class="right-details">
|
|
324
324
|
<div class="icons">
|
|
325
|
-
<a class="muted-link " href="https://pypi.org/project/pex/2.
|
|
325
|
+
<a class="muted-link " href="https://pypi.org/project/pex/2.60.1/" aria-label="PyPI"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
|
326
326
|
<path d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z">
|
|
327
327
|
</path>
|
|
328
328
|
</svg>
|
|
329
329
|
</a>
|
|
330
|
-
<a class="muted-link " href="https://github.com/pex-tool/pex/releases/download/v2.
|
|
330
|
+
<a class="muted-link " href="https://github.com/pex-tool/pex/releases/download/v2.60.1/pex" aria-label="Download"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 640 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
|
331
331
|
<path d="M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zm-132.9 88.7L299.3 420.7c-6.2 6.2-16.4 6.2-22.6 0L171.3 315.3c-10.1-10.1-2.9-27.3 11.3-27.3H248V176c0-8.8 7.2-16 16-16h48c8.8 0 16 7.2 16 16v112h65.4c14.2 0 21.4 17.2 11.3 27.3z">
|
|
332
332
|
</path>
|
|
333
333
|
</svg>
|
|
@@ -371,7 +371,7 @@ build executable .pex files. This is described more thoroughly in
|
|
|
371
371
|
|
|
372
372
|
</aside>
|
|
373
373
|
</div>
|
|
374
|
-
</div><script src="_static/documentation_options.js?v=
|
|
374
|
+
</div><script src="_static/documentation_options.js?v=bfb83a69"></script>
|
|
375
375
|
<script src="_static/doctools.js?v=9bcbadda"></script>
|
|
376
376
|
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
|
377
377
|
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
|
pex/entry_points_txt.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Copyright 2025 Pex project contributors.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
3
|
+
|
|
4
|
+
from __future__ import absolute_import, print_function
|
|
5
|
+
|
|
6
|
+
import itertools
|
|
7
|
+
import os
|
|
8
|
+
from textwrap import dedent
|
|
9
|
+
|
|
10
|
+
from pex import windows
|
|
11
|
+
from pex.common import safe_open
|
|
12
|
+
from pex.dist_metadata import CallableEntryPoint, EntryPoints
|
|
13
|
+
from pex.exceptions import reportable_unexpected_error_msg
|
|
14
|
+
from pex.executables import chmod_plus_x
|
|
15
|
+
from pex.interpreter import PythonInterpreter
|
|
16
|
+
from pex.os import WINDOWS
|
|
17
|
+
from pex.typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from typing import Iterator, Optional, Text, Tuple
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def install_scripts(
|
|
24
|
+
dest_dir, # type: str
|
|
25
|
+
entry_points, # type: EntryPoints
|
|
26
|
+
interpreter=None, # type: Optional[PythonInterpreter]
|
|
27
|
+
overwrite=True, # type: bool
|
|
28
|
+
):
|
|
29
|
+
# type: (...) -> Iterator[Tuple[Text, Text]]
|
|
30
|
+
|
|
31
|
+
if not entry_points:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
if entry_points.source is None:
|
|
35
|
+
raise AssertionError(reportable_unexpected_error_msg())
|
|
36
|
+
|
|
37
|
+
script_src = entry_points.source
|
|
38
|
+
shebang = interpreter.shebang() if interpreter else "#!python"
|
|
39
|
+
for named_entry_point, gui in itertools.chain.from_iterable(
|
|
40
|
+
((value, gui) for value in entry_points.get(key, {}).values())
|
|
41
|
+
for key, gui in (("console_scripts", False), ("gui_scripts", True))
|
|
42
|
+
):
|
|
43
|
+
entry_point = named_entry_point.entry_point
|
|
44
|
+
if isinstance(entry_point, CallableEntryPoint):
|
|
45
|
+
script = dedent(
|
|
46
|
+
"""\
|
|
47
|
+
{shebang}
|
|
48
|
+
# -*- coding: utf-8 -*-
|
|
49
|
+
import importlib
|
|
50
|
+
import sys
|
|
51
|
+
|
|
52
|
+
entry_point = importlib.import_module({modname!r})
|
|
53
|
+
for attr in {attrs!r}:
|
|
54
|
+
entry_point = getattr(entry_point, attr)
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
import os
|
|
58
|
+
pex_root_fallback = os.environ.get("_PEX_ROOT_FALLBACK")
|
|
59
|
+
if pex_root_fallback:
|
|
60
|
+
import atexit
|
|
61
|
+
import shutil
|
|
62
|
+
|
|
63
|
+
atexit.register(shutil.rmtree, pex_root_fallback, True)
|
|
64
|
+
|
|
65
|
+
sys.exit(entry_point())
|
|
66
|
+
"""
|
|
67
|
+
).format(shebang=shebang, modname=entry_point.module, attrs=entry_point.attrs)
|
|
68
|
+
else:
|
|
69
|
+
script = dedent(
|
|
70
|
+
"""\
|
|
71
|
+
{shebang}
|
|
72
|
+
# -*- coding: utf-8 -*-
|
|
73
|
+
import runpy
|
|
74
|
+
import sys
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
import os
|
|
78
|
+
pex_root_fallback = os.environ.get("_PEX_ROOT_FALLBACK")
|
|
79
|
+
if pex_root_fallback:
|
|
80
|
+
import atexit
|
|
81
|
+
import shutil
|
|
82
|
+
|
|
83
|
+
atexit.register(shutil.rmtree, pex_root_fallback, True)
|
|
84
|
+
|
|
85
|
+
runpy.run_module({modname!r}, run_name="__main__", alter_sys=True)
|
|
86
|
+
sys.exit(0)
|
|
87
|
+
"""
|
|
88
|
+
).format(shebang=shebang, modname=entry_point.module)
|
|
89
|
+
script_abspath = os.path.join(dest_dir, named_entry_point.name)
|
|
90
|
+
if WINDOWS:
|
|
91
|
+
script_abspath = windows.create_script(
|
|
92
|
+
script_abspath, script, gui=gui, overwrite=overwrite
|
|
93
|
+
)
|
|
94
|
+
elif overwrite or not os.path.exists(script_abspath):
|
|
95
|
+
with safe_open(script_abspath, "w") as fp:
|
|
96
|
+
fp.write(script)
|
|
97
|
+
chmod_plus_x(fp.name)
|
|
98
|
+
yield script_src, script_abspath
|
pex/environment.py
CHANGED
|
@@ -26,6 +26,7 @@ from pex.third_party.packaging import specifiers
|
|
|
26
26
|
from pex.third_party.packaging.tags import Tag
|
|
27
27
|
from pex.tracer import TRACER
|
|
28
28
|
from pex.typing import TYPE_CHECKING
|
|
29
|
+
from pex.whl import repacked_whl
|
|
29
30
|
|
|
30
31
|
if TYPE_CHECKING:
|
|
31
32
|
from typing import (
|
|
@@ -305,12 +306,7 @@ class PEXEnvironment(object):
|
|
|
305
306
|
|
|
306
307
|
def iter_distributions(self, result_type_wheel_file=False):
|
|
307
308
|
# type: (bool) -> Iterator[FingerprintedDistribution]
|
|
308
|
-
if result_type_wheel_file:
|
|
309
|
-
if not self._pex_info.deps_are_wheel_files:
|
|
310
|
-
raise ResolveError(
|
|
311
|
-
"Cannot resolve .whl files from PEX at {pex}; its dependencies are in the "
|
|
312
|
-
"form of pre-installed wheel chroots.".format(pex=self.source_pex)
|
|
313
|
-
)
|
|
309
|
+
if result_type_wheel_file and self._pex_info.deps_are_wheel_files:
|
|
314
310
|
with TRACER.timed(
|
|
315
311
|
"Searching dependency cache: {cache}".format(
|
|
316
312
|
cache=os.path.join(self.source_pex, self._pex_info.internal_cache)
|
|
@@ -334,10 +330,17 @@ class PEXEnvironment(object):
|
|
|
334
330
|
):
|
|
335
331
|
for distribution_name, fingerprint in self._pex_info.distributions.items():
|
|
336
332
|
dist_path = os.path.join(internal_cache, distribution_name)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
333
|
+
if result_type_wheel_file:
|
|
334
|
+
yield repacked_whl(
|
|
335
|
+
installed_wheel=dist_path,
|
|
336
|
+
distribution_name=distribution_name,
|
|
337
|
+
fingerprint=fingerprint,
|
|
338
|
+
use_system_time=True,
|
|
339
|
+
)
|
|
340
|
+
else:
|
|
341
|
+
yield FingerprintedDistribution(
|
|
342
|
+
distribution=Distribution.load(dist_path), fingerprint=fingerprint
|
|
343
|
+
)
|
|
341
344
|
|
|
342
345
|
def _update_candidate_distributions(self, distribution_iter):
|
|
343
346
|
# type: (Iterable[FingerprintedDistribution]) -> None
|
pex/finders.py
CHANGED
|
@@ -15,7 +15,7 @@ from pex.dist_metadata import (
|
|
|
15
15
|
NamedEntryPoint,
|
|
16
16
|
)
|
|
17
17
|
from pex.executables import is_python_script
|
|
18
|
-
from pex.
|
|
18
|
+
from pex.installed_wheel import InstalledWheel
|
|
19
19
|
from pex.pep_503 import ProjectName
|
|
20
20
|
from pex.sysconfig import SCRIPT_DIR, script_name
|
|
21
21
|
from pex.typing import TYPE_CHECKING, cast
|
pex/installed_wheel.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Copyright 2025 Pex project contributors.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
3
|
+
|
|
4
|
+
from __future__ import absolute_import
|
|
5
|
+
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from pex.typing import TYPE_CHECKING, cast
|
|
11
|
+
from pex.util import CacheHelper
|
|
12
|
+
from pex.wheel import WHEEL, Wheel, WheelMetadataLoadError
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Optional, Text
|
|
16
|
+
|
|
17
|
+
import attr # vendor:skip
|
|
18
|
+
else:
|
|
19
|
+
import pex.third_party.attr as attr
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@attr.s(frozen=True)
|
|
23
|
+
class InstalledWheel(object):
|
|
24
|
+
class LoadError(Exception):
|
|
25
|
+
"""Indicates an installed wheel was not loadable at a particular path."""
|
|
26
|
+
|
|
27
|
+
_LAYOUT_JSON_FILENAME = ".layout.json"
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def layout_file(cls, prefix_dir):
|
|
31
|
+
# type: (str) -> str
|
|
32
|
+
return os.path.join(prefix_dir, cls._LAYOUT_JSON_FILENAME)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def save(
|
|
36
|
+
cls,
|
|
37
|
+
prefix_dir, # type: str
|
|
38
|
+
stash_dir, # type: str
|
|
39
|
+
record_relpath, # type: Text
|
|
40
|
+
root_is_purelib, # type: bool
|
|
41
|
+
):
|
|
42
|
+
# type: (...) -> InstalledWheel
|
|
43
|
+
|
|
44
|
+
# We currently need the installed wheel chroot hash for PEX-INFO / boot purposes. It is
|
|
45
|
+
# expensive to calculate; so we do it here 1 time when saving the installed wheel.
|
|
46
|
+
fingerprint = CacheHelper.dir_hash(prefix_dir, hasher=hashlib.sha256)
|
|
47
|
+
|
|
48
|
+
layout = {
|
|
49
|
+
"stash_dir": stash_dir,
|
|
50
|
+
"record_relpath": record_relpath,
|
|
51
|
+
"fingerprint": fingerprint,
|
|
52
|
+
"root_is_purelib": root_is_purelib,
|
|
53
|
+
}
|
|
54
|
+
with open(cls.layout_file(prefix_dir), "w") as fp:
|
|
55
|
+
json.dump(layout, fp, sort_keys=True, separators=(",", ":"))
|
|
56
|
+
return cls(
|
|
57
|
+
prefix_dir=prefix_dir,
|
|
58
|
+
stash_dir=stash_dir,
|
|
59
|
+
record_relpath=record_relpath,
|
|
60
|
+
fingerprint=fingerprint,
|
|
61
|
+
root_is_purelib=root_is_purelib,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def load(cls, prefix_dir):
|
|
66
|
+
# type: (str) -> InstalledWheel
|
|
67
|
+
layout_file = cls.layout_file(prefix_dir)
|
|
68
|
+
try:
|
|
69
|
+
with open(layout_file) as fp:
|
|
70
|
+
layout = json.load(fp)
|
|
71
|
+
except (IOError, OSError) as e:
|
|
72
|
+
raise cls.LoadError(
|
|
73
|
+
"Failed to load an installed wheel layout from {layout_file}: {err}".format(
|
|
74
|
+
layout_file=layout_file, err=e
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
if not isinstance(layout, dict):
|
|
78
|
+
raise cls.LoadError(
|
|
79
|
+
"The installed wheel layout file at {layout_file} must contain a single top-level "
|
|
80
|
+
"object, found: {value}.".format(layout_file=layout_file, value=layout)
|
|
81
|
+
)
|
|
82
|
+
stash_dir = layout.get("stash_dir")
|
|
83
|
+
record_relpath = layout.get("record_relpath")
|
|
84
|
+
if not stash_dir or not record_relpath:
|
|
85
|
+
raise cls.LoadError(
|
|
86
|
+
"The installed wheel layout file at {layout_file} must contain an object with both "
|
|
87
|
+
"`stash_dir` and `record_relpath` attributes, found: {value}".format(
|
|
88
|
+
layout_file=layout_file, value=layout
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
fingerprint = layout.get("fingerprint")
|
|
93
|
+
|
|
94
|
+
# N.B.: Caching root_is_purelib was not part of the original InstalledWheel layout data; so
|
|
95
|
+
# we materialize the property if needed to support older installed wheel chroots.
|
|
96
|
+
root_is_purelib = layout.get("root_is_purelib")
|
|
97
|
+
if root_is_purelib is None:
|
|
98
|
+
try:
|
|
99
|
+
wheel = WHEEL.load(prefix_dir)
|
|
100
|
+
except WheelMetadataLoadError as e:
|
|
101
|
+
raise cls.LoadError(
|
|
102
|
+
"Failed to determine if installed wheel at {location} is platform-specific: "
|
|
103
|
+
"{err}".format(location=prefix_dir, err=e)
|
|
104
|
+
)
|
|
105
|
+
root_is_purelib = wheel.root_is_purelib
|
|
106
|
+
|
|
107
|
+
return cls(
|
|
108
|
+
prefix_dir=prefix_dir,
|
|
109
|
+
stash_dir=cast(str, stash_dir),
|
|
110
|
+
record_relpath=cast(str, record_relpath),
|
|
111
|
+
fingerprint=cast("Optional[str]", fingerprint),
|
|
112
|
+
root_is_purelib=root_is_purelib,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
prefix_dir = attr.ib() # type: str
|
|
116
|
+
stash_dir = attr.ib() # type: str
|
|
117
|
+
record_relpath = attr.ib() # type: Text
|
|
118
|
+
fingerprint = attr.ib() # type: Optional[str]
|
|
119
|
+
root_is_purelib = attr.ib() # type: bool
|
|
120
|
+
|
|
121
|
+
def wheel_file_name(self):
|
|
122
|
+
# type: () -> str
|
|
123
|
+
return Wheel.load(self.prefix_dir).wheel_file_name
|
|
124
|
+
|
|
125
|
+
def stashed_path(self, *components):
|
|
126
|
+
# type: (*str) -> str
|
|
127
|
+
return os.path.join(self.prefix_dir, self.stash_dir, *components)
|
pex/interpreter.py
CHANGED
|
@@ -483,7 +483,7 @@ class PythonIdentity(object):
|
|
|
483
483
|
env_markers=self._env_markers.as_dict(),
|
|
484
484
|
configured_macosx_deployment_target=self._configured_macosx_deployment_target,
|
|
485
485
|
)
|
|
486
|
-
return json.dumps(values, sort_keys=True)
|
|
486
|
+
return json.dumps(values, sort_keys=True, separators=(",", ":"))
|
|
487
487
|
|
|
488
488
|
@property
|
|
489
489
|
def binary(self):
|
|
@@ -1525,13 +1525,19 @@ class PythonInterpreter(object):
|
|
|
1525
1525
|
self._supported_platforms = frozenset(self._identity.iter_supported_platforms())
|
|
1526
1526
|
return self._supported_platforms
|
|
1527
1527
|
|
|
1528
|
-
def shebang(
|
|
1529
|
-
|
|
1528
|
+
def shebang(
|
|
1529
|
+
self,
|
|
1530
|
+
args=None, # type: Optional[Text]
|
|
1531
|
+
encoding_line="", # type: str
|
|
1532
|
+
):
|
|
1533
|
+
# type: (...) -> Text
|
|
1530
1534
|
"""Return the contents of an appropriate shebang for this interpreter and args.
|
|
1531
1535
|
|
|
1532
1536
|
The shebang will include the leading `#!` but will not include a trailing new line character.
|
|
1533
1537
|
"""
|
|
1534
|
-
return create_shebang(
|
|
1538
|
+
return create_shebang(
|
|
1539
|
+
adjust_to_final_path(self._binary), python_args=args, encoding_line=encoding_line
|
|
1540
|
+
)
|
|
1535
1541
|
|
|
1536
1542
|
def create_isolated_cmd(
|
|
1537
1543
|
self,
|
|
@@ -1637,6 +1643,7 @@ def create_shebang(
|
|
|
1637
1643
|
python_exe, # type: Text
|
|
1638
1644
|
python_args=None, # type: Optional[Text]
|
|
1639
1645
|
max_shebang_length=MAX_SHEBANG_LENGTH, # type: int
|
|
1646
|
+
encoding_line="", # type: str
|
|
1640
1647
|
):
|
|
1641
1648
|
# type: (...) -> Text
|
|
1642
1649
|
"""Return the contents of an appropriate shebang for the given Python interpreter and args.
|
|
@@ -1662,6 +1669,7 @@ def create_shebang(
|
|
|
1662
1669
|
dedent(
|
|
1663
1670
|
"""\
|
|
1664
1671
|
#!/bin/sh
|
|
1672
|
+
{encoding_line}
|
|
1665
1673
|
# N.B.: This python script executes via a /bin/sh re-exec as a hack to work around a
|
|
1666
1674
|
# potential maximum shebang length of {max_shebang_length} bytes on this system which
|
|
1667
1675
|
# the python interpreter `exec`ed below would violate.
|
|
@@ -1669,6 +1677,10 @@ def create_shebang(
|
|
|
1669
1677
|
'''
|
|
1670
1678
|
"""
|
|
1671
1679
|
)
|
|
1672
|
-
.format(
|
|
1680
|
+
.format(
|
|
1681
|
+
encoding_line=encoding_line.rstrip(),
|
|
1682
|
+
max_shebang_length=max_shebang_length,
|
|
1683
|
+
python=python,
|
|
1684
|
+
)
|
|
1673
1685
|
.strip()
|
|
1674
1686
|
)
|
pex/interpreter_constraints.py
CHANGED
|
@@ -375,10 +375,10 @@ COMPATIBLE_PYTHON_VERSIONS = (
|
|
|
375
375
|
PythonVersion(Lifecycle.EOL, 3, 6, 15),
|
|
376
376
|
PythonVersion(Lifecycle.EOL, 3, 7, 17),
|
|
377
377
|
PythonVersion(Lifecycle.EOL, 3, 8, 20),
|
|
378
|
-
PythonVersion(Lifecycle.STABLE, 3, 9,
|
|
379
|
-
PythonVersion(Lifecycle.STABLE, 3, 10,
|
|
380
|
-
PythonVersion(Lifecycle.STABLE, 3, 11,
|
|
381
|
-
PythonVersion(Lifecycle.STABLE, 3, 12,
|
|
378
|
+
PythonVersion(Lifecycle.STABLE, 3, 9, 24),
|
|
379
|
+
PythonVersion(Lifecycle.STABLE, 3, 10, 19),
|
|
380
|
+
PythonVersion(Lifecycle.STABLE, 3, 11, 14),
|
|
381
|
+
PythonVersion(Lifecycle.STABLE, 3, 12, 12),
|
|
382
382
|
PythonVersion(Lifecycle.STABLE, 3, 13, 8),
|
|
383
383
|
PythonVersion(Lifecycle.STABLE, 3, 14, 0),
|
|
384
384
|
PythonVersion(Lifecycle.DEV, 3, 15, 0),
|