Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions python/private/pypi/whl_installer/wheel_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,17 @@ def _extract_wheel(
whl = wheel.Wheel(wheel_file)
whl.unzip(installation_dir)

if enable_pipstar:
return

extras_requested = extras[whl.name] if whl.name in extras else set()
dependencies = whl.dependencies(extras_requested, platforms)

metadata = {
"name": whl.name,
"version": whl.version,
"deps": dependencies.deps,
"deps_by_platform": dependencies.deps_select,
"entry_points": [
{
"name": name,
Expand All @@ -106,18 +116,6 @@ def _extract_wheel(
for name, (module, attribute) in sorted(whl.entry_points().items())
],
}
if not enable_pipstar:
extras_requested = extras[whl.name] if whl.name in extras else set()
dependencies = whl.dependencies(extras_requested, platforms)

metadata.update(
{
"name": whl.name,
"version": whl.version,
"deps": dependencies.deps,
"deps_by_platform": dependencies.deps_select,
}
)

with open(os.path.join(installation_dir, "metadata.json"), "w") as f:
json.dump(metadata, f)
Expand Down
21 changes: 9 additions & 12 deletions python/private/pypi/whl_library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -391,18 +391,21 @@ def _whl_library_impl(rctx):
logger = logger,
)

metadata = json.decode(rctx.read("metadata.json"))
rctx.delete("metadata.json")
metadata = whl_metadata(
install_dir = whl_path.dirname.get_child("site-packages"),
read_fn = rctx.read,
logger = logger,
)

# NOTE @aignas 2024-06-22: this has to live on until we stop supporting
# passing `twine` as a `:pkg` library via the `WORKSPACE` builds.
#
# See ../../packaging.bzl line 190
entry_points = {}
for item in metadata["entry_points"]:
name = item["name"]
module = item["module"]
attribute = item["attribute"]
for item in metadata.entry_points:
name = item.name
module = item.module
attribute = item.attribute

# There is an extreme edge-case with entry_points that end with `.py`
# See: https://github.com/bazelbuild/bazel/blob/09c621e4cf5b968f4c6cdf905ab142d5961f9ddc/src/test/java/com/google/devtools/build/lib/rules/python/PyBinaryConfiguredTargetTest.java#L174
Expand All @@ -418,12 +421,6 @@ def _whl_library_impl(rctx):
)
entry_points[entry_point_without_py] = entry_point_script_name

metadata = whl_metadata(
install_dir = whl_path.dirname.get_child("site-packages"),
read_fn = rctx.read,
logger = logger,
)

build_file_contents = generate_whl_library_build_bazel(
name = whl_path.basename,
sdist_filename = sdist_filename,
Expand Down
59 changes: 57 additions & 2 deletions python/private/pypi/whl_metadata.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ _NAME = "Name: "
_PROVIDES_EXTRA = "Provides-Extra: "
_REQUIRES_DIST = "Requires-Dist: "
_VERSION = "Version: "
_CONSOLE_SCRIPTS = "[console_scripts]"

def whl_metadata(*, install_dir, read_fn, logger):
"""Find and parse the METADATA file in the extracted whl contents dir.
Expand All @@ -23,7 +24,13 @@ def whl_metadata(*, install_dir, read_fn, logger):
"""
metadata_file = find_whl_metadata(install_dir = install_dir, logger = logger)
contents = read_fn(metadata_file)
result = parse_whl_metadata(contents)
entry_points_file = metadata_file.dirname.get_child("entry_points.txt")
if entry_points_file.exists:
entry_points_contents = read_fn(entry_points_file)
else:
entry_points_contents = ""

result = parse_whl_metadata(contents, entry_points_contents)

if not (result.name and result.version):
logger.fail("Failed to parse the wheel METADATA file:\n{}\n{}\n{}".format(
Expand All @@ -35,11 +42,12 @@ def whl_metadata(*, install_dir, read_fn, logger):

return result

def parse_whl_metadata(contents):
def parse_whl_metadata(contents, entry_points_contents = ""):
"""Parse .whl METADATA file

Args:
contents: {type}`str` the contents of the file.
entry_points_contents: {type}`str` the contents of the `entry_points.txt` file if it exists.

Returns:
A struct with parsed values:
Expand All @@ -48,6 +56,8 @@ def parse_whl_metadata(contents):
* `requires_dist`: {type}`list[str]` the list of requirements.
* `provides_extra`: {type}`list[str]` the list of extras that this package
provides.
* `entry_points`: {type}`list[struct]` the list of
entry_point metadata.
"""
parsed = {
"name": "",
Expand Down Expand Up @@ -79,6 +89,7 @@ def parse_whl_metadata(contents):
provides_extra = parsed["provides_extra"],
requires_dist = parsed["requires_dist"],
version = parsed["version"],
entry_points = _parse_entry_points(entry_points_contents),
)

def find_whl_metadata(*, install_dir, logger):
Expand Down Expand Up @@ -110,3 +121,47 @@ def find_whl_metadata(*, install_dir, logger):
else:
logger.fail("The '*.dist-info' directory could not be found in '{}'".format(install_dir.basename))
return None

def _parse_entry_points(contents):
"""parse the entry_points.txt file.

Args:
contents: {type}`str` The contents of the file

Returns:
A list of console_script entry point metadata.
"""
start = False
ret = []
for line in contents.split("\n"):
line = line.rstrip()

if line == _CONSOLE_SCRIPTS:
start = True
continue

if not start:
continue

if start and line.startswith("["):
break

line, _, _comment = line.partition("#")
line = line.strip()
if not line:
continue

name, _, tail = line.partition("=")

# importable.module:object.attr
py_import, _, extras = tail.strip().partition(" ")
module, _, attribute = py_import.partition(":")

ret.append(struct(
name = name.strip(),
module = module.strip(),
attribute = attribute.strip(),
extras = extras.replace(" ", ""),
))

return ret
49 changes: 49 additions & 0 deletions tests/pypi/whl_metadata/whl_metadata_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,14 @@ def _parse_whl_metadata(env, **kwargs):
version = result.version,
requires_dist = result.requires_dist,
provides_extra = result.provides_extra,
entry_points = result.entry_points,
),
attrs = dict(
name = subjects.str,
version = subjects.str,
requires_dist = subjects.collection,
provides_extra = subjects.collection,
entry_points = subjects.collection,
),
)

Expand Down Expand Up @@ -171,6 +173,53 @@ Requires-Dist: this will be ignored

_tests.append(_test_parse_metadata_multiline_license)

def _test_parse_entry_points_txt(env):
got = _parse_whl_metadata(
env,
contents = """\
Name: foo
Version: 0.0.1
""",
entry_points_contents = """\
[something]
interesting # with comments

[console_scripts]
foo = foomod:main
# One which depends on extras:
foobar = importable.foomod:main_bar [bar, baz]

# With a comment at the end
foobarbaz = foomod:main.attr # comment

[something else]
not very much interesting

""",
)
got.entry_points().contains_exactly([
struct(
attribute = "main",
extras = "",
module = "foomod",
name = "foo",
),
struct(
attribute = "main_bar",
extras = "[bar,baz]",
module = "importable.foomod",
name = "foobar",
),
struct(
attribute = "main.attr",
extras = "",
module = "foomod",
name = "foobarbaz",
),
])

_tests.append(_test_parse_entry_points_txt)

def whl_metadata_test_suite(name): # buildifier: disable=function-docstring
test_suite(
name = name,
Expand Down