Skip to content

Commit

Permalink
releng - update readme re twine publishing, publish full direct dep g…
Browse files Browse the repository at this point in the history
…raph w/ marker (#10)
  • Loading branch information
kapilt authored Mar 27, 2023
1 parent b6f81ea commit b6ebff8
Show file tree
Hide file tree
Showing 9 changed files with 400 additions and 326 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 2

- name: Install poetry
run: pipx install poetry
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ The various language package distribution channels (npm, pypi, rubygems, etc) ar

A post build / pre publish command to allow for creating wheels with frozen dependencies. Basically we update wheel metadata for Requires-Dist to replace the pyproject.toml based version specification to a frozen (ie. ==version) one based on the version from the poetry lock information.


Note we can't use poetry to publish because the frozen wheel because it uses metadata from pyproject.toml instead
of frozen wheel metadata.

## Usage

```shell
Expand All @@ -26,8 +30,11 @@ poetry build
# add freeze step
poetry freeze-wheel

# Note we can't use poetry to publish because it uses metadata from pyproject.toml instead
# of frozen wheel metadata.

# publish per normal
poetry publish
twine upload dist/*.whl
```

## Mono-Repo Support
Expand Down
520 changes: 257 additions & 263 deletions poetry.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "poetry-plugin-freeze"
version = "1.0.2"
version = "1.0.3"
description = "Poetry plugin to freeze a wheel's dependencies per lock file"
license = "Apache-2.0"
authors = ["Kapil Thangavelu <[email protected]>"]
Expand Down Expand Up @@ -32,6 +32,11 @@ ruff = "^0.0.254"
[tool.poetry.plugins."poetry.application.plugin"]
freeze-wheel = "poetry_plugin_freeze.app:FreezeApplicationPlugin"

[tool.ruff]
line-length = 100

[tool.black]
line-length = 100

[build-system]
requires = ["poetry-core"]
Expand Down
91 changes: 45 additions & 46 deletions src/poetry_plugin_freeze/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from cleo.helpers import option
from poetry.console.commands.command import Command
from poetry.core.packages.dependency_group import MAIN_GROUP
from poetry.packages import DependencyPackage
from poetry.core.masonry.metadata import Metadata
from poetry.core.masonry.utils.helpers import distribution_name
from poetry.factory import Factory
Expand Down Expand Up @@ -106,10 +107,14 @@ def freeze(self):
wheels = list(self.get_wheels())
if not wheels:
return []
dep_package_map = self.get_dep_packages()
for w in wheels:
self.freeze_wheel(w, dep_package_map)
return wheels

def get_dep_packages(self):
root_package = self.poetry.package.with_dependency_groups([MAIN_GROUP], only=True)

root_package = self.poetry.package.with_dependency_groups(
[MAIN_GROUP], only=True
)
dep_packages = list(
get_project_dependency_packages(
self.poetry.locker,
Expand All @@ -118,40 +123,34 @@ def freeze(self):
project_python_marker=root_package.python_marker,
)
)
return {p.package.name: p for p in dep_packages}

def get_frozen_deps(self, dep_packages):
lines = []
for pkg_name, dep_package in dep_packages.items():
require_dist = "%s (==%s)" % (pkg_name, dep_package.package.version)
requirement = dep_package.dependency.to_pep_508(with_extras=False)
if ";" in requirement:
markers = requirement.split(";", 1)[1].strip()
require_dist += f" ; {markers}"
lines.append(require_dist)
return lines

def replace_deps(self, dist_meta, dep_lines):
start_pos = 0
for m in dist_meta.get_all("Requires-Dist"):
if not start_pos:
start_pos = dist_meta._headers.index(("Requires-Dist", m))
dist_meta._headers.remove(("Requires-Dist", m))

for idx, h in enumerate(dep_lines):
dist_meta._headers.insert(start_pos + idx, ("Requires-Dist", h))

dep_package_map = {p.package.name: p for p in dep_packages}
for w in wheels:
self.freeze_wheel(w, dep_package_map)

return wheels

def freeze_deps(self, dist_meta, dep_packages):
frozen_headers = []

for k, v in dist_meta.items():
if k != "Requires-Dist":
frozen_headers.append((k, v))
continue
pkg_name = v.split(" ", 1)[0]
extras = ""
if ";" in v:
extras = v.split(";", 1)[-1]
# This happens when running multiple times on the same
# wheel, where we've inserted a path dev dependency but
# we're resolving against normal dependencies.
if pkg_name not in dep_packages:
continue

dep_pkg = dep_packages[pkg_name]
requires = "%s (==%s)" % (pkg_name, dep_pkg.package.version)
if extras:
requires += "; %s" % extras
frozen_headers.append((k, requires))

dist_meta._headers = frozen_headers
return dist_meta

def freeze_path_deps(self, dist_meta, group="dev"):
def get_path_deps(self, group="dev"):
# assuming we're consistent install across deps.
package_deps = {}
group = self.poetry.package.dependency_group(group)
for dep in group.dependencies:
if not (dep.is_file() or dep.is_directory()):
Expand All @@ -160,7 +159,11 @@ def freeze_path_deps(self, dist_meta, group="dev"):
continue
assert dep.name in self.fridge, f"Unknown path dependency {dep.name}"
iced = self.fridge[dep.name]
dist_meta.add_header("Requires-Dist", f"{dep.name} (=={iced.version})")
package_deps[dep.name] = DependencyPackage(
dependency=iced.poetry.package.to_dependency(), package=iced.poetry.package
)
package_deps.update(iced.get_dep_packages())
return package_deps

def freeze_record(self, records_fh, dist_meta, md_path):
hash_digest = get_sha256_digest(str(dist_meta).encode("utf8"))
Expand All @@ -178,9 +181,7 @@ def freeze_record(self, records_fh, dist_meta, md_path):
continue
writer.writerow(row)

writer.writerow(
(md_path, f"sha256={hash_digest}", len(str(dist_meta).encode("utf8")))
)
writer.writerow((md_path, f"sha256={hash_digest}", len(str(dist_meta).encode("utf8"))))
return output.getvalue()

def freeze_wheel(self, wheel_path, dep_packages):
Expand All @@ -195,8 +196,10 @@ def freeze_wheel(self, wheel_path, dep_packages):
# freeze deps in metadata and update records
md_text = source_whl.open(md_path).read().decode("utf8")
dist_meta = Parser().parsestr(md_text)
self.freeze_deps(dist_meta, dep_packages)
self.freeze_path_deps(dist_meta)
deps = self.get_path_deps()
deps.update(dep_packages)
dep_lines = self.get_frozen_deps(deps)
self.replace_deps(dist_meta, dep_lines)

with source_whl.open(record_path) as record_fh:
record_text = self.freeze_record(record_fh, dist_meta, md_path)
Expand All @@ -211,9 +214,7 @@ def freeze_wheel(self, wheel_path, dep_packages):
sample = info
continue
info_fh = source_whl.open(info)
frozen_whl.writestr(
info, info_fh.read(), compress_type=zipfile.ZIP_DEFLATED
)
frozen_whl.writestr(info, info_fh.read(), compress_type=zipfile.ZIP_DEFLATED)

# finally add in our modified files
date_time = (2016, 1, 1, 0, 0, 0)
Expand All @@ -228,8 +229,6 @@ def freeze_wheel(self, wheel_path, dep_packages):

record_info = zipfile.ZipInfo(record_path, date_time)
record_info.external_attr = sample.external_attr
frozen_whl.writestr(
record_path, record_text, compress_type=zipfile.ZIP_DEFLATED
)
frozen_whl.writestr(record_path, record_text, compress_type=zipfile.ZIP_DEFLATED)

shutil.move(temp_path, str(wheel_path))
2 changes: 1 addition & 1 deletion tests/fixtures/nested_packages/others/app_c/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ packages = [
{ include = "app_c" }]

[tool.poetry.dependencies]
python = "^3.11"
python = "^3.10"
pytest = "^7.1"
pytest-cov = "^4.0"

Expand Down
29 changes: 28 additions & 1 deletion tests/fixtures/nested_packages/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/fixtures/nested_packages/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers=[
[tool.poetry.dependencies]
python = "^3.8"
pytest = "^7.1"
ruff = "^0.0.259"

[build-system]
requires = ["poetry>=0.12", "setuptools"]
Expand Down
65 changes: 52 additions & 13 deletions tests/test_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ def test_freeze_nested(fixture_root, fixture_copy):
wheel.open(f"{iced_sub.distro_name}-{iced_sub.version}.dist-info/RECORD").read()
)
md = parse_md(
wheel.open(
f"{iced_sub.distro_name}-{iced_sub.version}.dist-info/METADATA"
).read()
wheel.open(f"{iced_sub.distro_name}-{iced_sub.version}.dist-info/METADATA").read()
)

assert md._headers == [
Expand All @@ -54,9 +52,52 @@ def test_freeze_nested(fixture_root, fixture_copy):
("Classifier", "License :: OSI Approved :: Apache Software License"),
("Classifier", "Programming Language :: Python :: 3"),
("Classifier", "Programming Language :: Python :: 3.11"),
("Requires-Dist", "pytest (==7.2.2)"),
("Requires-Dist", "pytest-cov (==4.0.0)"),
("Requires-Dist", "app-b (==0.1)"),
(
"Requires-Dist",
'app-b (==0.1) ; python_version >= "3.8" and python_version < "4.0"',
),
(
"Requires-Dist",
'pytest (==7.2.2) ; python_version >= "3.10" and python_version < "4.0"',
),
("Requires-Dist", 'ruff (==0.0.259) ; python_version >= "3.8" and python_version < "4.0"'),
(
"Requires-Dist",
'attrs (==22.2.0) ; python_version >= "3.10" and python_version < "4.0"',
),
(
"Requires-Dist",
'colorama (==0.4.6) ; python_version >= "3.10" and python_version < "4.0" '
'and sys_platform == "win32"',
),
(
"Requires-Dist",
'exceptiongroup (==1.1.0) ; python_version >= "3.10" and python_version < "3.11"',
),
(
"Requires-Dist",
'iniconfig (==2.0.0) ; python_version >= "3.10" and python_version < "4.0"',
),
(
"Requires-Dist",
'packaging (==23.0) ; python_version >= "3.10" and python_version < "4.0"',
),
(
"Requires-Dist",
'pluggy (==1.0.0) ; python_version >= "3.10" and python_version < "4.0"',
),
(
"Requires-Dist",
'tomli (==2.0.1) ; python_version >= "3.10" and python_full_version <= ' '"3.11.0a6"',
),
(
"Requires-Dist",
'pytest-cov (==4.0.0) ; python_version >= "3.10" and python_version < "4.0"',
),
(
"Requires-Dist",
'coverage (==7.2.1) ; python_version >= "3.10" and python_version < "4.0"',
),
]

assert records == [
Expand All @@ -73,13 +114,11 @@ def test_freeze_nested(fixture_root, fixture_copy):
["app_c-0.2.dist-info/RECORD", "", ""],
[
"app_c-0.2.dist-info/METADATA",
"sha256=sTFPVGUHyrBa51vKxDR_p-4vAQS1pYWIsTepSOadLF4",
"394",
"sha256=qDFKDZ9yblLDU9jZ3UoInesMV9uf9r30gw3zw6-JDa0",
"1384",
],
]

md_bytes = wheel.open(
f"{iced_sub.distro_name}-{iced_sub.version}.dist-info/METADATA"
).read()
assert len(md_bytes) == 394
assert get_sha256_digest(md_bytes) == "sTFPVGUHyrBa51vKxDR_p-4vAQS1pYWIsTepSOadLF4"
md_bytes = wheel.open(f"{iced_sub.distro_name}-{iced_sub.version}.dist-info/METADATA").read()
assert len(md_bytes) == 1384
assert get_sha256_digest(md_bytes) == "qDFKDZ9yblLDU9jZ3UoInesMV9uf9r30gw3zw6-JDa0"

0 comments on commit b6ebff8

Please sign in to comment.