Skip to content

Commit 2356104

Browse files
authored
44 Make regex capture groups from patterns available in jinja (#70)
1 parent a595629 commit 2356104

4 files changed

Lines changed: 54 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ The [Changelogger tool](https://pypi.org/project/changelogged) is used for autom
1414
<!-- BEGIN RELEASE NOTES -->
1515
### [Unreleased]
1616

17+
### [0.11.0] - 2023-03-05
18+
19+
#### Added
20+
- Jinja templates now have access to the `match` variable. This allows access to match groups.
21+
1722
### [0.10.2] - 2023-03-05
1823

1924
#### Changed
@@ -132,7 +137,8 @@ The [Changelogger tool](https://pypi.org/project/changelogged) is used for autom
132137
- `unreleased add`, which allows inline or prompted adding of unreleased changes.
133138
<!-- END RELEASE NOTES -->
134139
<!-- BEGIN LINKS -->
135-
[Unreleased]: https://github.com/award28/changelogger/compare/0.10.2...HEAD
140+
[Unreleased]: https://github.com/award28/changelogger/compare/0.11.0...HEAD
141+
[0.11.0]: https://github.com/award28/changelogger/compare/0.10.2...0.11.0
136142
[0.10.2]: https://github.com/award28/changelogger/compare/0.10.1...0.10.2
137143
[0.10.1]: https://github.com/award28/changelogger/compare/0.10.0...0.10.1
138144
[0.10.0]: https://github.com/award28/changelogger/compare/0.9.1...0.10.0

changelogger/templating.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from datetime import date
2+
from functools import partial
3+
from re import Match
24
from typing import Any
35

46
from jinja2 import BaseLoader, Environment, Template
@@ -20,11 +22,16 @@ def update(
2022
assert file.jinja_rel_path, "No valid jinja template found."
2123
replacement_str = file.jinja_rel_path.read_text()
2224

23-
variables = _get_variables(file, update)
24-
pattern = render_jinja(file.pattern, variables)
25-
replacement = render_jinja(replacement_str, variables)
25+
var_getter = partial(_get_variables, file, update)
26+
pattern = render_jinja(file.pattern, var_getter())
2627

27-
return cached_compile(pattern).sub(replacement, content)
28+
# re.sub can take a callable as the replacement argument rather than a
29+
# string. This callable accepts a match and returns a string. For each
30+
# match which is found, re.sub will call repl with the match, and
31+
# replace the found pattern with the output string of the user supplied
32+
# repl function.
33+
repl = lambda m: render_jinja(replacement_str, var_getter(m))
34+
return cached_compile(pattern).sub(repl, content)
2835

2936

3037
def render_pattern(
@@ -47,11 +54,13 @@ def _tmpl(jinja: str) -> Template:
4754
def _get_variables(
4855
versioned_file: VersionedFile,
4956
update: ChangelogUpdate,
57+
match: Match | None = None,
5058
) -> dict[str, Any]:
5159
return dict(
5260
new_version=update.new_version,
5361
old_version=update.old_version,
5462
today=date.today(),
5563
sections=update.release_notes.dict(),
5664
context=versioned_file.context,
65+
match=match,
5766
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "changelogged"
3-
version = "0.10.2"
3+
version = "0.11.0"
44
description = "Automated management of your changelog and other versioned files, following the principles of Keep a Changelog and Semantic Versioning."
55
license = "MIT"
66
authors = ["award28 <austin.ward@klaviyo.com>"]

tests/test_templating.py

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,35 @@ def test_render_pattern(
9999
)
100100
mock_render_jinja.assert_called_once()
101101

102+
def test_update(
103+
self,
104+
) -> None:
105+
file = MagicMock()
106+
file.pattern = r"# This (?P<word>\w+)(?P<rest>.*)"
107+
file.jinja = r"# This {{ match.rest | reverse }}{{ match.word }}"
108+
file.context = {}
109+
file.jinja_rel_path = None
110+
111+
content = """
112+
# This is a test
113+
# Not being tested
114+
# This should be tested
115+
"""
116+
117+
expected = """
118+
# This tset a is
119+
# Not being tested
120+
# This detset eb should
121+
"""
122+
123+
update = MagicMock()
124+
actual = templating.update(
125+
file,
126+
update,
127+
content,
128+
)
129+
assert actual == expected
130+
102131
def test_update_neither_jinja_raises(
103132
self,
104133
):
@@ -134,20 +163,15 @@ def test_update_from_jinja_string(
134163
[
135164
call(file.pattern),
136165
call().render(),
137-
call(file.jinja),
138-
call().render(),
139166
]
140167
)
141168

142169
mock_tmpl().render.assert_has_calls(
143-
[call(**mock_get_variables())] * 2,
170+
[call(**mock_get_variables())],
144171
)
145172

146173
mock_cached_compile.assert_called_once_with(mock_tmpl().render())
147-
mock_cached_compile().sub.assert_called_once_with(
148-
mock_tmpl().render(),
149-
content,
150-
)
174+
mock_cached_compile().sub.assert_called_once()
151175

152176
def test_update_from_jinja_file(
153177
self,
@@ -171,17 +195,12 @@ def test_update_from_jinja_file(
171195
[
172196
call(file.pattern),
173197
call().render(),
174-
call(file.jinja_rel_path.read_text()),
175-
call().render(),
176198
]
177199
)
178200

179201
mock_tmpl().render.assert_has_calls(
180-
[call(**mock_get_variables())] * 2,
202+
[call(**mock_get_variables())],
181203
)
182204

183205
mock_cached_compile.assert_called_once_with(mock_tmpl().render())
184-
mock_cached_compile().sub.assert_called_once_with(
185-
mock_tmpl().render(),
186-
content,
187-
)
206+
mock_cached_compile().sub.assert_called_once()

0 commit comments

Comments
 (0)