32
32
from collections .abc import Iterable
33
33
from dataclasses import dataclass
34
34
from datetime import date
35
+ from itertools import islice
35
36
from typing import TYPE_CHECKING
36
37
37
38
from jinja2 import (
@@ -88,24 +89,21 @@ def generate_tree_from_commits(
88
89
pat = re .compile (changelog_pattern )
89
90
map_pat = re .compile (commit_parser , re .MULTILINE )
90
91
body_map_pat = re .compile (commit_parser , re .MULTILINE | re .DOTALL )
91
- current_tag : GitTag | None = None
92
92
rules = rules or TagRules ()
93
93
94
94
# Check if the latest commit is not tagged
95
- if commits :
96
- latest_commit = commits [0 ]
97
- current_tag = get_commit_tag (latest_commit , tags )
98
-
99
- current_tag_name : str = unreleased_version or "Unreleased"
100
- current_tag_date : str = ""
101
- if unreleased_version is not None :
102
- current_tag_date = date .today ().isoformat ()
95
+ current_tag = get_commit_tag (commits [0 ], tags ) if commits else None
96
+
97
+ current_tag_name = unreleased_version or "Unreleased"
98
+ current_tag_date = (
99
+ date .today ().isoformat () if unreleased_version is not None else ""
100
+ )
103
101
if current_tag is not None and current_tag .name :
104
102
current_tag_name = current_tag .name
105
103
current_tag_date = current_tag .date
106
104
107
- changes : dict = defaultdict (list )
108
- used_tags : list = [current_tag ]
105
+ changes : defaultdict [ str | None , list ] = defaultdict (list )
106
+ used_tags = [current_tag ]
109
107
for commit in commits :
110
108
commit_tag = get_commit_tag (commit , tags )
111
109
@@ -170,21 +168,23 @@ def process_commit_message(
170
168
changes : dict [str | None , list ],
171
169
change_type_map : dict [str , str ] | None = None ,
172
170
):
173
- message : dict = {
171
+ message = {
174
172
"sha1" : commit .rev ,
175
173
"parents" : commit .parents ,
176
174
"author" : commit .author ,
177
175
"author_email" : commit .author_email ,
178
176
** parsed .groupdict (),
179
177
}
178
+ processed = hook (message , commit ) if hook else message
179
+ if not processed :
180
+ return
180
181
181
- if processed := hook (message , commit ) if hook else message :
182
- messages = [processed ] if isinstance (processed , dict ) else processed
183
- for msg in messages :
184
- change_type = msg .pop ("change_type" , None )
185
- if change_type_map :
186
- change_type = change_type_map .get (change_type , change_type )
187
- changes [change_type ].append (msg )
182
+ processed_messages = [processed ] if isinstance (processed , dict ) else processed
183
+ for msg in processed_messages :
184
+ change_type = msg .pop ("change_type" , None )
185
+ if change_type_map :
186
+ change_type = change_type_map .get (change_type , change_type )
187
+ changes [change_type ].append (msg )
188
188
189
189
190
190
def order_changelog_tree (tree : Iterable , change_type_order : list [str ]) -> Iterable :
@@ -225,8 +225,7 @@ def render_changelog(
225
225
** kwargs ,
226
226
) -> str :
227
227
jinja_template = get_changelog_template (loader , template )
228
- changelog : str = jinja_template .render (tree = tree , ** kwargs )
229
- return changelog
228
+ return jinja_template .render (tree = tree , ** kwargs )
230
229
231
230
232
231
def incremental_build (
@@ -253,7 +252,9 @@ def incremental_build(
253
252
for index , line in enumerate (lines ):
254
253
if index == unreleased_start :
255
254
skip = True
256
- elif index == unreleased_end :
255
+ continue
256
+
257
+ if index == unreleased_end :
257
258
skip = False
258
259
if (
259
260
latest_version_position is None
@@ -268,13 +269,15 @@ def incremental_build(
268
269
269
270
if index == latest_version_position :
270
271
output_lines .extend ([new_content , "\n " ])
271
-
272
272
output_lines .append (line )
273
- if not isinstance (latest_version_position , int ):
274
- if output_lines and output_lines [- 1 ].strip ():
275
- # Ensure at least one blank line between existing and new content.
276
- output_lines .append ("\n " )
277
- output_lines .append (new_content )
273
+
274
+ if isinstance (latest_version_position , int ):
275
+ return output_lines
276
+
277
+ if output_lines and output_lines [- 1 ].strip ():
278
+ # Ensure at least one blank line between existing and new content.
279
+ output_lines .append ("\n " )
280
+ output_lines .append (new_content )
278
281
return output_lines
279
282
280
283
@@ -285,23 +288,26 @@ def get_smart_tag_range(
285
288
286
289
This is because we need to find until the next tag
287
290
"""
288
- accumulator = []
289
- keep = False
290
291
if not oldest :
291
292
oldest = newest
292
- for index , tag in enumerate (tags ):
293
- if tag .name == newest :
294
- keep = True
295
- if keep :
296
- accumulator .append (tag )
297
- if tag .name == oldest :
298
- keep = False
299
- try :
300
- accumulator .append (tags [index + 1 ])
301
- except IndexError :
302
- pass
303
- break
304
- return accumulator
293
+
294
+ # Find indices of newest and oldest tags
295
+ try :
296
+ newest_idx = next ((i for i , tag in enumerate (tags ) if tag .name == newest ))
297
+ oldest_idx = newest_idx + (
298
+ next (
299
+ i
300
+ for i , tag in enumerate (islice (tags , newest_idx , None ))
301
+ if tag .name == oldest
302
+ )
303
+ )
304
+ except StopIteration :
305
+ # Should not reach here
306
+ raise NoCommitsFoundError (f"Could not find the tags { newest = } and { oldest = } ." )
307
+
308
+ # Get the range of tags including the next tag after oldest if it exists
309
+ end_idx = min (oldest_idx + 2 , len (tags ))
310
+ return tags [newest_idx :end_idx ]
305
311
306
312
307
313
def get_oldest_and_newest_rev (
@@ -337,17 +343,17 @@ def get_oldest_and_newest_rev(
337
343
if not tags_range :
338
344
raise NoCommitsFoundError ("Could not find a valid revision range." )
339
345
340
- oldest_rev : str | None = tags_range [- 1 ].name
346
+ oldest_rev = tags_range [- 1 ].name
341
347
newest_rev = newest_tag .name
342
348
343
- # check if it's the first tag created
344
- # and it's also being requested as part of the range
345
- if oldest_rev == tags [ - 1 ]. name and oldest_rev == oldest_tag_name :
346
- return None , newest_rev
347
-
348
- # when they are the same, and it's also the
349
- # first tag created
350
- if oldest_rev == newest_rev :
349
+ # Return None for oldest_rev if:
350
+ # 1. The oldest tag is the last tag in the list and matches the requested oldest tag, or
351
+ # 2. The oldest and newest tags are the same
352
+ if (
353
+ oldest_rev == tags [ - 1 ]. name
354
+ and oldest_rev == oldest_tag_name
355
+ or oldest_rev == newest_rev
356
+ ) :
351
357
return None , newest_rev
352
358
353
359
return oldest_rev , newest_rev
0 commit comments