From e48b8ee68cfaffb17f36d7da912850c4523f70b4 Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Wed, 26 Oct 2022 18:56:02 +0200 Subject: [PATCH 1/4] first approach --- bumpversion/utils.py | 53 +++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/bumpversion/utils.py b/bumpversion/utils.py index f872f230..5d2eb83d 100644 --- a/bumpversion/utils.py +++ b/bumpversion/utils.py @@ -1,6 +1,7 @@ from argparse import _AppendAction from difflib import unified_diff import io +import itertools import logging import os @@ -72,33 +73,39 @@ def should_contain_version(self, version, context): ) def contains(self, search): + """ + Returns true iff the search string is found in this file. Ignores differences in line endings (LF/CRLF). + """ if not search: return False + search_lines = search.splitlines(keepends=True) + # normalize line endings to LF + normalized_search = "\n".join(search_lines) + with open(self.path, "rt", encoding="utf-8") as f: - search_lines = search.splitlines() - lookbehind = [] - - for lineno, line in enumerate(f.readlines()): - lookbehind.append(line.rstrip("\n")) - - if len(lookbehind) > len(search_lines): - lookbehind = lookbehind[1:] - - if ( - search_lines[0] in lookbehind[0] - and search_lines[-1] in lookbehind[-1] - and search_lines[1:-1] == lookbehind[1:-1] - ): - logger.info( - "Found '%s' in %s at line %s: %s", - search, - self.path, - lineno - (len(lookbehind) - 1), - line.rstrip(), - ) - return True - return False + file_lines = f.readlines() + normalized_file = "\n".join(file_lines) + index = normalized_file.find(normalized_search) + if index == -1: + return False + accumulative_line_length = itertools.accumulate( + itertools.chain([0], file_lines), # accumulate does not take initial arg in 3.5 + lambda acc, line: acc + len(line) + 1, + ) + *_, (match_line, _) = itertools.takewhile( + lambda tup: tup[1] <= index, + enumerate(accumulative_line_length), + ) + logger.info( + "Found '%s' in %s at line %s: %s", + search, + self.path, + match_line, + file_lines[match_line + len(search_lines) - 1].rstrip(), + ) + return True + def replace(self, current_version, new_version, context, dry_run): From 2c9232c8f27e4b8d3fb0857f965b02c5a38f3ca2 Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Wed, 26 Oct 2022 21:16:20 +0200 Subject: [PATCH 2/4] other approach --- bumpversion/utils.py | 49 +++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/bumpversion/utils.py b/bumpversion/utils.py index 5d2eb83d..b3dd64ad 100644 --- a/bumpversion/utils.py +++ b/bumpversion/utils.py @@ -1,7 +1,6 @@ from argparse import _AppendAction from difflib import unified_diff import io -import itertools import logging import os @@ -73,39 +72,29 @@ def should_contain_version(self, version, context): ) def contains(self, search): - """ - Returns true iff the search string is found in this file. Ignores differences in line endings (LF/CRLF). - """ if not search: return False - search_lines = search.splitlines(keepends=True) - # normalize line endings to LF - normalized_search = "\n".join(search_lines) - with open(self.path, "rt", encoding="utf-8") as f: - file_lines = f.readlines() - normalized_file = "\n".join(file_lines) - index = normalized_file.find(normalized_search) - if index == -1: - return False - accumulative_line_length = itertools.accumulate( - itertools.chain([0], file_lines), # accumulate does not take initial arg in 3.5 - lambda acc, line: acc + len(line) + 1, - ) - *_, (match_line, _) = itertools.takewhile( - lambda tup: tup[1] <= index, - enumerate(accumulative_line_length), - ) - logger.info( - "Found '%s' in %s at line %s: %s", - search, - self.path, - match_line, - file_lines[match_line + len(search_lines) - 1].rstrip(), - ) - return True - + search_lines = search.splitlines() + lookbehind = [] + + for lineno, line in enumerate(f.readlines()): + lookbehind.append(line.rstrip("\n")) + + if len(lookbehind) > len(search_lines): + lookbehind = lookbehind[1:] + + if "\n".join(search_lines) in "\n".join(lookbehind): + logger.info( + "Found '%s' in %s at line %s: %s", + search, + self.path, + lineno - (len(lookbehind) - 1), + line.rstrip(), + ) + return True + return False def replace(self, current_version, new_version, context, dry_run): From 8025e8aaf8e5aeab9caa56b9d8191b4c02440699 Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Wed, 26 Oct 2022 21:25:10 +0200 Subject: [PATCH 3/4] added test --- tests/test_cli.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/test_cli.py b/tests/test_cli.py index fbbd38d8..00aa17d5 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1923,6 +1923,49 @@ def test_non_matching_search_does_not_modify_file(tmpdir): assert config_content in tmpdir.join(".bumpversion.cfg").read() +def test_non_matching_search_does_not_modify_file_multiline(tmpdir): + # TODO: docstring + refer to issue nr + tmpdir.chdir() + + version_content = dedent( + """ + multi TRAILING + line + matching 1.0.0 + + multi + line + LEADING matching 1.0.0 + """ + ) + + config_content = dedent( + """ + [bumpversion] + current_version = 1.0.0 + + [bumpversion:file:VERSION] + search = multi + line + matching + replace = REPLACEMENT + """ + ) + + tmpdir.join("VERSION").write(version_content) + tmpdir.join(".bumpversion.cfg").write(config_content) + + with pytest.raises( + exceptions.VersionNotFoundException, + match="Did not find 'multi\nline\nmatching' in file: 'VERSION'" + ): + # TODO: verbose required? + main(['patch', '--verbose']) + + assert version_content == tmpdir.join("VERSION").read() + assert config_content in tmpdir.join(".bumpversion.cfg").read() + + def test_search_replace_cli(tmpdir): tmpdir.join("file89").write("My birthday: 3.5.98\nCurrent version: 3.5.98") tmpdir.chdir() From 38f800d3b27497ab55abb30fd8f00a169ed1040c Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Wed, 26 Oct 2022 21:29:10 +0200 Subject: [PATCH 4/4] todos --- tests/test_cli.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 00aa17d5..c420aac7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1924,7 +1924,10 @@ def test_non_matching_search_does_not_modify_file(tmpdir): def test_non_matching_search_does_not_modify_file_multiline(tmpdir): - # TODO: docstring + refer to issue nr + """ + Verify that a multiline search with trailing characters on the first line or leading characters on the last line does not + match (#198). + """ tmpdir.chdir() version_content = dedent( @@ -1959,8 +1962,7 @@ def test_non_matching_search_does_not_modify_file_multiline(tmpdir): exceptions.VersionNotFoundException, match="Did not find 'multi\nline\nmatching' in file: 'VERSION'" ): - # TODO: verbose required? - main(['patch', '--verbose']) + main(['patch']) assert version_content == tmpdir.join("VERSION").read() assert config_content in tmpdir.join(".bumpversion.cfg").read()