3232from collections .abc import Iterable
3333from dataclasses import dataclass
3434from datetime import date
35+ from itertools import islice
3536from typing import TYPE_CHECKING
3637
3738from jinja2 import (
@@ -88,24 +89,21 @@ def generate_tree_from_commits(
8889 pat = re .compile (changelog_pattern )
8990 map_pat = re .compile (commit_parser , re .MULTILINE )
9091 body_map_pat = re .compile (commit_parser , re .MULTILINE | re .DOTALL )
91- current_tag : GitTag | None = None
9292 rules = rules or TagRules ()
9393
9494 # 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+ )
103101 if current_tag is not None and current_tag .name :
104102 current_tag_name = current_tag .name
105103 current_tag_date = current_tag .date
106104
107- changes : dict = defaultdict (list )
108- used_tags : list = [current_tag ]
105+ changes : defaultdict [ str | None , list ] = defaultdict (list )
106+ used_tags = [current_tag ]
109107 for commit in commits :
110108 commit_tag = get_commit_tag (commit , tags )
111109
@@ -170,21 +168,23 @@ def process_commit_message(
170168 changes : dict [str | None , list ],
171169 change_type_map : dict [str , str ] | None = None ,
172170):
173- message : dict = {
171+ message = {
174172 "sha1" : commit .rev ,
175173 "parents" : commit .parents ,
176174 "author" : commit .author ,
177175 "author_email" : commit .author_email ,
178176 ** parsed .groupdict (),
179177 }
178+ processed = hook (message , commit ) if hook else message
179+ if not processed :
180+ return
180181
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 )
188188
189189
190190def order_changelog_tree (tree : Iterable , change_type_order : list [str ]) -> Iterable :
@@ -225,8 +225,7 @@ def render_changelog(
225225 ** kwargs ,
226226) -> str :
227227 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 )
230229
231230
232231def incremental_build (
@@ -253,7 +252,9 @@ def incremental_build(
253252 for index , line in enumerate (lines ):
254253 if index == unreleased_start :
255254 skip = True
256- elif index == unreleased_end :
255+ continue
256+
257+ if index == unreleased_end :
257258 skip = False
258259 if (
259260 latest_version_position is None
@@ -268,13 +269,15 @@ def incremental_build(
268269
269270 if index == latest_version_position :
270271 output_lines .extend ([new_content , "\n " ])
271-
272272 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 )
278281 return output_lines
279282
280283
@@ -285,23 +288,26 @@ def get_smart_tag_range(
285288
286289 This is because we need to find until the next tag
287290 """
288- accumulator = []
289- keep = False
290291 if not oldest :
291292 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 ]
305311
306312
307313def get_oldest_and_newest_rev (
@@ -337,17 +343,17 @@ def get_oldest_and_newest_rev(
337343 if not tags_range :
338344 raise NoCommitsFoundError ("Could not find a valid revision range." )
339345
340- oldest_rev : str | None = tags_range [- 1 ].name
346+ oldest_rev = tags_range [- 1 ].name
341347 newest_rev = newest_tag .name
342348
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+ ) :
351357 return None , newest_rev
352358
353359 return oldest_rev , newest_rev
0 commit comments