Skip to content

Commit fe45ee9

Browse files
authored
Split Pex .whl into two .whls. (#3057)
We now publish two wheels: 1. `pex-<rev>.py27.py35.py36.py37.py38.py39.py310.py311-none-any.whl`: Targets exactly the Pythons in the python tag. 2. `pex-<rev>.py3.py312-none-any.whl`: Targets Python>=3.12. The 1st wheel carries the same contents as the existing `pex-<rev>.py2.py3-none-any.whl` wheel and the 2nd wheel (`pex-<rev>.py3.py312-none-any.whl`) ships without vendored `pip`, `setuptools`, `toml` or `tomli` since these are either unusable or un-needed under Python>=3.12. Fixes #1526 Fixes #1527 Fixes #1528 Fixes #1877 Fixes #2731 Fixes #2785
1 parent 4576b5a commit fe45ee9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1386
-211
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ jobs:
7272
run: |
7373
uv run dev-cmd package -- \
7474
--additional-format sdist \
75-
--additional-format wheel \
75+
--additional-format whl \
76+
--additional-format whl-3.12-plus \
7677
--embed-docs \
7778
--clean-docs \
7879
--scies \

.github/workflows/release.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,15 @@ jobs:
6464
python-version: "3.11"
6565
- name: Install uv
6666
uses: astral-sh/setup-uv@v5
67-
- name: Build sdist and wheel
68-
run: uv run dev-cmd package -- --no-pex --additional-format sdist --additional-format wheel --embed-docs --clean-docs
67+
- name: Build sdist and wheels
68+
run: |
69+
uv run dev-cmd package -- \
70+
--no-pex \
71+
--additional-format sdist \
72+
--additional-format whl \
73+
--additional-format whl-3.12-plus \
74+
--embed-docs \
75+
--clean-docs
6976
- name: Publish Pex ${{ needs.determine-tag.outputs.release-tag }}
7077
uses: pypa/gh-action-pypi-publish@release/v1
7178
with:

CHANGES.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Release Notes
22

3+
## 2.77.0
4+
5+
This release has no fixes or new features per-se, but just changes the set of distributions that
6+
Pex releases to PyPI. Previously Pex released an sdist and a universal (`py2.py3-none-any`) `.whl`.
7+
Pex now releases two wheels in addition to the sdist. The `py3.py312-none-any.whl` targets
8+
Python>=3.12 and has un-needed vendored libraries elided making it bith a smaller `.whl` and less
9+
prone to false-positive security scan issues since unused vendored code is now omitted. The other
10+
wheel carries the same contents as prior and supports creating PEXes for Python 2.7 and
11+
Python>=3.5,<3.12.
12+
13+
* Split Pex `.whl` into two `.whl`s. (#3057)
14+
315
## 2.76.1
416

517
This release fixes bootstrapping of Pips specified via `--pip-version` to respect Pex Pip

build-backend/pex_build/__init__.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,25 @@
1414
from pex.common import safe_mkdir, safe_mkdtemp
1515
from pex.third_party.packaging.markers import Marker
1616
from pex.typing import TYPE_CHECKING
17+
from pex.vendor import iter_vendor_specs
1718

1819
if TYPE_CHECKING:
1920
from typing import Callable, Iterator, Optional
2021

21-
INCLUDE_DOCS = os.environ.get("__PEX_BUILD_INCLUDE_DOCS__", "False").lower() in ("1", "true")
22+
23+
def read_bool_env(
24+
name, # type: str
25+
default, # type: bool
26+
):
27+
# type: (...) -> bool
28+
value = os.environ.get(name)
29+
if value is None:
30+
return default
31+
return value.lower() in ("1", "true")
32+
33+
34+
INCLUDE_DOCS = read_bool_env("__PEX_BUILD_INCLUDE_DOCS__", default=False)
35+
WHEEL_3_12_PLUS = read_bool_env("__PEX_BUILD_WHL_3_12_PLUS__", default=False)
2236

2337

2438
@contextmanager
@@ -128,3 +142,8 @@ def modify_wheel(
128142
out_dir,
129143
]
130144
)
145+
if WHEEL_3_12_PLUS:
146+
for vendor_spec in iter_vendor_specs():
147+
if vendor_spec.key in ("pip", "setuptools", "toml", "tomli"):
148+
shutil.rmtree(os.path.join(wheel_dir, vendor_spec.relpath))
149+
print("Removed un-needed vendored library", vendor_spec.relpath, file=sys.stderr)

pex/environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def _import_pkg_resources():
5959
except ImportError:
6060
from pex import third_party
6161

62-
third_party.install(expose=["setuptools"])
62+
third_party.install(expose_if_available=["setuptools"])
6363
try:
6464
import pkg_resources # vendor:skip
6565

pex/pep_425.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ class RankedTag(object):
4949
rank = attr.ib() # type: TagRank
5050

5151
def select_higher_rank(self, other):
52-
# type: (RankedTag) -> RankedTag
52+
# type: (Optional[RankedTag]) -> RankedTag
53+
if other is None:
54+
return self
5355
return Rank.select_highest_rank(
5456
self, other, extract_rank=lambda ranked_tag: ranked_tag.rank
5557
)

pex/pep_427.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ class InstallableType(Enum["InstallableType.Value"]):
7676
class Value(Enum.Value):
7777
pass
7878

79-
INSTALLED_WHEEL_CHROOT = Value("installed wheel chroot")
80-
WHEEL_FILE = Value(".whl file")
79+
INSTALLED_WHEEL_CHROOT = Value("installed-wheel-chroot")
80+
WHEEL_FILE = Value(".whl-file")
8181

8282

8383
InstallableType.seal()
@@ -974,8 +974,9 @@ def is_entry_point_script(script_path):
974974
if not compile and installed_file.path.endswith(".pyc"):
975975
continue
976976

977-
src_file = os.path.realpath(os.path.join(wheel.location, installed_file.path))
978-
if not os.path.exists(src_file):
977+
src_file = os.path.normpath(os.path.join(wheel.location, installed_file.path))
978+
src_file_realpath = os.path.realpath(src_file)
979+
if not os.path.exists(src_file_realpath):
979980
if not warned_bad_record:
980981
pex_warnings.warn(
981982
"The wheel {whl} has a bad RECORD. Skipping install of non-existent file "
@@ -989,17 +990,24 @@ def is_entry_point_script(script_path):
989990
dst_components = None # type: Optional[Tuple[Text, Text, bool]]
990991
for path_name, installed_path in wheel.iter_install_paths_by_name():
991992
installed_path = os.path.realpath(installed_path)
992-
if installed_path == commonpath((installed_path, src_file)):
993+
994+
src_path = None # type: Optional[Text]
995+
if installed_path == commonpath((installed_path, src_file_realpath)):
996+
src_path = src_file_realpath
997+
elif installed_path == commonpath((installed_path, src_file)):
998+
src_path = src_file
999+
1000+
if src_path:
9931001
rewrite_script = False
9941002
if "scripts" == path_name:
995-
if is_entry_point_script(src_file):
1003+
if is_entry_point_script(src_path):
9961004
# This entry point script will be installed afresh below as needed.
9971005
break
9981006
rewrite_script = interpreter is not None and is_python_script(
999-
src_file, check_executable=False
1007+
src_path, check_executable=False
10001008
)
10011009

1002-
dst_rel_path = os.path.relpath(src_file, installed_path)
1010+
dst_rel_path = os.path.relpath(src_path, installed_path)
10031011
dst_components = path_name, dst_rel_path, rewrite_script
10041012
break
10051013
else:

pex/pex_builder.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,11 @@ def _prepare_bootstrap(self):
543543
# NB: We use pip here in the builder, but that's only at build time, and
544544
# although we don't use pyparsing directly, packaging.markers, which we
545545
# do use at runtime, does.
546-
root_module_names = ["appdirs", "attr", "colors", "packaging", "pkg_resources", "pyparsing"]
546+
root_module_names = ["appdirs", "attr", "colors", "packaging", "pyparsing"]
547+
for vendor_spec in vendor.iter_vendor_specs():
548+
if vendor_spec.key == "setuptools":
549+
root_module_names.append("pkg_resources")
550+
547551
prepared_sources = vendor.vendor_runtime(
548552
chroot=self._chroot,
549553
dest_basedir=self._pex_info.bootstrap,

pex/pip/installation.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -278,14 +278,7 @@ def bootstrap_pip():
278278
try:
279279
bootstrap_pip_version = PipVersion.for_value(pip_version.raw)
280280
except ValueError:
281-
# Backstop with the version of Pip CPython 3.12.0 shipped with. This should be the
282-
# oldest Pip we need in the Pip bootstrap process (which is only required for
283-
# Python >= 3.12 which have distutils removed rendering vendored Pip unusable).
284-
bootstrap_pip_version = PipVersion.v23_2
285-
for rev in sorted(PipVersion.values(), reverse=True):
286-
if pip_version > rev.version:
287-
bootstrap_pip_version = rev
288-
break
281+
bootstrap_pip_version = PipVersionValue(pip_version.raw)
289282

290283
pip = BootstrapPip(
291284
pip_venv=PipVenv(
@@ -388,7 +381,7 @@ def _resolved_installation(
388381
warn=False,
389382
)
390383
)
391-
if bootstrap_pip_version is PipVersion.VENDORED:
384+
if bootstrap_pip_version is PipVersion.VENDORED and PipVersion.VENDORED.available:
392385

393386
def resolve_distribution_locations():
394387
for resolved_distribution in resolver.resolve_requirements(
@@ -555,7 +548,7 @@ def get_pip(
555548
pip = _PIP.get(installation)
556549
if pip is None:
557550
installation.check_python_applies()
558-
if installation.version is PipVersion.VENDORED:
551+
if installation.version is PipVersion.VENDORED and PipVersion.VENDORED.available:
559552
pip = _vendored_installation(
560553
interpreter=interpreter,
561554
resolver=resolver,

pex/pip/tool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ def _calculate_resolver_version_args(
365365
if (
366366
resolver_version == ResolverVersion.PIP_2020
367367
and interpreter.version[0] == 2
368-
and self.version.version < PipVersion.v22_3.version
368+
and PipVersion.VENDORED <= self.version < PipVersion.v22_3
369369
):
370370
yield "--use-feature"
371371
yield "2020-resolver"
@@ -398,7 +398,7 @@ def _spawn_pip_isolated(
398398
# We are not interactive.
399399
"--no-input",
400400
]
401-
if self.version < PipVersion.v25_0:
401+
if PipVersion.VENDORED <= self.version < PipVersion.v25_0:
402402
# If we want to warn about a version of python we support, we should do it, not pip.
403403
# That said, the option does nothing in Pip 25.0 and is deprecated and slated for
404404
# removal.

0 commit comments

Comments
 (0)