From c5dce21648709758058f011c5691537b9dcfe191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Sun, 11 Jun 2023 13:57:49 +0200 Subject: [PATCH 1/3] Be explicit about encoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default encoding is not always UTF-8, make it explicit. Also use the surrogateescape error handler to preserve undecodable bytes. Signed-off-by: Nikola Forró --- specfile/specfile.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/specfile/specfile.py b/specfile/specfile.py index b4a8bb9..400b4bb 100644 --- a/specfile/specfile.py +++ b/specfile/specfile.py @@ -64,7 +64,7 @@ def __init__( """ self.autosave = autosave self._path = Path(path) - self._lines = self.path.read_text().splitlines() + self._lines = self._read_lines(self._path) self._parser = SpecParser( Path(sourcedir or self.path.parent), macros, force_parse ) @@ -101,6 +101,10 @@ def __exit__( ) -> None: self.save() + @staticmethod + def _read_lines(path: Path) -> List[str]: + return path.read_text(encoding="utf8", errors="surrogateescape").splitlines() + @property def path(self) -> Path: """Path to the spec file.""" @@ -154,11 +158,11 @@ def rpm_spec(self) -> rpm.spec: def reload(self) -> None: """Reload the spec file content.""" - self._lines = self.path.read_text().splitlines() + self._lines = self._read_lines(self.path) def save(self) -> None: """Save the spec file content.""" - self.path.write_text(str(self)) + self.path.write_text(str(self), encoding="utf8", errors="surrogateescape") def expand( self, From 7771658c0318b7cfae44a4c470af67a7efc38b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Sun, 11 Jun 2023 21:01:08 +0200 Subject: [PATCH 2/3] Add functional test to parse all Fedora spec files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nikola Forró --- plans/functional.fmf | 11 +++++++++++ tests/functional.fmf | 15 +++++++++++++++ tests/functional/__init__.py | 2 ++ tests/functional/conftest.py | 20 ++++++++++++++++++++ tests/functional/test_parse.py | 9 +++++++++ 5 files changed, 57 insertions(+) create mode 100644 plans/functional.fmf create mode 100644 tests/functional.fmf create mode 100644 tests/functional/__init__.py create mode 100644 tests/functional/conftest.py create mode 100644 tests/functional/test_parse.py diff --git a/plans/functional.fmf b/plans/functional.fmf new file mode 100644 index 0000000..6b62252 --- /dev/null +++ b/plans/functional.fmf @@ -0,0 +1,11 @@ +summary: + Functional tests +discover+: + filter: tier:2 +prepare: + - how: install + package: xz + - how: shell + script: | + curl -O https://src.fedoraproject.org/lookaside/rpm-specs-latest.tar.xz + tar -xf rpm-specs-latest.tar.xz -C /tmp diff --git a/tests/functional.fmf b/tests/functional.fmf new file mode 100644 index 0000000..1803639 --- /dev/null +++ b/tests/functional.fmf @@ -0,0 +1,15 @@ +summary: + Functional tests +require: + - python3-pytest + - python3-specfile + - redhat-rpm-config + - rpmautospec-rpm-macros + - "*-srpm-macros" +tag: + - functional +tier: 2 +duration: 1h +# running from the "tests" directory prevents pytest from processing "tests/functional/conftest.py" +path: / +test: python3 -m pytest --verbose --specdir=/tmp/rpm-specs tests/functional diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py new file mode 100644 index 0000000..e01ff12 --- /dev/null +++ b/tests/functional/__init__.py @@ -0,0 +1,2 @@ +# Copyright Contributors to the Packit project. +# SPDX-License-Identifier: MIT diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py new file mode 100644 index 0000000..9347581 --- /dev/null +++ b/tests/functional/conftest.py @@ -0,0 +1,20 @@ +# Copyright Contributors to the Packit project. +# SPDX-License-Identifier: MIT + +from pathlib import Path + + +def pytest_addoption(parser): + parser.addoption( + "--specdir", + action="store", + default=None, + help="path to a directory containing spec files", + ) + + +def pytest_generate_tests(metafunc): + if "spec_path" in metafunc.fixturenames: + specdir = metafunc.config.getoption("specdir") + specs = list(Path(specdir).glob("*.spec")) if specdir else [] + metafunc.parametrize("spec_path", specs, ids=lambda p: p.name) diff --git a/tests/functional/test_parse.py b/tests/functional/test_parse.py new file mode 100644 index 0000000..a26ecf6 --- /dev/null +++ b/tests/functional/test_parse.py @@ -0,0 +1,9 @@ +# Copyright Contributors to the Packit project. +# SPDX-License-Identifier: MIT + +from specfile import Specfile + + +def test_parse(spec_path): + spec = Specfile(spec_path, force_parse=True) + assert spec.expanded_version From 5e7c8abe822224680cf3e1ac55b2d2fc5ec326f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Tue, 13 Jun 2023 13:23:42 +0200 Subject: [PATCH 3/3] Fix a trivial bug in section parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nikola Forró --- specfile/sections.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/specfile/sections.py b/specfile/sections.py index b98284f..f467c6d 100644 --- a/specfile/sections.py +++ b/specfile/sections.py @@ -12,6 +12,7 @@ SECTION_OPTIONS, SIMPLE_SCRIPT_SECTIONS, ) +from specfile.exceptions import RPMException from specfile.formatter import formatted from specfile.macro_definitions import MacroDefinitions from specfile.macros import Macros @@ -241,9 +242,14 @@ def split_id(line): if len(tokens) > 2: # if the last token after macro expansion starts with a newline, # consider it part of section content - if expand(tokens[-1]).startswith("\n"): - content = [tokens.pop()] - separator = tokens.pop() + try: + expanded = expand(tokens[-1]) + except RPMException: + pass + else: + if expanded.startswith("\n"): + content = [tokens.pop()] + separator = tokens.pop() if len(tokens) > 2: name = tokens[0] delimiter = tokens[1]