11"""
2- Vendor toml-fmt-common into the wheel , as a PEP 517 backend and as a CLI patcher.
2+ Vendor toml-fmt-common into what we build , as a PEP 517 backend and as a CLI patcher.
33
44A new toml-fmt-common release must never break an already published consumer
5- (tox-dev/toml-fmt#355), so the wheel is made self-contained instead of depending on it.
5+ (tox-dev/toml-fmt#355), so every artifact is made self-contained instead of depending on it.
66
77The CLI entry point exists because CI builds wheels with ``maturin build``, which never
88invokes a PEP 517 backend; the same patch then runs on maturin-action's output.
1717
1818from base64 import urlsafe_b64encode
1919from hashlib import sha256
20+ from io import BytesIO
2021from os import environ
2122from pathlib import Path
2223from re import findall , search , sub
2324from shutil import copy2
2425from sys import argv
26+ from tarfile import TarInfo
27+ from tarfile import open as tar_open
2528from tempfile import mkdtemp
2629from typing import TYPE_CHECKING
2730from zipfile import ZIP_DEFLATED , ZipFile
2831
2932import maturin
3033
3134if TYPE_CHECKING :
32- from collections .abc import Mapping
35+ from collections .abc import Callable , Iterator , Mapping
3336
3437 ConfigSettings = Mapping [str , str | list [str ]]
3538
3639# our wrapper backend is intentional; silence maturin's missing-backend warning
3740environ .setdefault ("MATURIN_NO_MISSING_BUILD_BACKEND_WARNING" , "1" )
3841
3942_HERE = Path (__file__ ).resolve ().parent
40- _MODULE = _HERE .name .replace ("-" , "_" )
41- _COMMON = _HERE .parent / "toml-fmt-common"
43+ _MODULE = findall (r'(?m)^name = "(.*)"' , (_HERE / "pyproject.toml" ).read_text ())[0 ].replace ("-" , "_" )
4244_VENDOR = "toml_fmt_common"
45+ # a checkout keeps toml-fmt-common beside the package; the sdist carries it within
46+ _COMMON = _HERE / "toml-fmt-common" if (_HERE / "toml-fmt-common" ).is_dir () else _HERE .parent / "toml-fmt-common"
4347
4448
4549def build_wheel (
4650 wheel_directory : str ,
4751 config_settings : ConfigSettings | None = None ,
4852 metadata_directory : str | None = None ,
4953) -> str :
50- if not (_COMMON / "src" / _VENDOR ).is_dir (): # no workspace (e.g. building from sdist)
51- return maturin .build_wheel (wheel_directory , config_settings , metadata_directory )
52- tmp = Path (mkdtemp ())
53- name = maturin .build_wheel (str (tmp ), config_settings , metadata_directory )
54- vendor_into_wheel (tmp / name )
55- copy2 (tmp / name , Path (wheel_directory ) / name )
56- return name
54+ return built (maturin .build_wheel , wheel_directory , config_settings , metadata_directory , vendor_into_wheel )
5755
5856
5957def build_sdist (sdist_directory : str , config_settings : ConfigSettings | None = None ) -> str :
60- return maturin .build_sdist (sdist_directory , config_settings )
58+ name = maturin .build_sdist (sdist_directory , config_settings )
59+ if common_is_present ():
60+ vendor_into_sdist (Path (sdist_directory ) / name )
61+ return name
6162
6263
6364def build_editable (
6465 wheel_directory : str ,
6566 config_settings : ConfigSettings | None = None ,
6667 metadata_directory : str | None = None ,
6768) -> str :
68- return maturin .build_editable ( wheel_directory , config_settings , metadata_directory )
69+ return built ( maturin .build_editable , wheel_directory , config_settings , metadata_directory , link_common_into_wheel )
6970
7071
7172def get_requires_for_build_wheel (config_settings : ConfigSettings | None = None ) -> list [str ]:
@@ -80,6 +81,26 @@ def get_requires_for_build_editable(config_settings: ConfigSettings | None = Non
8081 return maturin .get_requires_for_build_editable (config_settings )
8182
8283
84+ def built (
85+ build : Callable [[str , ConfigSettings | None , str | None ], str ],
86+ wheel_directory : str ,
87+ config_settings : ConfigSettings | None ,
88+ metadata_directory : str | None ,
89+ carry : Callable [[Path ], None ],
90+ ) -> str :
91+ if not common_is_present ():
92+ return build (wheel_directory , config_settings , metadata_directory )
93+ tmp = Path (mkdtemp ())
94+ name = build (str (tmp ), config_settings , metadata_directory )
95+ carry (tmp / name )
96+ copy2 (tmp / name , Path (wheel_directory ) / name )
97+ return name
98+
99+
100+ def common_is_present () -> bool :
101+ return (_COMMON / "src" / _VENDOR ).is_dir ()
102+
103+
83104def main () -> None :
84105 target = Path (argv [1 ])
85106 if not (wheels := sorted (target .glob ("*.whl" )) if target .is_dir () else [target ]):
@@ -91,29 +112,27 @@ def main() -> None:
91112
92113
93114def vendor_into_wheel (wheel : Path ) -> None :
94- common_src = _COMMON / "src" / _VENDOR
115+ at = f"{ _MODULE } /_vendor/"
116+ changed = {f"{ at } __init__.py" : b"" } | {f"{ at } { name } " : data for name , data in common_sources ()}
117+ with ZipFile (wheel ) as src :
118+ if (entry := f"{ _MODULE } /__main__.py" ) in src .namelist ():
119+ spelled = sub (rf"\b{ _VENDOR } \b" , f"{ _MODULE } ._vendor.{ _VENDOR } " , src .read (entry ).decode ())
120+ changed [entry ] = spelled .encode ()
121+ rewrite (wheel , changed )
95122
123+
124+ def link_common_into_wheel (wheel : Path ) -> None :
125+ # an editable install reads the package from the source tree, where the import stays unvendored
126+ rewrite (wheel , {f"{ _MODULE } _{ _VENDOR } .pth" : f"{ _COMMON / 'src' } \n " .encode ()})
127+
128+
129+ def rewrite (wheel : Path , changed : dict [str , bytes ]) -> None :
96130 with ZipFile (wheel ) as src :
97131 names = src .namelist ()
98132 dist_info = next (n for n in names if n .endswith (".dist-info/METADATA" )).split ("/" )[0 ]
99133 out = {n : src .read (n ) for n in names if not n .endswith ("/RECORD" )}
100-
101- if (entry := f"{ _MODULE } /__main__.py" ) in out :
102- out [entry ] = sub (rf"\b{ _VENDOR } \b" , f"{ _MODULE } ._vendor.{ _VENDOR } " , out [entry ].decode ()).encode ()
103- meta = f"{ dist_info } /METADATA"
104- deps_block = search (r"(?ms)^dependencies = \[(.*?)\]" , (_COMMON / "pyproject.toml" ).read_text ())
105- deps = findall (r'"([^"]*)"' , deps_block .group (1 )) if deps_block else []
106- stripped = sub (r"(?m)^Requires-Dist: toml-fmt-common.*\n" , "" , out [meta ].decode ())
107- # Requires-Dist belongs in the header block; everything past the first blank line is the description
108- headers , sep , description = stripped .partition ("\n \n " )
109- parts = [headers .rstrip ("\n " ), * (f"\n Requires-Dist: { d } " for d in deps ), sep , description ]
110- out [meta ] = "" .join (parts ).encode ()
111-
112- out [f"{ _MODULE } /_vendor/__init__.py" ] = b""
113- # vendor only the package's Python sources; local build artifacts (bytecode, caches, ext modules) never leak
114- for file in common_src .rglob ("*" ):
115- if file .is_file () and (file .suffix in {".py" , ".pyi" } or file .name == "py.typed" ):
116- out [f"{ _MODULE } /_vendor/{ file .relative_to (common_src .parent ).as_posix ()} " ] = file .read_bytes ()
134+ out .update (changed )
135+ out [f"{ dist_info } /METADATA" ] = own_metadata (out [f"{ dist_info } /METADATA" ])
117136
118137 record = []
119138 for name , data in out .items ():
@@ -127,5 +146,43 @@ def vendor_into_wheel(wheel: Path) -> None:
127146 zf .writestr (name , data )
128147
129148
149+ def vendor_into_sdist (sdist : Path ) -> None :
150+ held = []
151+ with tar_open (sdist ) as src :
152+ for member in src .getmembers ():
153+ content = src .extractfile (member )
154+ held .append ((member , content .read () if content else b"" ))
155+ root = held [0 ][0 ].name .split ("/" )[0 ]
156+
157+ added = [(f"{ root } /{ _COMMON .name } /pyproject.toml" , (_COMMON / "pyproject.toml" ).read_bytes ())]
158+ added += [(f"{ root } /{ _COMMON .name } /src/{ name } " , data ) for name , data in common_sources ()]
159+
160+ with tar_open (sdist , "w:gz" ) as out :
161+ for member , data in held :
162+ out .addfile (member , BytesIO (data ) if member .isfile () else None )
163+ for name , data in added :
164+ info = TarInfo (name )
165+ info .size = len (data )
166+ info .mode = 0o644
167+ out .addfile (info , BytesIO (data ))
168+
169+
170+ def own_metadata (metadata : bytes ) -> bytes :
171+ deps_block = search (r"(?ms)^dependencies = \[(.*?)\]" , (_COMMON / "pyproject.toml" ).read_text ())
172+ if not (deps := findall (r'"([^"]*)"' , deps_block .group (1 )) if deps_block else []):
173+ return metadata
174+ # Requires-Dist belongs in the header block; everything past the first blank line is the description
175+ headers , sep , description = metadata .decode ().partition ("\n \n " )
176+ return "" .join ([headers .rstrip ("\n " ), * (f"\n Requires-Dist: { d } " for d in deps ), sep , description ]).encode ()
177+
178+
179+ def common_sources () -> Iterator [tuple [str , bytes ]]:
180+ src = _COMMON / "src" / _VENDOR
181+ # vendor only the package's Python sources; local build artifacts (bytecode, caches, ext modules) never leak
182+ for file in sorted (src .rglob ("*" )):
183+ if file .is_file () and (file .suffix in {".py" , ".pyi" } or file .name == "py.typed" ):
184+ yield file .relative_to (src .parent ).as_posix (), file .read_bytes ()
185+
186+
130187if __name__ == "__main__" :
131188 main ()
0 commit comments