Skip to content

Commit c66a7dd

Browse files
authored
Support wrapping build_editable in wrap. (#3105)
1 parent cf3b5f5 commit c66a7dd

File tree

9 files changed

+122
-19
lines changed

9 files changed

+122
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ __pycache__/
1111
/.mypy_cache/
1212
/dist/
1313
/docs/_static_dynamic/
14+
/pex/docs/html/
1415
/pex/windows/stubs/
1516
/docker/cache/.env
1617

CHANGES.md

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

3+
## 2.90.0
4+
5+
This release adds support for wrapping PEP-660 `build_editable` to `pex_build.setuptools.build`
6+
plugins and dogfoods this.
7+
8+
* Support wrapping `build_editable` in wrap. (#3105)
9+
310
## 2.89.1
411

512
This release adds better diagnostics for certain Pex filesystem interaction errors.

build-backend/pex_build/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,25 @@ def modify_wheel(
147147
if vendor_spec.key in ("pip", "setuptools", "toml", "tomli"):
148148
shutil.rmtree(os.path.join(wheel_dir, vendor_spec.relpath))
149149
print("Removed un-needed vendored library", vendor_spec.relpath, file=sys.stderr)
150+
151+
152+
PEX_DIR = os.path.abspath(os.path.join(__file__, "..", "..", ".."))
153+
154+
155+
def modify_editable(
156+
wheel_dir, # type: str
157+
dist_info_dir=None, # type: Optional[str]
158+
):
159+
# type: (...) -> None
160+
for stub in windows.fetch_all_stubs(PEX_DIR):
161+
print("Embedded Windows script stub", stub.path, file=sys.stderr)
162+
if INCLUDE_DOCS:
163+
out_dir = os.path.join(PEX_DIR, "pex", "docs")
164+
subprocess.check_call(
165+
args=[
166+
sys.executable,
167+
os.path.join("scripts", "build-docs.py"),
168+
"--clean-html",
169+
out_dir,
170+
]
171+
)

build-backend/pex_build/setuptools/build.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@
1616
from typing import Any, Dict, List, Optional
1717

1818

19-
def get_requires_for_build_editable(config_settings=None):
20-
# type: (Optional[Dict[str, Any]]) -> List[str]
21-
22-
# N.B.: The default setuptools implementation would eventually return nothing, but only after
23-
# running code that can temporarily pollute our project directory, foiling concurrent test runs;
24-
# so we short-circuit the answer here. Faster and safer.
25-
return []
26-
27-
2819
def get_requires_for_build_sdist(config_settings=None):
2920
# type: (Optional[Dict[str, Any]]) -> List[str]
3021

@@ -105,3 +96,10 @@ def prepare_metadata_for_build_wheel(
10596
metadata_directory, config_settings=config_settings
10697
),
10798
)
99+
100+
101+
@serialized_build
102+
def get_requires_for_build_editable(config_settings=None):
103+
# type: (Optional[Dict[str, Any]]) -> List[str]
104+
105+
return cast("List[str]", get_requires_for_build_wheel(config_settings))

pex/build_backend/configuration.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,36 @@ def wrap(cls, plugin):
5050
"The `modify_wheel` attribute of pex.build_backend.wrap plugin {plugin} must be a "
5151
"callable but given {value} of type {type}.".format(
5252
plugin=qualified_name(plugin),
53-
value=modify_sdist,
54-
type=type(modify_sdist).__name__,
53+
value=modify_wheel,
54+
type=type(modify_wheel).__name__,
5555
)
5656
)
5757

58-
if not modify_sdist and not modify_wheel:
58+
modify_editable = getattr(plugin, "modify_editable", None)
59+
if modify_editable and not callable(modify_editable):
5960
raise ConfigurationError(
60-
"The pex.build_backend.wrap plugin {plugin} must define a `modify_sdist` function, "
61-
"a `modify_wheel` or both; it has neither.".format(plugin=qualified_name(plugin))
61+
"The `modify_editable` attribute of pex.build_backend.wrap plugin {plugin} must be "
62+
"a callable but given {value} of type {type}.".format(
63+
plugin=qualified_name(plugin),
64+
value=modify_editable,
65+
type=type(modify_editable).__name__,
66+
)
6267
)
6368

64-
return cls(modify_sdist=modify_sdist, modify_wheel=modify_wheel)
69+
if not modify_sdist and not modify_wheel and not modify_editable:
70+
raise ConfigurationError(
71+
"The pex.build_backend.wrap plugin {plugin} must define at least one plugin "
72+
"function: `modify_sdist`, `modify_wheel` or `modify_editable`; "
73+
"it has none.".format(plugin=qualified_name(plugin))
74+
)
75+
76+
return cls(
77+
modify_sdist=modify_sdist, modify_wheel=modify_wheel, modify_editable=modify_editable
78+
)
6579

6680
_modify_sdist = attr.ib() # type: Optional[Callable[[Text], None]]
6781
_modify_wheel = attr.ib() # type: Optional[Callable[[Text, Text], None]]
82+
_modify_editable = attr.ib() # type: Optional[Callable[[Text, Optional[Text]], None]]
6883

6984
@property
7085
def modifies_sdists(self):
@@ -92,6 +107,21 @@ def modify_wheel(
92107
return self._modify_wheel(wheel_dir, dist_info_dir_relpath)
93108
return None
94109

110+
@property
111+
def modifies_editable(self):
112+
# type: () -> bool
113+
return self._modify_editable is not None
114+
115+
def modify_editable(
116+
self,
117+
wheel_dir, # type: Text
118+
dist_info_dir=None, # type: Optional[Text]
119+
):
120+
# type: (...) -> Any
121+
if self._modify_editable:
122+
return self._modify_editable(wheel_dir, dist_info_dir)
123+
return None
124+
95125

96126
def _check_plugin(
97127
plugin, # type: Any

pex/build_backend/wrap.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,42 @@ def build_wheel(
169169
zf.writestr(record_zinfo, get_csv_bytes())
170170

171171
return wheel_name
172+
173+
174+
def build_editable(
175+
wheel_directory, # type: str
176+
config_settings=None, # type: Optional[Dict[str, Any]]
177+
metadata_directory=None, # type: Optional[str]
178+
):
179+
# type: (...) -> str
180+
181+
wheel_name = cast(
182+
str,
183+
_CONFIG.build_backend.build_editable( # type: ignore[attr-defined]
184+
wheel_directory, config_settings, metadata_directory
185+
),
186+
)
187+
188+
plugins = tuple(plugin for plugin in _CONFIG.plugins if plugin.modifies_editable)
189+
if not plugins:
190+
return wheel_name
191+
192+
if not metadata_directory:
193+
entries = glob.glob(os.path.join(wheel_directory, "*.dist-info"))
194+
if len(entries) > 1:
195+
raise BuildError(
196+
"Calling `{backend}.build_editable` produced an wheel with unexpected contents.\n"
197+
"Expected expected one top-level <project>-<version>.dist-info directory but found "
198+
"{count}:\n"
199+
"{entries}".format(
200+
backend=_CONFIG.delegate_build_backend,
201+
count=len(entries),
202+
entries="\n".join(entries),
203+
)
204+
)
205+
metadata_directory = entries[0] if entries else None
206+
207+
for plugin in plugins:
208+
plugin.modify_editable(wheel_dir=wheel_directory, dist_info_dir=metadata_directory)
209+
210+
return wheel_name

pex/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Copyright 2015 Pex project contributors.
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4-
__version__ = "2.89.1"
4+
__version__ = "2.90.0"

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,11 @@ vendor = [
200200
]
201201

202202
[tool.uv]
203+
cache-keys = [
204+
{ file = "pex/docs/html/index.html" },
205+
{ file = "pex/windows/stubs/*.exe" },
206+
]
207+
203208
# This is used to force past a <2 upper bound in blacks deps, and it turns out black works just fine
204209
# with this version of tomli.
205210
override-dependencies = ["tomli==2.2.1; python_version >= '3.8'"]

tests/build_backend/test_configuration.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,14 +296,15 @@ class InvalidPlugin(object):
296296
pass
297297

298298

299-
def test_load_config_plugin_neither(write_config):
299+
def test_load_config_plugin_none(write_config):
300300
# type: (WriteConfig) -> None
301301

302302
with pytest.raises(
303303
ConfigurationError,
304304
match=re.escape(
305-
"The pex.build_backend.wrap plugin test_configuration.InvalidPlugin must define a "
306-
"`modify_sdist` function, a `modify_wheel` or both; it has neither."
305+
"The pex.build_backend.wrap plugin test_configuration.InvalidPlugin must define at "
306+
"least one plugin function: `modify_sdist`, `modify_wheel` or `modify_editable`; "
307+
"it has none."
307308
),
308309
):
309310
write_config(

0 commit comments

Comments
 (0)