Skip to content

Commit 382b678

Browse files
vonschultzaignas
andauthored
feat(py_wheel): Normalize name and version (#1331)
Added the `incompatible_normalize_name` feature flag to normalize the package distribution name according to latest Python packaging standards. Defaults to `False` for the time being. Added the `incompatible_normalize_version` feature flag to normalize the package version according to PEP440 standard. This also adds support for local version specifiers (versions with a `+` in them), in accordance with PEP440. Defaults to `False` for the time being. Instead of following the obsolete PEP 427 escaping procedure for distribution names and versions, we should use the rules specified by https://packaging.python.org/en/latest/specifications (sections "Package name normalization" and "Binary distribution format"). For the versions, this means normalizing them according to PEP 440. Added as feature flags to avoid forcing the user to deal with breaking changes when upgrading `rules_python`: - Distribution names have stronger requirements now: "A valid name consists only of ASCII letters and numbers, period, underscore and hyphen. It must start and end with a letter or number." https://packaging.python.org/en/latest/specifications/name-normalization/ - Versions must be valid PEP 440 version identifiers. Previously versions such as "0.1-2-3" would have been accepted; that is no longer the case. - The file name of generated wheels may have changed, if the distribution name or the version identifier wasn't in normalized form. - The wheelmaker now depends on `packaging.version`, which means the `py_wheel` user now needs to load pip dependencies in their `WORKSPACE.bazel` file: ``` load("@rules_python//python/pip_install:repositories.bzl", "pip_install_dependencies") pip_install_dependencies() ``` Fixes #883. Fixes #1132. --------- Co-authored-by: Ignas Anikevicius <[email protected]> Co-authored-by: Ignas Anikevicius <[email protected]>
1 parent 423c1de commit 382b678

File tree

11 files changed

+906
-23
lines changed

11 files changed

+906
-23
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ A brief description of the categories of changes:
5555
authentication against private HTTP hosts serving Python toolchain binaries.
5656
* `//python:packaging_bzl` added, a `bzl_library` for the Starlark
5757
files `//python:packaging.bzl` requires.
58+
* (py_wheel) Added the `incompatible_normalize_name` feature flag to
59+
normalize the package distribution name according to latest Python
60+
packaging standards. Defaults to `False` for the time being.
61+
* (py_wheel) Added the `incompatible_normalize_version` feature flag
62+
to normalize the package version according to PEP440 standard. This
63+
also adds support for local version specifiers (versions with a `+`
64+
in them), in accordance with PEP440. Defaults to `False` for the
65+
time being.
5866

5967
### Removed
6068

docs/packaging.md

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/wheel/BUILD.bazel

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ py_wheel(
5454
testonly = True, # Set this to verify the generated .dist target doesn't break things
5555
# Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl"
5656
distribution = "example_minimal_library",
57+
incompatible_normalize_name = True,
58+
incompatible_normalize_version = True,
5759
python_tag = "py3",
5860
version = "0.0.1",
5961
deps = [
@@ -76,6 +78,8 @@ py_wheel(
7678
testonly = True,
7779
abi = "$(ABI)",
7880
distribution = "example_minimal_library",
81+
incompatible_normalize_name = True,
82+
incompatible_normalize_version = True,
7983
python_tag = "$(PYTHON_TAG)",
8084
toolchains = ["//examples/wheel:make_variable_tags"],
8185
version = "$(VERSION)",
@@ -95,6 +99,8 @@ py_wheel(
9599
name = "minimal_with_py_library_with_stamp",
96100
# Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl"
97101
distribution = "example_minimal_library{BUILD_USER}",
102+
incompatible_normalize_name = False,
103+
incompatible_normalize_version = False,
98104
python_tag = "py3",
99105
stamp = 1,
100106
version = "0.1.{BUILD_TIMESTAMP}",
@@ -123,6 +129,8 @@ py_wheel(
123129
name = "minimal_with_py_package",
124130
# Package data. We're building "example_minimal_package-0.0.1-py3-none-any.whl"
125131
distribution = "example_minimal_package",
132+
incompatible_normalize_name = True,
133+
incompatible_normalize_version = True,
126134
python_tag = "py3",
127135
version = "0.0.1",
128136
deps = [":example_pkg"],
@@ -156,6 +164,8 @@ py_wheel(
156164
"//examples/wheel:README.md": "README",
157165
},
158166
homepage = "www.example.com",
167+
incompatible_normalize_name = True,
168+
incompatible_normalize_version = True,
159169
license = "Apache 2.0",
160170
project_urls = {
161171
"Bug Tracker": "www.example.com/issues",
@@ -177,6 +187,8 @@ py_wheel(
177187
entry_points = {
178188
"console_scripts": ["main = foo.bar:baz"],
179189
},
190+
incompatible_normalize_name = True,
191+
incompatible_normalize_version = True,
180192
python_tag = "py3",
181193
strip_path_prefixes = [
182194
"examples",
@@ -191,6 +203,8 @@ py_wheel(
191203
name = "custom_package_root_multi_prefix",
192204
# Package data. We're building "custom_custom_package_root_multi_prefix-0.0.1-py3-none-any.whl"
193205
distribution = "example_custom_package_root_multi_prefix",
206+
incompatible_normalize_name = True,
207+
incompatible_normalize_version = True,
194208
python_tag = "py3",
195209
strip_path_prefixes = [
196210
"examples/wheel/lib",
@@ -206,6 +220,8 @@ py_wheel(
206220
name = "custom_package_root_multi_prefix_reverse_order",
207221
# Package data. We're building "custom_custom_package_root_multi_prefix_reverse_order-0.0.1-py3-none-any.whl"
208222
distribution = "example_custom_package_root_multi_prefix_reverse_order",
223+
incompatible_normalize_name = True,
224+
incompatible_normalize_version = True,
209225
python_tag = "py3",
210226
strip_path_prefixes = [
211227
"examples/wheel",
@@ -220,6 +236,8 @@ py_wheel(
220236
py_wheel(
221237
name = "python_requires_in_a_package",
222238
distribution = "example_python_requires_in_a_package",
239+
incompatible_normalize_name = True,
240+
incompatible_normalize_version = True,
223241
python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
224242
python_tag = "py3",
225243
version = "0.0.1",
@@ -231,6 +249,8 @@ py_wheel(
231249
py_wheel(
232250
name = "use_rule_with_dir_in_outs",
233251
distribution = "use_rule_with_dir_in_outs",
252+
incompatible_normalize_name = True,
253+
incompatible_normalize_version = True,
234254
python_tag = "py3",
235255
version = "0.0.1",
236256
deps = [
@@ -244,6 +264,8 @@ py_wheel(
244264
name = "python_abi3_binary_wheel",
245265
abi = "abi3",
246266
distribution = "example_python_abi3_binary_wheel",
267+
incompatible_normalize_name = True,
268+
incompatible_normalize_version = True,
247269
# these platform strings must line up with test_python_abi3_binary_wheel() in wheel_test.py
248270
platform = select({
249271
":aarch64-apple-darwin": "macosx_11_0_arm64",
@@ -258,16 +280,32 @@ py_wheel(
258280
)
259281

260282
py_wheel(
261-
name = "filename_escaping",
283+
name = "legacy_filename_escaping",
262284
# Per https://www.python.org/dev/peps/pep-0427/#escaping-and-unicode
263285
# runs of non-alphanumeric, non-digit symbols should be replaced with a single underscore.
264286
# Unicode non-ascii letters should *not* be replaced with underscore.
265287
distribution = "file~~name-escaping",
288+
incompatible_normalize_name = False,
289+
incompatible_normalize_version = False,
266290
python_tag = "py3",
267291
version = "0.0.1-r7",
268292
deps = [":example_pkg"],
269293
)
270294

295+
py_wheel(
296+
name = "filename_escaping",
297+
# Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
298+
# runs of "-", "_" and "." should be replaced with a single underscore.
299+
# Unicode non-ascii letters aren't allowed according to
300+
# https://packaging.python.org/en/latest/specifications/name-normalization/.
301+
distribution = "File--Name-Escaping",
302+
incompatible_normalize_name = True,
303+
incompatible_normalize_version = True,
304+
python_tag = "py3",
305+
version = "v0.0.1.RC1+ubuntu-r7",
306+
deps = [":example_pkg"],
307+
)
308+
271309
py_test(
272310
name = "wheel_test",
273311
srcs = ["wheel_test.py"],
@@ -277,6 +315,7 @@ py_test(
277315
":custom_package_root_multi_prefix_reverse_order",
278316
":customized",
279317
":filename_escaping",
318+
":legacy_filename_escaping",
280319
":minimal_with_py_library",
281320
":minimal_with_py_library_with_stamp",
282321
":minimal_with_py_package",

examples/wheel/wheel_test.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,51 @@ def test_customized_wheel(self):
153153
second = second.main:s""",
154154
)
155155

156+
def test_legacy_filename_escaping(self):
157+
filename = os.path.join(
158+
os.environ['TEST_SRCDIR'],
159+
'rules_python',
160+
'examples',
161+
'wheel',
162+
'file_name_escaping-0.0.1_r7-py3-none-any.whl',
163+
)
164+
with zipfile.ZipFile(filename) as zf:
165+
self.assertEquals(
166+
zf.namelist(),
167+
[
168+
'examples/wheel/lib/data.txt',
169+
'examples/wheel/lib/module_with_data.py',
170+
'examples/wheel/lib/simple_module.py',
171+
'examples/wheel/main.py',
172+
# PEP calls for replacing only in the archive filename.
173+
# Alas setuptools also escapes in the dist-info directory
174+
# name, so let's be compatible.
175+
'file_name_escaping-0.0.1_r7.dist-info/WHEEL',
176+
'file_name_escaping-0.0.1_r7.dist-info/METADATA',
177+
'file_name_escaping-0.0.1_r7.dist-info/RECORD',
178+
],
179+
)
180+
metadata_contents = zf.read(
181+
'file_name_escaping-0.0.1_r7.dist-info/METADATA'
182+
)
183+
self.assertEquals(
184+
metadata_contents,
185+
b"""\
186+
Metadata-Version: 2.1
187+
Name: file~~name-escaping
188+
Version: 0.0.1-r7
189+
190+
UNKNOWN
191+
""",
192+
)
193+
156194
def test_filename_escaping(self):
157195
filename = os.path.join(
158196
os.environ["TEST_SRCDIR"],
159197
"rules_python",
160198
"examples",
161199
"wheel",
162-
"file_name_escaping-0.0.1_r7-py3-none-any.whl",
200+
"file_name_escaping-0.0.1rc1+ubuntu.r7-py3-none-any.whl",
163201
)
164202
with zipfile.ZipFile(filename) as zf:
165203
self.assertEqual(
@@ -172,20 +210,20 @@ def test_filename_escaping(self):
172210
# PEP calls for replacing only in the archive filename.
173211
# Alas setuptools also escapes in the dist-info directory
174212
# name, so let's be compatible.
175-
"file_name_escaping-0.0.1_r7.dist-info/WHEEL",
176-
"file_name_escaping-0.0.1_r7.dist-info/METADATA",
177-
"file_name_escaping-0.0.1_r7.dist-info/RECORD",
213+
"file_name_escaping-0.0.1rc1+ubuntu.r7.dist-info/WHEEL",
214+
"file_name_escaping-0.0.1rc1+ubuntu.r7.dist-info/METADATA",
215+
"file_name_escaping-0.0.1rc1+ubuntu.r7.dist-info/RECORD",
178216
],
179217
)
180218
metadata_contents = zf.read(
181-
"file_name_escaping-0.0.1_r7.dist-info/METADATA"
219+
"file_name_escaping-0.0.1rc1+ubuntu.r7.dist-info/METADATA"
182220
)
183221
self.assertEqual(
184222
metadata_contents,
185223
b"""\
186224
Metadata-Version: 2.1
187-
Name: file~~name-escaping
188-
Version: 0.0.1-r7
225+
Name: File--Name-Escaping
226+
Version: 0.0.1rc1+ubuntu.r7
189227
190228
UNKNOWN
191229
""",

python/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ bzl_library(
7777
":py_binary_bzl",
7878
"//python/private:py_package.bzl",
7979
"//python/private:py_wheel_bzl",
80+
"//python/private:py_wheel_normalize_pep440.bzl",
8081
"//python/private:stamp_bzl",
8182
"//python/private:util_bzl",
8283
],

python/private/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ exports_files(
236236
"coverage.patch",
237237
"py_package.bzl",
238238
"py_wheel.bzl",
239+
"py_wheel_normalize_pep440.bzl",
239240
"reexports.bzl",
240241
"stamp.bzl",
241242
"util.bzl",

0 commit comments

Comments
 (0)