Skip to content

Commit 43b36dc

Browse files
authored
fix: support freezing multiple versions of a package (#30)
* fix: support freezing multiple versions of a package In cases where there is more than one locked version of a package (due to Python version or platform constraints for example), freeze all locked versions. * adjust test to reflect multiple frozen versions of a single package
1 parent 7b7794c commit 43b36dc

5 files changed

Lines changed: 118 additions & 36 deletions

File tree

src/poetry_plugin_freeze/app.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import csv
33
from email.parser import Parser
44
from functools import lru_cache
5+
from itertools import chain
56
import hashlib
67
from io import StringIO, TextIOWrapper
78
from pathlib import Path
@@ -155,15 +156,15 @@ def freeze(self):
155156
wheels = list(self.get_wheels())
156157
if not wheels:
157158
return []
158-
dep_package_map = self.get_dep_packages()
159+
dep_packages = self.get_dep_packages()
159160
for w in wheels:
160-
self.freeze_wheel(w, dep_package_map)
161+
self.freeze_wheel(w, dep_packages)
161162
return wheels
162163

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

166-
dep_packages = list(
167+
return list(
167168
get_project_dependency_packages(
168169
self.poetry.locker,
169170
project_requires=root_package.all_requires,
@@ -174,7 +175,6 @@ def get_dep_packages(self):
174175
extras=root_package.extras,
175176
)
176177
)
177-
return {p.package.name: p for p in dep_packages}
178178

179179
@lru_cache(maxsize=None)
180180
def get_dependency_sources(self):
@@ -197,7 +197,9 @@ def _with_python_marker(requirements, root_package):
197197

198198
repository = self.poetry.locker.locked_repository()
199199
root_package = self.poetry.package
200-
locked_packages_by_name = {p.name: [p] for p in repository.packages}
200+
locked_packages_by_name = {}
201+
for pkg in repository.packages:
202+
locked_packages_by_name.setdefault(pkg.name, []).append(pkg)
201203
dependency_sources = {}
202204
base_requires = [
203205
dep
@@ -249,7 +251,7 @@ def compact_markers(self, dependency):
249251
def get_frozen_deps(self, dep_packages, exclude_packages=None):
250252
lines = []
251253
dependency_sources = self.get_dependency_sources()
252-
for pkg_name, dep_package in dep_packages.items():
254+
for dep_package in dep_packages:
253255
self.compact_markers(dep_package.dependency)
254256
# Freeze extra markers for dependencies which were pulled in via extras
255257
# Don't freeze markers if a dependency is also part of the base
@@ -261,7 +263,7 @@ def get_frozen_deps(self, dep_packages, exclude_packages=None):
261263
lines.append(requirement)
262264
continue
263265

264-
require_dist = "%s (==%s)" % (pkg_name, dep_package.package.version)
266+
require_dist = "%s (==%s)" % (dep_package.package.name, dep_package.package.version)
265267
if ";" in requirement:
266268
markers = requirement.split(";", 1)[1].strip()
267269
require_dist += f" ; {markers}"
@@ -283,7 +285,6 @@ def replace_deps(self, dist_meta, dep_lines):
283285

284286
def get_path_deps(self, group="dev"):
285287
# assuming we're consistent install across deps.
286-
package_deps = {}
287288
group = self.poetry.package.dependency_group(group)
288289
for dep in group.dependencies:
289290
if not (dep.is_file() or dep.is_directory()):
@@ -296,8 +297,7 @@ def get_path_deps(self, group="dev"):
296297
iced_dep = iced.poetry.package.to_dependency()
297298
iced_dep.marker = MultiMarker(dep.marker, iced_dep.marker)
298299
package_dep = DependencyPackage(dependency=iced_dep, package=iced.poetry.package)
299-
package_deps[dep.name] = package_dep
300-
return package_deps
300+
yield package_dep
301301

302302
def freeze_record(self, records_fh, dist_meta, md_path):
303303
hash_digest = get_sha256_digest(str(dist_meta).encode("utf8"))
@@ -330,9 +330,8 @@ def freeze_wheel(self, wheel_path, dep_packages):
330330
# freeze deps in metadata and update records
331331
md_text = source_whl.open(md_path).read().decode("utf8")
332332
dist_meta = Parser().parsestr(md_text)
333-
deps = self.get_path_deps(MAIN_GROUP)
334-
deps.update(dep_packages)
335-
dep_lines = self.get_frozen_deps(deps, self.exclude_packages)
333+
path_deps = self.get_path_deps(MAIN_GROUP)
334+
dep_lines = self.get_frozen_deps(chain(path_deps, dep_packages), self.exclude_packages)
336335
if dep_lines:
337336
self.replace_deps(dist_meta, dep_lines)
338337

Binary file not shown.

tests/fixtures/nested_packages/others/app_c/poetry.lock

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

tests/fixtures/nested_packages/others/app_c/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ packages = [
1111
python = "^3.10"
1212
pytest = "^7.1"
1313
pytest-cov = "^4.0"
14+
urllib3 = [
15+
{ version = "<2", python = "<3.11" },
16+
{ version = "*", python = ">=3.11" }
17+
]
1418

1519
[tool.poetry.group.dev.dependencies]
1620
app_b = {path = "../..", develop = true}

tests/test_freeze.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,19 @@ def test_freeze_nested(fixture_root, fixture_copy):
9191
)
9292

9393
expected_headers = [
94-
("Metadata-Version", "2.1"),
95-
("Name", "app-c"),
94+
("Metadata-Version", "2.3"),
95+
("Name", "app_c"),
9696
("Version", "0.2"),
9797
("Summary", "lorem ipsum"),
9898
("License", "Apache-2.0"),
9999
("Author", "SideCars"),
100-
("Requires-Python", ">=3.11,<4.0"),
100+
("Requires-Python", ">=3.10,<4.0"),
101101
("Classifier", "License :: OSI Approved :: Apache Software License"),
102102
("Classifier", "Programming Language :: Python :: 3"),
103+
("Classifier", "Programming Language :: Python :: 3.10"),
103104
("Classifier", "Programming Language :: Python :: 3.11"),
105+
("Classifier", "Programming Language :: Python :: 3.12"),
106+
("Classifier", "Programming Language :: Python :: 3.13"),
104107
(
105108
"Requires-Dist",
106109
'pytest (==7.2.2) ; python_version >= "3.10" and python_version < "4.0"',
@@ -134,6 +137,14 @@ def test_freeze_nested(fixture_root, fixture_copy):
134137
"Requires-Dist",
135138
'tomli (==2.0.1) ; python_version >= "3.10" and python_full_version <= ' '"3.11.0a6"',
136139
),
140+
(
141+
"Requires-Dist",
142+
'urllib3 (==1.26.20) ; python_version >= "3.10" and python_version < "3.11"',
143+
),
144+
(
145+
"Requires-Dist",
146+
'urllib3 (==2.3.0) ; python_version >= "3.11" and python_version < "4.0"',
147+
),
137148
(
138149
"Requires-Dist",
139150
'pytest-cov (==4.0.0) ; python_version >= "3.10" and python_version < "4.0"',
@@ -153,20 +164,20 @@ def test_freeze_nested(fixture_root, fixture_copy):
153164
],
154165
[
155166
"app_c-0.2.dist-info/WHEEL",
156-
"sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y",
167+
"sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM",
157168
"88",
158169
],
159170
["app_c-0.2.dist-info/RECORD", "", ""],
160171
[
161172
"app_c-0.2.dist-info/METADATA",
162-
"sha256=ZTdp4AJVW1WFj_Wv5oUVdtUC1_5r9bYWNxDzssJgO6o",
163-
"1217",
173+
"sha256=Y0clowJC222mCfG3SHng-M--m9e9Xz9CHRVarGNDlcY",
174+
"1547",
164175
],
165176
]
166177

167178
md_bytes = wheel.open(f"{iced_sub.distro_name}-{iced_sub.version}.dist-info/METADATA").read()
168-
assert len(md_bytes) == 1217
169-
assert get_sha256_digest(md_bytes) == "ZTdp4AJVW1WFj_Wv5oUVdtUC1_5r9bYWNxDzssJgO6o"
179+
assert len(md_bytes) == 1547
180+
assert get_sha256_digest(md_bytes) == "Y0clowJC222mCfG3SHng-M--m9e9Xz9CHRVarGNDlcY"
170181

171182

172183
def test_freeze_no_deps(fixture_root, fixture_copy):

0 commit comments

Comments
 (0)