From ab636c37699191dfcb770c9b4ab0a1f34ebe6f79 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Thu, 13 Aug 2020 14:07:08 +0200 Subject: [PATCH 1/5] use csv writer to write RECORD file for wheel package --- poetry/core/masonry/builders/wheel.py | 12 +++++++++--- .../fixtures/comma_file/comma_file/__init__.py | 0 .../builders/fixtures/comma_file/comma_file/a,b.py | 0 .../builders/fixtures/comma_file/pyproject.toml | 12 ++++++++++++ tests/masonry/builders/test_wheel.py | 13 +++++++++++++ 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/masonry/builders/fixtures/comma_file/comma_file/__init__.py create mode 100644 tests/masonry/builders/fixtures/comma_file/comma_file/a,b.py create mode 100644 tests/masonry/builders/fixtures/comma_file/pyproject.toml diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 08d0423e2..c8fa8c566 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import contextlib +import csv import hashlib import logging import os @@ -37,7 +38,6 @@ class WheelBuilder(Builder): - format = "wheel" def __init__(self, poetry, target_dir=None, original=None): @@ -179,10 +179,16 @@ def _write_metadata(self, wheel): def _write_record(self, wheel): # Write a record of the files in the wheel with self._write_to_zip(wheel, self.dist_info + "/RECORD") as f: + record = StringIO() + csv_writer = csv.writer( + record, delimiter=",", quotechar='"', lineterminator="\n" + ) for path, hash, size in self._records: - f.write("{},sha256={},{}\n".format(path, hash, size)) + csv_writer.writerow((path, "sha256={}".format(hash), size)) + # RECORD itself is recorded with no hash or size - f.write(self.dist_info + "/RECORD,,\n") + csv_writer.writerow((self.dist_info + "/RECORD", "", "")) + f.write(record.getvalue()) @property def dist_info(self): # type: () -> str diff --git a/tests/masonry/builders/fixtures/comma_file/comma_file/__init__.py b/tests/masonry/builders/fixtures/comma_file/comma_file/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/builders/fixtures/comma_file/comma_file/a,b.py b/tests/masonry/builders/fixtures/comma_file/comma_file/a,b.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/masonry/builders/fixtures/comma_file/pyproject.toml b/tests/masonry/builders/fixtures/comma_file/pyproject.toml new file mode 100644 index 000000000..110845e3f --- /dev/null +++ b/tests/masonry/builders/fixtures/comma_file/pyproject.toml @@ -0,0 +1,12 @@ +[tool.poetry] +name = "comma-file" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +[tool.poetry.dependencies] +python = "^3.6" + +[tool.poetry.dev-dependencies] + diff --git a/tests/masonry/builders/test_wheel.py b/tests/masonry/builders/test_wheel.py index 2fd81bc23..db12f7a83 100644 --- a/tests/masonry/builders/test_wheel.py +++ b/tests/masonry/builders/test_wheel.py @@ -198,3 +198,16 @@ def test_wheel_package_pep_561_stub_only_includes_typed_marker(): with zipfile.ZipFile(str(whl)) as z: assert "pkg-stubs/py.typed" in z.namelist() + + +def test_wheel_with_file_with_comma(): + root = fixtures_dir / "comma_file" + WheelBuilder.make(Factory().create_poetry(root)) + + whl = root / "dist" / "comma_file-1.2.3-py3-none-any.whl" + + assert whl.exists() + + with zipfile.ZipFile(str(whl)) as z: + records = z.read("comma_file-1.2.3.dist-info/RECORD") + assert '\n"comma_file/a,b.py"' in records.decode() From 1480edeb62945ca3be5c4ec5b64ed60c496e244d Mon Sep 17 00:00:00 2001 From: finswimmer Date: Thu, 13 Aug 2020 14:16:19 +0200 Subject: [PATCH 2/5] mark delimiter and quotechar explicit as string --- poetry/core/masonry/builders/wheel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index c8fa8c566..5b44d73ab 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -181,7 +181,7 @@ def _write_record(self, wheel): with self._write_to_zip(wheel, self.dist_info + "/RECORD") as f: record = StringIO() csv_writer = csv.writer( - record, delimiter=",", quotechar='"', lineterminator="\n" + record, delimiter=str(","), quotechar=str('"'), lineterminator="\n" ) for path, hash, size in self._records: csv_writer.writerow((path, "sha256={}".format(hash), size)) From 16029334107ce6365c6ab012fd42352eaf6f7702 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Thu, 13 Aug 2020 19:50:46 +0200 Subject: [PATCH 3/5] use BytesIO instead of StringIO if using python2 --- poetry/core/masonry/builders/wheel.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 5b44d73ab..202086ff0 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -13,11 +13,13 @@ import zipfile from base64 import urlsafe_b64encode +from io import BytesIO from io import StringIO from packaging.tags import sys_tags from poetry.core import __version__ from poetry.core.semver import parse_constraint +from poetry.core.utils._compat import PY2 from poetry.core.utils._compat import decode from ..utils.helpers import escape_name @@ -180,6 +182,9 @@ def _write_record(self, wheel): # Write a record of the files in the wheel with self._write_to_zip(wheel, self.dist_info + "/RECORD") as f: record = StringIO() + if PY2: + record = BytesIO() + csv_writer = csv.writer( record, delimiter=str(","), quotechar=str('"'), lineterminator="\n" ) @@ -188,7 +193,12 @@ def _write_record(self, wheel): # RECORD itself is recorded with no hash or size csv_writer.writerow((self.dist_info + "/RECORD", "", "")) - f.write(record.getvalue()) + + record_values = record.getvalue() + if PY2: + record_values = record_values.decode("utf-8") + + f.write(record_values) @property def dist_info(self): # type: () -> str From 72cf5043ff208a1b60f59b1febc1e9938604c658 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Thu, 13 Aug 2020 20:46:34 +0200 Subject: [PATCH 4/5] * use _compat.decode() method * use csv.excel constants to define delimiter and quotechar --- poetry/core/masonry/builders/wheel.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 202086ff0..8e2ad674f 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -186,7 +186,10 @@ def _write_record(self, wheel): record = BytesIO() csv_writer = csv.writer( - record, delimiter=str(","), quotechar=str('"'), lineterminator="\n" + record, + delimiter=csv.excel.delimiter, + quotechar=csv.excel.quotechar, + lineterminator="\n", ) for path, hash, size in self._records: csv_writer.writerow((path, "sha256={}".format(hash), size)) @@ -194,11 +197,7 @@ def _write_record(self, wheel): # RECORD itself is recorded with no hash or size csv_writer.writerow((self.dist_info + "/RECORD", "", "")) - record_values = record.getvalue() - if PY2: - record_values = record_values.decode("utf-8") - - f.write(record_values) + f.write(decode(record.getvalue())) @property def dist_info(self): # type: () -> str From fbc7b01690700e18e309623c10dfeb452489df9a Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 14 Aug 2020 06:34:36 +0200 Subject: [PATCH 5/5] use conditional expression to decide whether to use StringIO or BytesIO --- poetry/core/masonry/builders/wheel.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 8e2ad674f..ecebe1c96 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -181,9 +181,7 @@ def _write_metadata(self, wheel): def _write_record(self, wheel): # Write a record of the files in the wheel with self._write_to_zip(wheel, self.dist_info + "/RECORD") as f: - record = StringIO() - if PY2: - record = BytesIO() + record = StringIO() if not PY2 else BytesIO() csv_writer = csv.writer( record,