Skip to content

Commit c6bd03b

Browse files
dimblebyradoering
authored andcommitted
fix for duplicate lines in wheel RECORD, prefer pathlib
1 parent 0809719 commit c6bd03b

File tree

4 files changed

+64
-41
lines changed

4 files changed

+64
-41
lines changed

src/poetry/core/json/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
from __future__ import annotations
22

33
import json
4-
import os
54

5+
from pathlib import Path
66
from typing import Any
77

88

9-
SCHEMA_DIR = os.path.join(os.path.dirname(__file__), "schemas")
9+
SCHEMA_DIR = Path(__file__).parent / "schemas"
1010

1111

1212
class ValidationError(ValueError):
1313
pass
1414

1515

1616
def validate_object(obj: dict[str, Any], schema_name: str) -> list[str]:
17-
schema_file = os.path.join(SCHEMA_DIR, f"{schema_name}.json")
17+
schema_file = SCHEMA_DIR / f"{schema_name}.json"
1818

19-
if not os.path.exists(schema_file):
19+
if not schema_file.exists():
2020
raise ValueError(f"Schema {schema_name} does not exist.")
2121

22-
with open(schema_file, encoding="utf-8") as f:
22+
with schema_file.open(encoding="utf-8") as f:
2323
schema = json.loads(f.read())
2424

2525
from jsonschema import Draft7Validator

src/poetry/core/masonry/builders/wheel.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,19 @@ def _build(self, wheel: zipfile.ZipFile) -> None:
163163
# we assume that the build script will build and copy the files
164164
# directly.
165165
# That way they will be picked up when adding files to the wheel.
166-
current_path = os.getcwd()
166+
current_path = Path.cwd()
167167
try:
168-
os.chdir(str(self._path))
168+
os.chdir(self._path)
169169
self._run_build_script(self._package.build_script)
170170
finally:
171171
os.chdir(current_path)
172172
else:
173173
with SdistBuilder(poetry=self._poetry).setup_py() as setup:
174174
# We need to place ourselves in the temporary
175175
# directory in order to build the package
176-
current_path = os.getcwd()
176+
current_path = Path.cwd()
177177
try:
178-
os.chdir(str(self._path))
178+
os.chdir(self._path)
179179
self._run_build_command(setup)
180180
finally:
181181
os.chdir(current_path)
@@ -194,9 +194,9 @@ def _build(self, wheel: zipfile.ZipFile) -> None:
194194
if pkg.is_dir() or self.is_excluded(pkg):
195195
continue
196196

197-
rel_path = str(pkg.relative_to(lib))
197+
rel_path = pkg.relative_to(lib)
198198

199-
if rel_path in wheel.namelist():
199+
if rel_path.as_posix() in wheel.namelist():
200200
continue
201201

202202
logger.debug(f"Adding: {rel_path}")
@@ -210,7 +210,7 @@ def _copy_file_scripts(self, wheel: zipfile.ZipFile) -> None:
210210
self._add_file(
211211
wheel,
212212
abs_path,
213-
Path.joinpath(Path(self.wheel_data_folder), "scripts", abs_path.name),
213+
Path(self.wheel_data_folder) / "scripts" / abs_path.name,
214214
)
215215

216216
def _run_build_command(self, setup: Path) -> None:
@@ -268,7 +268,7 @@ def prepare_metadata(self, metadata_directory: Path) -> Path:
268268
continue
269269

270270
dest = dist_info / license_file.relative_to(self._path)
271-
os.makedirs(dest.parent, exist_ok=True)
271+
dest.parent.mkdir(parents=True, exist_ok=True)
272272
shutil.copy(license_file, dest)
273273

274274
return dist_info
@@ -345,19 +345,15 @@ def tag(self) -> str:
345345
def _add_file(
346346
self,
347347
wheel: zipfile.ZipFile,
348-
full_path: Path | str,
349-
rel_path: Path | str,
348+
full_path: Path,
349+
rel_path: Path,
350350
) -> None:
351-
full_path, rel_path = str(full_path), str(rel_path)
352-
if os.sep != "/":
353-
# We always want to have /-separated paths in the zip file and in
354-
# RECORD
355-
rel_path = rel_path.replace(os.sep, "/")
356-
357-
zinfo = zipfile.ZipInfo(rel_path)
351+
# We always want to have /-separated paths in the zip file and in RECORD
352+
rel_path_name = rel_path.as_posix()
353+
zinfo = zipfile.ZipInfo(rel_path_name)
358354

359355
# Normalize permission bits to either 755 (executable) or 644
360-
st_mode = os.stat(full_path).st_mode
356+
st_mode = full_path.stat().st_mode
361357
new_mode = normalize_file_permissions(st_mode)
362358
zinfo.external_attr = (new_mode & 0xFFFF) << 16 # Unix attributes
363359

@@ -375,10 +371,10 @@ def _add_file(
375371
src.seek(0)
376372
wheel.writestr(zinfo, src.read(), compress_type=zipfile.ZIP_DEFLATED)
377373

378-
size = os.stat(full_path).st_size
374+
size = full_path.stat().st_size
379375
hash_digest = urlsafe_b64encode(hashsum.digest()).decode("ascii").rstrip("=")
380376

381-
self._records.append((rel_path, hash_digest, size))
377+
self._records.append((rel_path_name, hash_digest, size))
382378

383379
@contextlib.contextmanager
384380
def _write_to_zip(

src/poetry/core/version/markers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,8 +830,10 @@ def dnf(marker: BaseMarker) -> BaseMarker:
830830
return MarkerUnion.of(
831831
*[MultiMarker.of(*c) for c in itertools.product(*sub_marker_lists)]
832832
)
833+
833834
if isinstance(marker, MarkerUnion):
834835
return MarkerUnion.of(*[dnf(m) for m in marker.markers])
836+
835837
return marker
836838

837839

tests/masonry/builders/test_complete.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import csv
34
import os
45
import platform
56
import re
@@ -47,7 +48,7 @@ def clear_samples_dist() -> None:
4748
or platform.python_implementation().lower() == "pypy",
4849
reason="Disable test on Windows for Python <=3.6 and for PyPy",
4950
)
50-
def test_wheel_c_extension() -> None:
51+
def test_wheel_c_extension() -> None: # NOSONAR
5152
module_path = fixtures_dir / "extended"
5253
builder = Builder(Factory().create_poetry(module_path))
5354
builder.build(fmt="all")
@@ -89,20 +90,27 @@ def test_wheel_c_extension() -> None:
8990
is not None
9091
)
9192

92-
records = zip.read("extended-0.1.dist-info/RECORD").decode()
93+
record = zip.read("extended-0.1.dist-info/RECORD").decode()
94+
records = csv.reader(record.splitlines())
95+
record_files = [row[0] for row in records]
9396

94-
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
97+
assert re.search(r"\s+extended/extended.*\.(so|pyd)", record) is not None
9598
finally:
9699
zip.close()
97100

101+
# Files in RECORD should match files in wheel.
102+
zip_files = sorted(zip.namelist())
103+
assert zip_files == sorted(record_files)
104+
assert len(set(record_files)) == len(record_files)
105+
98106

99107
@pytest.mark.skipif(
100108
sys.platform == "win32"
101109
and sys.version_info <= (3, 6)
102110
or platform.python_implementation().lower() == "pypy",
103111
reason="Disable test on Windows for Python <=3.6 and for PyPy",
104112
)
105-
def test_wheel_c_extension_with_no_setup() -> None:
113+
def test_wheel_c_extension_with_no_setup() -> None: # NOSONAR
106114
module_path = fixtures_dir / "extended_with_no_setup"
107115
builder = Builder(Factory().create_poetry(module_path))
108116
builder.build(fmt="all")
@@ -144,20 +152,27 @@ def test_wheel_c_extension_with_no_setup() -> None:
144152
is not None
145153
)
146154

147-
records = zip.read("extended-0.1.dist-info/RECORD").decode()
155+
record = zip.read("extended-0.1.dist-info/RECORD").decode()
156+
records = csv.reader(record.splitlines())
157+
record_files = [row[0] for row in records]
148158

149-
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
159+
assert re.search(r"\s+extended/extended.*\.(so|pyd)", record) is not None
150160
finally:
151161
zip.close()
152162

163+
# Files in RECORD should match files in wheel.
164+
zip_files = sorted(zip.namelist())
165+
assert zip_files == sorted(record_files)
166+
assert len(set(record_files)) == len(record_files)
167+
153168

154169
@pytest.mark.skipif(
155170
sys.platform == "win32"
156171
and sys.version_info <= (3, 6)
157172
or platform.python_implementation().lower() == "pypy",
158173
reason="Disable test on Windows for Python <=3.6 and for PyPy",
159174
)
160-
def test_wheel_c_extension_src_layout() -> None:
175+
def test_wheel_c_extension_src_layout() -> None: # NOSONAR
161176
module_path = fixtures_dir / "src_extended"
162177
builder = Builder(Factory().create_poetry(module_path))
163178
builder.build(fmt="all")
@@ -199,12 +214,19 @@ def test_wheel_c_extension_src_layout() -> None:
199214
is not None
200215
)
201216

202-
records = zip.read("extended-0.1.dist-info/RECORD").decode()
217+
record = zip.read("extended-0.1.dist-info/RECORD").decode()
218+
records = csv.reader(record.splitlines())
219+
record_files = [row[0] for row in records]
203220

204-
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
221+
assert re.search(r"\s+extended/extended.*\.(so|pyd)", record) is not None
205222
finally:
206223
zip.close()
207224

225+
# Files in RECORD should match files in wheel.
226+
zip_files = sorted(zip.namelist())
227+
assert zip_files == sorted(record_files)
228+
assert len(set(record_files)) == len(record_files)
229+
208230

209231
def test_complete() -> None:
210232
module_path = fixtures_dir / "complete"
@@ -297,20 +319,23 @@ def test_complete() -> None:
297319
# vary per operating systems and Python versions.
298320
# So instead of 1:1 assertion, let's do a bit clunkier one:
299321

300-
expected_records = [
322+
actual_files = [row[0] for row in csv.reader(actual_records.splitlines())]
323+
expected_files = [
324+
"my_package-1.2.3.data/scripts/script.sh",
325+
"my_package-1.2.3.dist-info/LICENSE",
326+
"my_package-1.2.3.dist-info/METADATA",
327+
"my_package-1.2.3.dist-info/RECORD",
328+
"my_package-1.2.3.dist-info/WHEEL",
329+
"my_package-1.2.3.dist-info/entry_points.txt",
301330
"my_package/__init__.py",
302331
"my_package/data1/test.json",
303332
"my_package/sub_pkg1/__init__.py",
304333
"my_package/sub_pkg2/__init__.py",
305334
"my_package/sub_pkg2/data2/data.json",
306-
"my_package-1.2.3.dist-info/entry_points.txt",
307-
"my_package-1.2.3.dist-info/LICENSE",
308-
"my_package-1.2.3.dist-info/WHEEL",
309-
"my_package-1.2.3.dist-info/METADATA",
335+
"my_package/sub_pkg3/foo.py",
310336
]
311337

312-
for expected_record in expected_records:
313-
assert expected_record in actual_records
338+
assert sorted(expected_files) == sorted(actual_files)
314339

315340
finally:
316341
zip.close()

0 commit comments

Comments
 (0)