diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98a20e3..4242597 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,6 +28,7 @@ repos: - id: check-yaml - id: detect-private-key - id: end-of-file-fixer + exclude: tests/data/spec_no_trailing_newline/test.spec - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/PyCQA/flake8 diff --git a/specfile/specfile.py b/specfile/specfile.py index 203231a..385986a 100644 --- a/specfile/specfile.py +++ b/specfile/specfile.py @@ -65,7 +65,7 @@ def __init__( """ self.autosave = autosave self._path = Path(path) - self._lines = self._read_lines(self._path) + self._lines, self._trailing_newline = self._read_lines(self._path) self._parser = SpecParser( Path(sourcedir or self.path.parent), macros, force_parse ) @@ -89,7 +89,7 @@ def __repr__(self) -> str: ) def __str__(self) -> str: - return "\n".join(self._lines) + "\n" + return "\n".join(self._lines) + ("\n" if self._trailing_newline else "") def __enter__(self) -> "Specfile": return self @@ -103,8 +103,9 @@ def __exit__( self.save() @staticmethod - def _read_lines(path: Path) -> List[str]: - return path.read_text(encoding="utf8", errors="surrogateescape").splitlines() + def _read_lines(path: Path) -> Tuple[List[str], bool]: + content = path.read_text(encoding="utf8", errors="surrogateescape") + return content.splitlines(), content[-1] == "\n" @property def path(self) -> Path: @@ -159,7 +160,7 @@ def rpm_spec(self) -> rpm.spec: def reload(self) -> None: """Reloads the spec file content.""" - self._lines = self._read_lines(self.path) + self._lines, self._trailing_newline = self._read_lines(self.path) def save(self) -> None: """Saves the spec file content.""" diff --git a/tests/constants.py b/tests/constants.py index 7860264..97c9265 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -18,6 +18,7 @@ SPEC_MULTIPLE_SOURCES = DATA_DIR / "spec_multiple_sources" SPEC_COMMENTED_PATCHES = DATA_DIR / "spec_commented_patches" SPEC_SHELL_EXPANSIONS = DATA_DIR / "spec_shell_expansions" +SPEC_NO_TRAILING_NEWLINE = DATA_DIR / "spec_no_trailing_newline" SPEC_CONDITIONALIZED_CHANGELOG = DATA_DIR / "spec_conditionalized_changelog" SPEC_CONDITIONALIZED_VERSION = DATA_DIR / "spec_conditionalized_version" diff --git a/tests/data/spec_no_trailing_newline/patch0.patch b/tests/data/spec_no_trailing_newline/patch0.patch new file mode 100644 index 0000000..ae1c84e --- /dev/null +++ b/tests/data/spec_no_trailing_newline/patch0.patch @@ -0,0 +1,18 @@ +From b7af0b9194585c6d208de3a0e9978d5ad9c5d97b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Nikola=20Forr=C3=B3?= +Date: Wed, 16 Mar 2022 10:29:59 +0100 +Subject: [PATCH 1/3] patch0 + +--- + test.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/test.txt b/test.txt +index 9daeafb..dec2cbe 100644 +--- a/test.txt ++++ b/test.txt +@@ -1 +1,2 @@ + test ++test +-- +2.35.1 diff --git a/tests/data/spec_no_trailing_newline/patch1.patch b/tests/data/spec_no_trailing_newline/patch1.patch new file mode 100644 index 0000000..d94ca7c --- /dev/null +++ b/tests/data/spec_no_trailing_newline/patch1.patch @@ -0,0 +1,19 @@ +From 6d5d1561b3ccf2df9d001a7af011144acc352361 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Nikola=20Forr=C3=B3?= +Date: Wed, 16 Mar 2022 10:30:15 +0100 +Subject: [PATCH 2/3] patch1 + +--- + test.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/test.txt b/test.txt +index dec2cbe..0867e73 100644 +--- a/test.txt ++++ b/test.txt +@@ -1,2 +1,3 @@ + test + test ++test +-- +2.35.1 diff --git a/tests/data/spec_no_trailing_newline/patch2.patch b/tests/data/spec_no_trailing_newline/patch2.patch new file mode 100644 index 0000000..c124147 --- /dev/null +++ b/tests/data/spec_no_trailing_newline/patch2.patch @@ -0,0 +1,20 @@ +From ae1d3bbca0caf1cce1842ceab4c6d7252c0a7bd8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Nikola=20Forr=C3=B3?= +Date: Wed, 16 Mar 2022 10:30:29 +0100 +Subject: [PATCH 3/3] patch2 + +--- + test.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/test.txt b/test.txt +index 0867e73..d0c7fbe 100644 +--- a/test.txt ++++ b/test.txt +@@ -1,3 +1,4 @@ + test + test + test ++test +-- +2.35.1 diff --git a/tests/data/spec_no_trailing_newline/test-0.1.tar.xz b/tests/data/spec_no_trailing_newline/test-0.1.tar.xz new file mode 100644 index 0000000..d6cd06f Binary files /dev/null and b/tests/data/spec_no_trailing_newline/test-0.1.tar.xz differ diff --git a/tests/data/spec_no_trailing_newline/test.spec b/tests/data/spec_no_trailing_newline/test.spec new file mode 100644 index 0000000..0d15e2a --- /dev/null +++ b/tests/data/spec_no_trailing_newline/test.spec @@ -0,0 +1,24 @@ +Name: test +Version: 0.1 +Release: 1%{?dist} +Summary: Test package + +License: MIT + +Source: %{name}-%{version}.tar.xz +Patch0: patch0.patch +Patch1: patch1.patch +Patch2: patch2.patch + + +%description +Test package + + +%prep +%autosetup -p1 + + +%changelog +* Thu Jun 07 2018 Nikola Forró - 0.1-1 +- first version \ No newline at end of file diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index aedc9ca..ad5ec20 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -15,6 +15,7 @@ SPEC_MACROS, SPEC_MINIMAL, SPEC_MULTIPLE_SOURCES, + SPEC_NO_TRAILING_NEWLINE, SPEC_PATCHLIST, SPEC_PRERELEASE, SPEC_PRERELEASE2, @@ -116,6 +117,13 @@ def spec_shell_expansions(tmp_path): return destination / SPECFILE +@pytest.fixture(scope="function") +def spec_no_trailing_newline(tmp_path): + destination = tmp_path / "spec_no_trailing_newline" + shutil.copytree(SPEC_NO_TRAILING_NEWLINE, destination) + return destination / SPECFILE + + @pytest.fixture(scope="function") def spec_conditionalized_changelog(tmp_path): specfile_path = tmp_path / SPECFILE diff --git a/tests/integration/test_specfile.py b/tests/integration/test_specfile.py index 0eabcd8..dc94a1f 100644 --- a/tests/integration/test_specfile.py +++ b/tests/integration/test_specfile.py @@ -661,3 +661,10 @@ def test_update_version( assert md.upstream_version.body != version assert spec.version == version assert spec.expanded_version == version + + +def test_trailing_newline(spec_autosetup, spec_no_trailing_newline): + spec = Specfile(spec_autosetup) + assert str(spec)[-1] == "\n" + spec = Specfile(spec_no_trailing_newline) + assert str(spec)[-1] != "\n"