pex 2.64.1__py2.py3-none-any.whl → 2.69.0__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/bin/pex.py +2 -1
- pex/build_backend/configuration.py +5 -5
- pex/build_backend/wrap.py +2 -19
- pex/cli/commands/lock.py +4 -2
- pex/cli/commands/run.py +10 -11
- pex/cli/pex.py +11 -4
- pex/dist_metadata.py +29 -2
- pex/docs/html/_pagefind/fragment/en_4250138.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_7125dad.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_785d562.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_8e94bb8.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/{en_17782b6.pf_fragment → en_a0396bb.pf_fragment} +0 -0
- pex/docs/html/_pagefind/fragment/en_a8a3588.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_c07d988.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_d718411.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/en_a2e3c5e.pf_index +0 -0
- pex/docs/html/_pagefind/pagefind-entry.json +1 -1
- pex/docs/html/_pagefind/pagefind.en_4ce1afa9e3.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/hashing.py +71 -9
- pex/interpreter_constraints.py +1 -1
- pex/jobs.py +13 -6
- pex/pep_376.py +21 -6
- pex/pep_427.py +30 -8
- pex/pex_builder.py +1 -4
- pex/pip/local_project.py +6 -14
- pex/pip/tool.py +3 -3
- pex/pip/vcs.py +93 -44
- pex/pip/version.py +7 -0
- pex/resolve/configured_resolve.py +13 -5
- pex/resolve/lock_downloader.py +1 -0
- pex/resolve/locker.py +30 -14
- pex/resolve/lockfile/create.py +2 -7
- pex/resolve/pre_resolved_resolver.py +1 -7
- pex/resolve/project.py +233 -47
- pex/resolve/resolver_configuration.py +1 -1
- pex/resolve/resolver_options.py +14 -9
- pex/resolve/venv_resolver.py +221 -65
- pex/resolver.py +59 -55
- pex/scie/__init__.py +40 -1
- pex/scie/model.py +2 -0
- pex/scie/science.py +25 -3
- pex/sdist.py +219 -0
- pex/version.py +1 -1
- pex/wheel.py +16 -12
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/METADATA +4 -4
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/RECORD +60 -59
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/entry_points.txt +1 -0
- pex/docs/html/_pagefind/fragment/en_1048255.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_3f7efc3.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_40667cd.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_55ee2f4.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_d6d92dd.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_d834316.pf_fragment +0 -0
- pex/docs/html/_pagefind/fragment/en_ec2ce54.pf_fragment +0 -0
- pex/docs/html/_pagefind/index/en_17effb2.pf_index +0 -0
- pex/docs/html/_pagefind/pagefind.en_49ec86cf86.pf_meta +0 -0
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/WHEEL +0 -0
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/licenses/LICENSE +0 -0
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/pylock/pylock.toml +0 -0
- {pex-2.64.1.dist-info → pex-2.69.0.dist-info}/top_level.txt +0 -0
pex/scie/model.py
CHANGED
|
@@ -303,6 +303,8 @@ class ScieOptions(object):
|
|
|
303
303
|
python_version = attr.ib(
|
|
304
304
|
default=None
|
|
305
305
|
) # type: Optional[Union[Tuple[int, int], Tuple[int, int, int]]]
|
|
306
|
+
pbs_free_threaded = attr.ib(default=False) # type: bool
|
|
307
|
+
pbs_debug = attr.ib(default=False) # type: bool
|
|
306
308
|
pbs_stripped = attr.ib(default=False) # type: bool
|
|
307
309
|
hash_algorithms = attr.ib(default=()) # type: Tuple[str, ...]
|
|
308
310
|
science_binary = attr.ib(default=None) # type: Optional[Union[File, Url]]
|
pex/scie/science.py
CHANGED
|
@@ -24,6 +24,7 @@ from pex.hashing import Sha256
|
|
|
24
24
|
from pex.os import is_exe
|
|
25
25
|
from pex.pep_440 import Version
|
|
26
26
|
from pex.pex import PEX
|
|
27
|
+
from pex.pex_info import PexInfo
|
|
27
28
|
from pex.result import Error, try_
|
|
28
29
|
from pex.scie.model import (
|
|
29
30
|
File,
|
|
@@ -37,6 +38,7 @@ from pex.scie.model import (
|
|
|
37
38
|
)
|
|
38
39
|
from pex.sysconfig import SysPlatform
|
|
39
40
|
from pex.third_party.packaging.specifiers import SpecifierSet
|
|
41
|
+
from pex.third_party.packaging.utils import parse_wheel_filename
|
|
40
42
|
from pex.third_party.packaging.version import InvalidVersion
|
|
41
43
|
from pex.tracer import TRACER
|
|
42
44
|
from pex.typing import TYPE_CHECKING
|
|
@@ -66,7 +68,7 @@ class Manifest(object):
|
|
|
66
68
|
|
|
67
69
|
|
|
68
70
|
SCIENCE_RELEASES_URL = "https://github.com/a-scie/lift/releases"
|
|
69
|
-
MIN_SCIENCE_VERSION = Version("0.
|
|
71
|
+
MIN_SCIENCE_VERSION = Version("0.15.1")
|
|
70
72
|
SCIENCE_REQUIREMENT = SpecifierSet("~={min_version}".format(min_version=MIN_SCIENCE_VERSION))
|
|
71
73
|
|
|
72
74
|
|
|
@@ -104,6 +106,16 @@ class Filenames(Enum["Filenames.Value"]):
|
|
|
104
106
|
Filenames.seal()
|
|
105
107
|
|
|
106
108
|
|
|
109
|
+
def _is_free_threaded_pex(pex_info):
|
|
110
|
+
# type: (PexInfo) -> bool
|
|
111
|
+
for distribution in pex_info.distributions:
|
|
112
|
+
_, _, _, tags = parse_wheel_filename(os.path.basename(distribution))
|
|
113
|
+
for tag in tags:
|
|
114
|
+
if tag.abi.startswith(("cp", "abi3")) and "t" in tag.abi:
|
|
115
|
+
return True
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
|
|
107
119
|
def create_manifests(
|
|
108
120
|
configuration, # type: ScieConfiguration
|
|
109
121
|
name, # type: str
|
|
@@ -270,6 +282,7 @@ def create_manifests(
|
|
|
270
282
|
}
|
|
271
283
|
|
|
272
284
|
configure_binding_args = [Filenames.PEX.placeholder, Filenames.CONFIGURE_BINDING.placeholder]
|
|
285
|
+
pbs_free_threaded = _is_free_threaded_pex(pex_info) or configuration.options.pbs_free_threaded
|
|
273
286
|
for interpreter in configuration.interpreters:
|
|
274
287
|
lift = lift_template.copy()
|
|
275
288
|
|
|
@@ -285,10 +298,17 @@ def create_manifests(
|
|
|
285
298
|
interpreter.platform.qualified_file_name("{name}-lift.toml".format(name=name)),
|
|
286
299
|
)
|
|
287
300
|
|
|
301
|
+
version_str = interpreter.version_str
|
|
302
|
+
if Provider.PythonBuildStandalone is interpreter.provider:
|
|
303
|
+
if configuration.options.pbs_debug:
|
|
304
|
+
version_str += "d"
|
|
305
|
+
if pbs_free_threaded:
|
|
306
|
+
version_str += "t"
|
|
307
|
+
|
|
288
308
|
interpreter_config = {
|
|
289
309
|
"id": "python-distribution",
|
|
290
310
|
"provider": interpreter.provider.value,
|
|
291
|
-
"version":
|
|
311
|
+
"version": version_str,
|
|
292
312
|
"lazy": configuration.options.style is ScieStyle.LAZY,
|
|
293
313
|
}
|
|
294
314
|
if interpreter.release:
|
|
@@ -297,7 +317,9 @@ def create_manifests(
|
|
|
297
317
|
interpreter_config["base_url"] = "/".join(
|
|
298
318
|
(configuration.options.assets_base_url, "providers", str(interpreter.provider))
|
|
299
319
|
)
|
|
300
|
-
if Provider.PythonBuildStandalone is interpreter.provider
|
|
320
|
+
if Provider.PythonBuildStandalone is interpreter.provider and not (
|
|
321
|
+
configuration.options.pbs_debug or pbs_free_threaded
|
|
322
|
+
):
|
|
301
323
|
interpreter_config.update(
|
|
302
324
|
flavor=(
|
|
303
325
|
"install_only_stripped"
|
pex/sdist.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
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 copy
|
|
7
|
+
import os.path
|
|
8
|
+
import sys
|
|
9
|
+
import tarfile
|
|
10
|
+
from tarfile import TarInfo
|
|
11
|
+
|
|
12
|
+
from pex.compatibility import commonpath
|
|
13
|
+
from pex.typing import TYPE_CHECKING, cast
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from typing import Any, Dict, Optional, Text, TypeVar
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FilterError(tarfile.TarError):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AbsolutePathError(FilterError):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class OutsideDestinationError(FilterError):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SpecialFileError(FilterError):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AbsoluteLinkError(FilterError):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LinkOutsideDestinationError(FilterError):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_REALPATH_KWARGS = (
|
|
44
|
+
{"strict": getattr(os.path, "ALLOW_MISSING", False)} if sys.version_info[:2] >= (3, 10) else {}
|
|
45
|
+
) # type: Dict[str, Any]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
_Text = TypeVar("_Text", str, Text)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _realpath(path):
|
|
53
|
+
# type: (_Text) -> _Text
|
|
54
|
+
return os.path.realpath(path, **_REALPATH_KWARGS)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _get_filtered_attrs(
|
|
58
|
+
member, # type: TarInfo
|
|
59
|
+
dest_path, # type: Text
|
|
60
|
+
for_data=True, # type: bool
|
|
61
|
+
):
|
|
62
|
+
# type: (...) -> Dict[str, Any]
|
|
63
|
+
|
|
64
|
+
# N.B.: Copied from CPython 3.14 stdlib tarfile.py
|
|
65
|
+
# Modifications:
|
|
66
|
+
# + Exception types replicated with error messages placed at call site.
|
|
67
|
+
# + `os.path.realpath` -> `_realpath` to deal with `strict` parameter.
|
|
68
|
+
# + `os.path.commonpath` -> `pex.compatibility.commonpath`
|
|
69
|
+
# + `mode = None` guarded by `sys.version_info[:2] >= (3, 12)` with commentary.
|
|
70
|
+
# + `{uid,gid,uname,gname} = None` guarded by `sys.version_info[:2] >= (3, 12)` with commentary.
|
|
71
|
+
|
|
72
|
+
new_attrs = {} # type: Dict[str, Any]
|
|
73
|
+
name = member.name
|
|
74
|
+
dest_path = _realpath(dest_path)
|
|
75
|
+
# Strip leading / (tar's directory separator) from filenames.
|
|
76
|
+
# Include os.sep (target OS directory separator) as well.
|
|
77
|
+
if name.startswith(("/", os.sep)):
|
|
78
|
+
name = new_attrs["name"] = member.path.lstrip("/" + os.sep)
|
|
79
|
+
if os.path.isabs(name):
|
|
80
|
+
# Path is absolute even after stripping.
|
|
81
|
+
# For example, 'C:/foo' on Windows.
|
|
82
|
+
raise AbsolutePathError("member {name!r} has an absolute path".format(name=member.name))
|
|
83
|
+
# Ensure we stay in the destination
|
|
84
|
+
target_path = _realpath(os.path.join(dest_path, name))
|
|
85
|
+
if commonpath([target_path, dest_path]) != dest_path:
|
|
86
|
+
raise OutsideDestinationError(
|
|
87
|
+
"{name!r} would be extracted to {path!r}, which is outside the destination".format(
|
|
88
|
+
name=member.name, path=target_path
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
# Limit permissions (no high bits, and go-w)
|
|
92
|
+
mode = member.mode # type: Optional[int]
|
|
93
|
+
if mode is not None:
|
|
94
|
+
# Strip high bits & group/other write bits
|
|
95
|
+
mode = mode & 0o755
|
|
96
|
+
if for_data:
|
|
97
|
+
# For data, handle permissions & file types
|
|
98
|
+
if member.isreg() or member.islnk():
|
|
99
|
+
if not mode & 0o100:
|
|
100
|
+
# Clear executable bits if not executable by user
|
|
101
|
+
mode &= ~0o111
|
|
102
|
+
# Ensure owner can read & write
|
|
103
|
+
mode |= 0o600
|
|
104
|
+
elif member.isdir() or member.issym():
|
|
105
|
+
if sys.version_info[:2] >= (3, 12):
|
|
106
|
+
# Ignore mode for directories & symlinks
|
|
107
|
+
mode = None
|
|
108
|
+
else:
|
|
109
|
+
# Retain stripped mode since older Pythons do not support None.
|
|
110
|
+
pass
|
|
111
|
+
else:
|
|
112
|
+
# Reject special files
|
|
113
|
+
raise SpecialFileError("{name!r} is a special file".format(name=member.name))
|
|
114
|
+
if mode != member.mode:
|
|
115
|
+
new_attrs["mode"] = mode
|
|
116
|
+
if for_data:
|
|
117
|
+
if sys.version_info[:2] >= (3, 12):
|
|
118
|
+
# Ignore ownership for 'data'
|
|
119
|
+
if member.uid is not None:
|
|
120
|
+
new_attrs["uid"] = None
|
|
121
|
+
if member.gid is not None:
|
|
122
|
+
new_attrs["gid"] = None
|
|
123
|
+
if member.uname is not None:
|
|
124
|
+
new_attrs["uname"] = None
|
|
125
|
+
if member.gname is not None:
|
|
126
|
+
new_attrs["gname"] = None
|
|
127
|
+
else:
|
|
128
|
+
# Retain uid/gid/uname/gname since older Pythons do not support None.
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
# Check link destination for 'data'
|
|
132
|
+
if member.islnk() or member.issym():
|
|
133
|
+
if os.path.isabs(member.linkname):
|
|
134
|
+
raise AbsoluteLinkError(
|
|
135
|
+
"{name!r} is a link to an absolute path".format(name=member.name)
|
|
136
|
+
)
|
|
137
|
+
normalized = os.path.normpath(member.linkname)
|
|
138
|
+
if normalized != member.linkname:
|
|
139
|
+
new_attrs["linkname"] = normalized
|
|
140
|
+
if member.issym():
|
|
141
|
+
target_path = os.path.join(dest_path, os.path.dirname(name), member.linkname)
|
|
142
|
+
else:
|
|
143
|
+
target_path = os.path.join(dest_path, member.linkname)
|
|
144
|
+
target_path = _realpath(target_path)
|
|
145
|
+
if commonpath([target_path, dest_path]) != dest_path:
|
|
146
|
+
raise LinkOutsideDestinationError(
|
|
147
|
+
"{name!r} would link to {path!r}, which is outside the destination".format(
|
|
148
|
+
name=member.name, path=target_path
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
return new_attrs
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _replace(
|
|
155
|
+
member, # type: TarInfo
|
|
156
|
+
attrs, # type: Dict[str, Any]
|
|
157
|
+
):
|
|
158
|
+
# type: (...) -> TarInfo
|
|
159
|
+
|
|
160
|
+
replace = getattr(member, "replace", None)
|
|
161
|
+
if replace:
|
|
162
|
+
attrs["deep"] = False
|
|
163
|
+
return cast(TarInfo, replace(**attrs))
|
|
164
|
+
|
|
165
|
+
result = copy.copy(member)
|
|
166
|
+
for attr, value in attrs.items():
|
|
167
|
+
setattr(result, attr, value)
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _data_filter(
|
|
172
|
+
member, # type: TarInfo
|
|
173
|
+
dest_path, # type: Text
|
|
174
|
+
):
|
|
175
|
+
# type: (...) -> TarInfo
|
|
176
|
+
new_attrs = _get_filtered_attrs(member, dest_path, True)
|
|
177
|
+
if new_attrs:
|
|
178
|
+
return _replace(member, new_attrs)
|
|
179
|
+
return member
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
_EXTRACTALL_DATA_FILTER_KWARGS = {"filter": "data"} # type: Dict[str, Any]
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class InvalidSourceDistributionError(ValueError):
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def extract_tarball(
|
|
190
|
+
tarball_path, # type: Text
|
|
191
|
+
dest_dir, # type: _Text
|
|
192
|
+
):
|
|
193
|
+
# type: (...) -> _Text
|
|
194
|
+
|
|
195
|
+
with tarfile.open(tarball_path) as tf:
|
|
196
|
+
if sys.version_info[:2] >= (3, 12):
|
|
197
|
+
tf.extractall(dest_dir, **_EXTRACTALL_DATA_FILTER_KWARGS)
|
|
198
|
+
else:
|
|
199
|
+
for tar_info in tf: # type: ignore[unreachable]
|
|
200
|
+
tar_info = _data_filter(tar_info, dest_dir)
|
|
201
|
+
tf.extract(tar_info, dest_dir)
|
|
202
|
+
|
|
203
|
+
listing = os.listdir(dest_dir)
|
|
204
|
+
if len(listing) != 1:
|
|
205
|
+
raise InvalidSourceDistributionError(
|
|
206
|
+
"Expected one top-level project directory to be extracted from {project}, "
|
|
207
|
+
"found {count}: {listing}".format(
|
|
208
|
+
project=tarball_path, count=len(listing), listing=", ".join(listing)
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
project_dir = os.path.join(dest_dir, listing[0])
|
|
213
|
+
if not os.path.isdir(project_dir):
|
|
214
|
+
raise InvalidSourceDistributionError(
|
|
215
|
+
"Expected one top-level project directory to be extracted from {project}, "
|
|
216
|
+
"found file: {path}".format(project=tarball_path, path=listing[0])
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return project_dir
|
pex/version.py
CHANGED
pex/wheel.py
CHANGED
|
@@ -42,7 +42,7 @@ class WHEEL(object):
|
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
45
|
-
def
|
|
45
|
+
def from_metadata_files(cls, metadata_files):
|
|
46
46
|
# type: (MetadataFiles) -> WHEEL
|
|
47
47
|
|
|
48
48
|
metadata_bytes = metadata_files.read("WHEEL")
|
|
@@ -52,7 +52,18 @@ class WHEEL(object):
|
|
|
52
52
|
wheel=metadata_files.render_description(metadata_file_name="WHEEL")
|
|
53
53
|
)
|
|
54
54
|
)
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
# Some WHEEL metadata in the wild has blank lines in between headers when it should not.
|
|
57
|
+
# Since WHEEL metadata, unlike METADATA, does not use the message body to convey metadata,
|
|
58
|
+
# this should be safe.
|
|
59
|
+
#
|
|
60
|
+
# See here for initial discovery case:
|
|
61
|
+
# https://github.com/pex-tool/pex/issues/2998#issuecomment-3492998265
|
|
62
|
+
normalized_metadata = b"".join(
|
|
63
|
+
line for line in metadata_bytes.splitlines(True) if line.strip()
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
metadata = parse_message(normalized_metadata)
|
|
56
67
|
return cls(files=metadata_files, metadata=metadata)
|
|
57
68
|
|
|
58
69
|
_CACHE = {} # type: Dict[Tuple[Text, Optional[ProjectName]], WHEEL]
|
|
@@ -73,7 +84,7 @@ class WHEEL(object):
|
|
|
73
84
|
raise WheelMetadataLoadError(
|
|
74
85
|
"Could not find any metadata in {wheel}.".format(wheel=location)
|
|
75
86
|
)
|
|
76
|
-
wheel = cls.
|
|
87
|
+
wheel = cls.from_metadata_files(metadata_files)
|
|
77
88
|
cls._CACHE[(location, project_name)] = wheel
|
|
78
89
|
return wheel
|
|
79
90
|
|
|
@@ -84,7 +95,7 @@ class WHEEL(object):
|
|
|
84
95
|
project_name = distribution.metadata.project_name
|
|
85
96
|
wheel = cls._CACHE.get((location, project_name))
|
|
86
97
|
if not wheel:
|
|
87
|
-
wheel = cls.
|
|
98
|
+
wheel = cls.from_metadata_files(distribution.metadata.files)
|
|
88
99
|
cls._CACHE[(location, project_name)] = wheel
|
|
89
100
|
return wheel
|
|
90
101
|
|
|
@@ -144,14 +155,7 @@ class Wheel(object):
|
|
|
144
155
|
if wheel:
|
|
145
156
|
metadata = wheel
|
|
146
157
|
else:
|
|
147
|
-
|
|
148
|
-
if not wheel_data:
|
|
149
|
-
raise WheelMetadataLoadError(
|
|
150
|
-
"Could not find WHEEL metadata in {source}.".format(
|
|
151
|
-
source=cls._source(location, metadata_files)
|
|
152
|
-
)
|
|
153
|
-
)
|
|
154
|
-
metadata = WHEEL(files=metadata_files, metadata=parse_message(wheel_data))
|
|
158
|
+
metadata = WHEEL.from_metadata_files(metadata_files)
|
|
155
159
|
|
|
156
160
|
wheel_metadata_dir = os.path.dirname(metadata_files.metadata.rel_path)
|
|
157
161
|
if not wheel_metadata_dir.endswith(".dist-info"):
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pex
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.69.0
|
|
4
4
|
Summary: The PEX packaging toolchain.
|
|
5
5
|
Home-page: https://github.com/pex-tool/pex
|
|
6
|
-
Download-URL: https://github.com/pex-tool/pex/releases/download/v2.
|
|
6
|
+
Download-URL: https://github.com/pex-tool/pex/releases/download/v2.69.0/pex
|
|
7
7
|
Author: The PEX developers
|
|
8
8
|
Author-email: developers@pex-tool.org
|
|
9
9
|
License-Expression: Apache-2.0
|
|
10
|
-
Project-URL: Changelog, https://github.com/pex-tool/pex/blob/v2.
|
|
10
|
+
Project-URL: Changelog, https://github.com/pex-tool/pex/blob/v2.69.0/CHANGES.md
|
|
11
11
|
Project-URL: Documentation, https://docs.pex-tool.org/
|
|
12
|
-
Project-URL: Source, https://github.com/pex-tool/pex/tree/v2.
|
|
12
|
+
Project-URL: Source, https://github.com/pex-tool/pex/tree/v2.69.0
|
|
13
13
|
Keywords: package,executable,virtualenv,lock,freeze
|
|
14
14
|
Classifier: Development Status :: 5 - Production/Stable
|
|
15
15
|
Classifier: Intended Audience :: Developers
|