Skip to content

Commit 4ec1e80

Browse files
docs,tests: Clarify how py_wheel.strip_path_prefixes works; add test case (bazel-contrib#3027)
Include a minor change to `arcname_from` to support cases where the distribution_prefix is the empty string. Fixes bazel-contrib#3017 --------- Co-authored-by: Richard Levasseur <richardlev@gmail.com>
1 parent aab2650 commit 4ec1e80

File tree

4 files changed

+103
-15
lines changed

4 files changed

+103
-15
lines changed

python/private/py_wheel.bzl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,15 @@ _other_attrs = {
217217
),
218218
"strip_path_prefixes": attr.string_list(
219219
default = [],
220-
doc = "path prefixes to strip from files added to the generated package",
220+
doc = """\
221+
Path prefixes to strip from files added to the generated package.
222+
Prefixes are checked **in order** and only the **first match** will be used.
223+
224+
For example:
225+
+ `["foo", "foo/bar/baz"]` will strip `"foo/bar/baz/file.py"` to `"bar/baz/file.py"`
226+
+ `["foo/bar/baz", "foo"]` will strip `"foo/bar/baz/file.py"` to `"file.py"` and
227+
`"foo/file2.py"` to `"file2.py"`
228+
""",
221229
),
222230
"summary": attr.string(
223231
doc = "A one-line summary of what the distribution does",

tests/tools/BUILD.bazel

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2025 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
load("//python:py_test.bzl", "py_test")
15+
16+
licenses(["notice"])
17+
18+
py_test(
19+
name = "wheelmaker_test",
20+
size = "small",
21+
srcs = ["wheelmaker_test.py"],
22+
deps = ["//tools:wheelmaker"],
23+
)

tests/tools/wheelmaker_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import unittest
2+
3+
import tools.wheelmaker as wheelmaker
4+
5+
6+
class ArcNameFromTest(unittest.TestCase):
7+
def test_arcname_from(self) -> None:
8+
# (name, distribution_prefix, strip_path_prefixes, want) tuples
9+
checks = [
10+
("a/b/c/file.py", "", [], "a/b/c/file.py"),
11+
("a/b/c/file.py", "", ["a"], "/b/c/file.py"),
12+
("a/b/c/file.py", "", ["a/b/"], "c/file.py"),
13+
# only first found is used and it's not cumulative.
14+
("a/b/c/file.py", "", ["a/", "b/"], "b/c/file.py"),
15+
# Examples from docs
16+
("foo/bar/baz/file.py", "", ["foo", "foo/bar/baz"], "/bar/baz/file.py"),
17+
("foo/bar/baz/file.py", "", ["foo/bar/baz", "foo"], "/file.py"),
18+
("foo/file2.py", "", ["foo/bar/baz", "foo"], "/file2.py"),
19+
# Files under the distribution prefix (eg mylib-1.0.0-dist-info)
20+
# are unmodified
21+
("mylib-0.0.1-dist-info/WHEEL", "mylib", [], "mylib-0.0.1-dist-info/WHEEL"),
22+
("mylib/a/b/c/WHEEL", "mylib", ["mylib"], "mylib/a/b/c/WHEEL"),
23+
]
24+
for name, prefix, strip, want in checks:
25+
with self.subTest(
26+
name=name,
27+
distribution_prefix=prefix,
28+
strip_path_prefixes=strip,
29+
want=want,
30+
):
31+
got = wheelmaker.arcname_from(
32+
name=name, distribution_prefix=prefix, strip_path_prefixes=strip
33+
)
34+
self.assertEqual(got, want)
35+
36+
37+
if __name__ == "__main__":
38+
unittest.main()

tools/wheelmaker.py

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import stat
2525
import sys
2626
import zipfile
27+
from collections.abc import Iterable
2728
from pathlib import Path
2829

2930
_ZIP_EPOCH = (1980, 1, 1, 0, 0, 0)
@@ -98,6 +99,30 @@ def normalize_pep440(version):
9899
return str(packaging.version.Version(f"0+{sanitized}"))
99100

100101

102+
def arcname_from(
103+
name: str, distribution_prefix: str, strip_path_prefixes: Sequence[str] = ()
104+
) -> str:
105+
"""Return the within-archive name for a given file path name.
106+
107+
Prefixes to strip are checked in order and only the first match will be used.
108+
109+
Args:
110+
name: The file path eg 'mylib/a/b/c/file.py'
111+
distribution_prefix: The
112+
strip_path_prefixes: Remove these prefixes from names.
113+
"""
114+
# Always use unix path separators.
115+
normalized_arcname = name.replace(os.path.sep, "/")
116+
# Don't manipulate names filenames in the .distinfo or .data directories.
117+
if distribution_prefix and normalized_arcname.startswith(distribution_prefix):
118+
return normalized_arcname
119+
for prefix in strip_path_prefixes:
120+
if normalized_arcname.startswith(prefix):
121+
return normalized_arcname[len(prefix) :]
122+
123+
return normalized_arcname
124+
125+
101126
class _WhlFile(zipfile.ZipFile):
102127
def __init__(
103128
self,
@@ -126,18 +151,6 @@ def data_path(self, basename):
126151
def add_file(self, package_filename, real_filename):
127152
"""Add given file to the distribution."""
128153

129-
def arcname_from(name):
130-
# Always use unix path separators.
131-
normalized_arcname = name.replace(os.path.sep, "/")
132-
# Don't manipulate names filenames in the .distinfo or .data directories.
133-
if normalized_arcname.startswith(self._distribution_prefix):
134-
return normalized_arcname
135-
for prefix in self._strip_path_prefixes:
136-
if normalized_arcname.startswith(prefix):
137-
return normalized_arcname[len(prefix) :]
138-
139-
return normalized_arcname
140-
141154
if os.path.isdir(real_filename):
142155
directory_contents = os.listdir(real_filename)
143156
for file_ in directory_contents:
@@ -147,7 +160,11 @@ def arcname_from(name):
147160
)
148161
return
149162

150-
arcname = arcname_from(package_filename)
163+
arcname = arcname_from(
164+
package_filename,
165+
distribution_prefix=self._distribution_prefix,
166+
strip_path_prefixes=self._strip_path_prefixes,
167+
)
151168
zinfo = self._zipinfo(arcname)
152169

153170
# Write file to the zip archive while computing the hash and length
@@ -569,7 +586,9 @@ def get_new_requirement_line(reqs_text, extra):
569586
else:
570587
return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {req.marker}"
571588
else:
572-
return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {extra}".strip(" ;")
589+
return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {extra}".strip(
590+
" ;"
591+
)
573592

574593
for meta_line in metadata.splitlines():
575594
if not meta_line.startswith("Requires-Dist: "):

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy