From 152332c4a07911b4ca089323a5de4a2931c16a3d Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Mon, 24 Oct 2022 20:52:31 +0200 Subject: [PATCH 1/6] expanded set of special character escapes --- CHANGELOG.md | 1 + bumpversion/cli.py | 6 +++++- tests/test_cli.py | 49 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c0bafb..d1157f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ **unreleased** **v1.0.2-dev** +- Added: expanded set of special character escapes and added documentation - Housekeeping: migrated from travis+appveyor to GitHub Actions for CI, thanks @clbarnes **v1.0.1** diff --git a/bumpversion/cli.py b/bumpversion/cli.py index d627d702..5935da75 100644 --- a/bumpversion/cli.py +++ b/bumpversion/cli.py @@ -57,7 +57,11 @@ logger_list = logging.getLogger("bumpversion.list") logger = logging.getLogger(__name__) time_context = {"now": datetime.now(), "utcnow": datetime.utcnow()} -special_char_context = {c: c for c in ("#", ";")} +special_char_context = { + **{c: c for c in ("#", ";")}, + "space": " ", + "tab": "\t", +} OPTIONAL_ARGUMENTS_THAT_TAKE_VALUES = [ diff --git a/tests/test_cli.py b/tests/test_cli.py index fbbd38d8..672f6d71 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1401,8 +1401,8 @@ def test_subjunctive_dry_run_logging(tmpdir, vcs): commit = True tag = True serialize = - {major}.{minor}.{patch} - {major}.{minor} + {major}.{minor}.{patch} + {major}.{minor} parse = (?P\d+)\.(?P\d+)(\.(?P\d+))? [bumpversion:file:dont_touch_me.txt] """).strip()) @@ -2392,3 +2392,48 @@ def test_independent_falsy_value_in_config_does_not_bump_independently(tmpdir): main(['major']) assert '3.0.0-0' == tmpdir.join("VERSION").read() + + +@pytest.mark.parametrize( + "char, var", + [ + ("#", "#"), + (";", ";"), + (" ", "space"), + ("\t", "tab"), + ], +) +def test_special_char_escapes(tmpdir, char, var): + """ + Verify behavior of escapes (through string format variables) for characters that have a special meaning in the .cfg file + syntax when used in a value. + """ + tmpdir.join("VERSION").write( + dedent( + f""" + 1.0.0 + {char}1.0.0 + """.lstrip("\n") + ) + ) + tmpdir.join(".bumpversion.cfg").write( + dedent( + f""" + [bumpversion] + current_version: 1.0.0 + search = {{{var}}}{{current_version}} + replace = {{{var}}}{{new_version}} + + [bumpversion:file:VERSION] + """ + ) + ) + tmpdir.chdir() + main(["major"]) + assert tmpdir.join("VERSION").read() == dedent( + # assert that first line did not match while second did + f""" + 1.0.0 + {char}2.0.0 + """.lstrip("\n") + ) From 3a6411b2ce16b6a4e47477a5afe82b03f0af8afa Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Mon, 24 Oct 2022 21:22:08 +0200 Subject: [PATCH 2/6] added documentation --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index 0e2fa552..fb8f5033 100644 --- a/README.md +++ b/README.md @@ -472,6 +472,46 @@ search = MY_VERSION="{current_version}" replace = MY_VERSION="{new_version}" ``` +### Configuration file -- Special characters + +Some characters have a special meaning in the config file and may need to be +escaped if you want to use them as part of values. Examples are comment +characters `#` and `;`, as well as whitespace characters. Leading whitespace +characters are dropped, and comment characters at the beginning of a line make +the whole line a comment. When you do need to express these characters at the +beginning of a line, you can use `{#}`, `{;}`, `{space}` and `{tab}` +respectively. + +For example, given the following `version.json`: +``` +{ + "name": "mypackage", + "version": "1.0.0", + "dependencies": [ + { + "name": "mydependency", + "version": "1.0.0" + } + ] +} +``` + +Suppose we want to match the version for "mypackage", but not the version of any +dependency. This could be specified as follows: +```ini +[bumpversion] +current_version = 1.0.0 +# leading spaces are dropped: use {space} instead for at least the first space +# we want to match on each line +search = "name": "mypackage", + {space}{space}"version": "{current_version}" +replace = "name": "mypackage", + {space}{space}"version": "{new_version}" + +[bumpversion:file:version.json] +``` + + ## Command-line Options Most of the configuration values above can also be given as an option on the command-line. From 459c3b465d6e5be76e01e66bf7e6f43574efac1e Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Mon, 24 Oct 2022 21:26:20 +0200 Subject: [PATCH 3/6] rewording --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fb8f5033..f40437c1 100644 --- a/README.md +++ b/README.md @@ -478,9 +478,10 @@ Some characters have a special meaning in the config file and may need to be escaped if you want to use them as part of values. Examples are comment characters `#` and `;`, as well as whitespace characters. Leading whitespace characters are dropped, and comment characters at the beginning of a line make -the whole line a comment. When you do need to express these characters at the -beginning of a line, you can use `{#}`, `{;}`, `{space}` and `{tab}` -respectively. +the whole line a comment. + +When you do need to express these characters at the beginning of a line, you can +use the special variables `{#}`, `{;}`, `{space}` and `{tab}` respectively. For example, given the following `version.json`: ``` From cce400b4eeaaf0347dd3510d4775d7e2be09e8d6 Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Mon, 24 Oct 2022 21:35:14 +0200 Subject: [PATCH 4/6] added test for docs snippet --- tests/test_cli.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_cli.py b/tests/test_cli.py index 672f6d71..5375ee6e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2437,3 +2437,45 @@ def test_special_char_escapes(tmpdir, char, var): {char}2.0.0 """.lstrip("\n") ) + + +def test_special_characters_docs(tmpdir): + """ + Verify that special characters snippets in the documentation works as advertized. + """ + def json_for_version(version): + return dedent( + f""" + {{ + "name": "mypackage", + "version": "{version}", + "dependencies": [ + {{ + "name": "mydependency", + "version": "1.0.0" + }} + ] + }} + """.lstrip("\n") + ) + + tmpdir.join("version.json").write(json_for_version("1.0.0")) + tmpdir.join(".bumpversion.cfg").write( + dedent( + """ + [bumpversion] + current_version = 1.0.0 + # leading spaces are dropped: use {space} instead for at least the first space + # we want to match on each line + search = "name": "mypackage", + {space}{space}"version": "{current_version}" + replace = "name": "mypackage", + {space}{space}"version": "{new_version}" + + [bumpversion:file:version.json] + """ + ) + ) + tmpdir.chdir() + main(["major"]) + assert tmpdir.join("version.json").read() == json_for_version("2.0.0") From 0db135644910046b4fdcdba168e1f1d6962b4984 Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Mon, 24 Oct 2022 21:40:33 +0200 Subject: [PATCH 5/6] fixed test case --- tests/test_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 5375ee6e..0c6f3bcf 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1401,8 +1401,8 @@ def test_subjunctive_dry_run_logging(tmpdir, vcs): commit = True tag = True serialize = - {major}.{minor}.{patch} - {major}.{minor} + {major}.{minor}.{patch} + {major}.{minor} parse = (?P\d+)\.(?P\d+)(\.(?P\d+))? [bumpversion:file:dont_touch_me.txt] """).strip()) From 823029e8c68fc038f796ca5043e982e6995a61cf Mon Sep 17 00:00:00 2001 From: Sander Van Balen Date: Mon, 24 Oct 2022 22:00:27 +0200 Subject: [PATCH 6/6] python 3.5 compatibility --- tests/test_cli.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 0c6f3bcf..a7b9dafb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2410,32 +2410,32 @@ def test_special_char_escapes(tmpdir, char, var): """ tmpdir.join("VERSION").write( dedent( - f""" + """ 1.0.0 - {char}1.0.0 - """.lstrip("\n") + %s1.0.0 + """.lstrip("\n") % char ) ) tmpdir.join(".bumpversion.cfg").write( dedent( - f""" + """ [bumpversion] current_version: 1.0.0 - search = {{{var}}}{{current_version}} - replace = {{{var}}}{{new_version}} + search = {%s}{current_version} + replace = {%s}{new_version} [bumpversion:file:VERSION] - """ + """ % (var, var) ) ) tmpdir.chdir() main(["major"]) assert tmpdir.join("VERSION").read() == dedent( # assert that first line did not match while second did - f""" + """ 1.0.0 - {char}2.0.0 - """.lstrip("\n") + %s2.0.0 + """.lstrip("\n") % char ) @@ -2445,21 +2445,21 @@ def test_special_characters_docs(tmpdir): """ def json_for_version(version): return dedent( - f""" - {{ + """ + { "name": "mypackage", - "version": "{version}", + "version": "%s", "dependencies": [ - {{ + { "name": "mydependency", "version": "1.0.0" - }} + } ] - }} - """.lstrip("\n") + } + """.lstrip("\n") % version ) - tmpdir.join("version.json").write(json_for_version("1.0.0")) + tmpdir.join("version.json").write(json_for_version("1.0.0")) # same version as mydependency tmpdir.join(".bumpversion.cfg").write( dedent( """