python-semantic-release 9.15.2__py3-none-any.whl → 9.16.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-semantic-release
3
- Version: 9.15.2
3
+ Version: 9.16.1
4
4
  Summary: Automatic Semantic Versioning for Python projects
5
5
  Author-email: Rolf Erik Lekang <me@rolflekang.com>
6
6
  License: MIT
@@ -45,7 +45,7 @@ Requires-Dist: sphinxcontrib-apidoc==0.5.0; extra == "docs"
45
45
  Requires-Dist: sphinx-autobuild==2024.2.4; extra == "docs"
46
46
  Requires-Dist: furo~=2024.1; extra == "docs"
47
47
  Provides-Extra: mypy
48
- Requires-Dist: mypy==1.13.0; extra == "mypy"
48
+ Requires-Dist: mypy==1.14.1; extra == "mypy"
49
49
  Requires-Dist: types-requests~=2.32.0; extra == "mypy"
50
50
  Requires-Dist: types-pyyaml~=6.0; extra == "mypy"
51
51
  Provides-Extra: test
@@ -1,11 +1,11 @@
1
- semantic_release/__init__.py,sha256=PaDXUxHfhYwLilGM9c0vX8F6Jooc8hMhPm3CL8UIX0M,1229
1
+ semantic_release/__init__.py,sha256=Uuw6q8uKl2tHg0H9Nmlkb_MgTX6uXnS7ttKTo4pyNc0,1229
2
2
  semantic_release/__main__.py,sha256=KOIBOvLruqfi5ArXcWK3ucIZ7NB55kfCbycJaxx6aQg,1485
3
3
  semantic_release/const.py,sha256=Z1o2QNh60wSLeF-_1TemMBjU3ZXbV0XghnUFsbTVfOs,831
4
4
  semantic_release/enums.py,sha256=vrEw1UNRcNrFjPqOFnuUzfeoqKj0ChixVVlyk5fqbng,1744
5
5
  semantic_release/errors.py,sha256=PY9rmviSFBZkqawW6VXbUfmF9C_RNOIObcmeGxLefMo,2904
6
6
  semantic_release/gitproject.py,sha256=G4XrucN-ZwT1Kj4RMrABcr1vWb0bjKgurEeJjcL-61c,9422
7
7
  semantic_release/globals.py,sha256=imI9WKGa6MS2pTRAZiWZ2qIJup2eWnBz3OZmIj2YIHM,158
8
- semantic_release/helpers.py,sha256=d1jOX0SNyqPc_3wr14xR25FfpqhMd4Ev7MNBOWlScc0,5581
8
+ semantic_release/helpers.py,sha256=nNm4Pof2nr6D6lixs9CWnwN4bEXCDGgctsT2fNE79hA,7066
9
9
  semantic_release/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
11
11
  semantic_release/changelog/context.py,sha256=jyVluJq8Vu6TyyzQQrsBIQRKm7kEnh1GZt8ObwibR5k,5374
@@ -14,7 +14,7 @@ semantic_release/changelog/template.py,sha256=O4EKXVJtN1z6FowcRUiZdZmi9u_TsTiXcH
14
14
  semantic_release/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  semantic_release/cli/changelog_writer.py,sha256=RupwYqApOeAMidIvjCttnyyGNRxtLftmxDBmEu5azX0,9049
16
16
  semantic_release/cli/cli_context.py,sha256=Nop71LdVCJOeSUHgTXunMyK3xAu_QKQC2cRp1QBVkX0,4134
17
- semantic_release/cli/config.py,sha256=kb8r20PJ-oEJIS69zOQotbHcDpppsRuqwuazA7gQl0Y,30976
17
+ semantic_release/cli/config.py,sha256=TnvHDaqMbNp2hWeRyi-o3UbZ1_mH766RL76N_ca2Puc,32073
18
18
  semantic_release/cli/const.py,sha256=h7XE2D0D__TAZSrUUtVszwvzpkHTMOiQCf97XQNbEvA,163
19
19
  semantic_release/cli/github_actions_output.py,sha256=6oNwjnQBg9XF5QgGc4TgbwX_-W0aj65VwGSL4ALvqVg,2296
20
20
  semantic_release/cli/masking_filter.py,sha256=DxqjiJyABlzwwwZ1r8JGQpb6QrF00StJFm0-2-s5Fv0,3071
@@ -24,7 +24,7 @@ semantic_release/cli/commands/changelog.py,sha256=w_sXuFJHIqcueBQdeNeWn6fqbLVPPl
24
24
  semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
25
25
  semantic_release/cli/commands/main.py,sha256=237rn_Od4LOWfjUjiUKI_jSV820MfcCtRpwPjxjLbyU,4312
26
26
  semantic_release/cli/commands/publish.py,sha256=y_LalPti_kZeQJzl2CR2pTZUK8DCMvNSTe4NaMC5TJA,2875
27
- semantic_release/cli/commands/version.py,sha256=vC4myixs_w8UPj58ZqJmmochl8RWUBmCbseoOEnVmD8,24220
27
+ semantic_release/cli/commands/version.py,sha256=ztNoAUVFjXE2bjrrKXB1EUIOpwRPpnfR_zYdtnp_uQQ,24813
28
28
  semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
29
29
  semantic_release/commit_parser/_base.py,sha256=LAscBtS3_28jebRCeR-eGo3UtAsuxCWBzgb7FF4n4Vo,3046
30
30
  semantic_release/commit_parser/angular.py,sha256=zDYYOK1itsYJ0Ar7-cf29MnfrEpbQTeQCAcExWPH1fM,10486
@@ -62,14 +62,14 @@ semantic_release/hvcs/remote_hvcs_base.py,sha256=cV8qYHtP47bmfIZqV4K2EiMHskFEoIo
62
62
  semantic_release/hvcs/token_auth.py,sha256=ZjT56-NIPB4OKIt1qwHCu1TavXnrWFIBl9ARlg56hgU,663
63
63
  semantic_release/hvcs/util.py,sha256=guxisysY_IW5tv7aaV-iVPEVJzgbOs375kiRRpSquTI,2879
64
64
  semantic_release/version/__init__.py,sha256=CLhtGQry9dLIij5XyRa9ZevxU_1p8tjMTSQ-K_GMpWM,270
65
- semantic_release/version/algorithm.py,sha256=UiJch0cpMxbuL-K_yaah8VF3PVy8mdwj1K8hArcwvdo,15183
65
+ semantic_release/version/algorithm.py,sha256=0cj5LqT8DpBr2zo7Va0-VP8t6a3xAIhFiTLb11xdGpo,15246
66
66
  semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
67
67
  semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
68
68
  semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
69
- python_semantic_release-9.15.2.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
70
- python_semantic_release-9.15.2.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
71
- python_semantic_release-9.15.2.dist-info/METADATA,sha256=yD3qvMg3m175Z40HU3Gj5P8Z7z9Z8q4jhcwX3KS3JNM,3812
72
- python_semantic_release-9.15.2.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
73
- python_semantic_release-9.15.2.dist-info/entry_points.txt,sha256=r2Jql3GTQyugQnvf34l2eXk1O_Qx6llR_xixG1ZWgD0,105
74
- python_semantic_release-9.15.2.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
75
- python_semantic_release-9.15.2.dist-info/RECORD,,
69
+ python_semantic_release-9.16.1.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
70
+ python_semantic_release-9.16.1.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
71
+ python_semantic_release-9.16.1.dist-info/METADATA,sha256=OocBkzse3jhj4CLVaGPr7y4VwkliK9NZP8xVCluIrxY,3812
72
+ python_semantic_release-9.16.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
73
+ python_semantic_release-9.16.1.dist-info/entry_points.txt,sha256=r2Jql3GTQyugQnvf34l2eXk1O_Qx6llR_xixG1ZWgD0,105
74
+ python_semantic_release-9.16.1.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
75
+ python_semantic_release-9.16.1.dist-info/RECORD,,
@@ -24,7 +24,7 @@ from semantic_release.version import (
24
24
  tags_and_versions,
25
25
  )
26
26
 
27
- __version__ = "9.15.2"
27
+ __version__ = "9.16.1"
28
28
 
29
29
  __all__ = [
30
30
  "CommitParser",
@@ -26,6 +26,7 @@ from semantic_release.enums import LevelBump
26
26
  from semantic_release.errors import (
27
27
  BuildDistributionsError,
28
28
  GitCommitEmptyIndexError,
29
+ InternalError,
29
30
  UnexpectedResponse,
30
31
  )
31
32
  from semantic_release.gitproject import GitProject
@@ -35,7 +36,6 @@ from semantic_release.version.algorithm import (
35
36
  tags_and_versions,
36
37
  )
37
38
  from semantic_release.version.translator import VersionTranslator
38
- from semantic_release.version.version import Version
39
39
 
40
40
  if TYPE_CHECKING: # pragma: no cover
41
41
  from pathlib import Path
@@ -45,6 +45,7 @@ if TYPE_CHECKING: # pragma: no cover
45
45
 
46
46
  from semantic_release.cli.cli_context import CliContextObj
47
47
  from semantic_release.version.declaration import VersionDeclarationABC
48
+ from semantic_release.version.version import Version
48
49
 
49
50
 
50
51
  log = logging.getLogger(__name__)
@@ -90,7 +91,18 @@ def version_from_forced_level(
90
91
 
91
92
  # If we have no tags, return the default version
92
93
  if not ts_and_vs:
93
- return Version.parse(DEFAULT_VERSION).bump(forced_level_bump)
94
+ # Since the translator is configured by the user, we can't guarantee that it will
95
+ # be able to parse the default version. So we first cast it to a tag using the default
96
+ # value and the users configured tag format, then parse it back to a version object
97
+ default_initial_version = translator.from_tag(
98
+ translator.str_to_tag(DEFAULT_VERSION)
99
+ )
100
+ if default_initial_version is None:
101
+ # This should never happen, but if it does, it's a bug
102
+ raise InternalError(
103
+ "Translator was unable to parse the embedded default version"
104
+ )
105
+ return default_initial_version.bump(forced_level_bump)
94
106
 
95
107
  _, latest_version = ts_and_vs[0]
96
108
  if forced_level_bump is not LevelBump.PRERELEASE_REVISION:
@@ -516,12 +528,8 @@ def version( # noqa: C901
516
528
  gha_output.version = new_version
517
529
  ctx.call_on_close(gha_output.write_if_possible)
518
530
 
519
- # Make string variant of version && Translate to tag if necessary
520
- version_to_print = (
521
- str(new_version)
522
- if not print_only_tag
523
- else translator.str_to_tag(str(new_version))
524
- )
531
+ # Make string variant of version or appropriate tag as necessary
532
+ version_to_print = str(new_version) if not print_only_tag else new_version.as_tag()
525
533
 
526
534
  # Print the new version so that command-line output capture will work
527
535
  click.echo(version_to_print)
@@ -2,11 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  import os
5
- import re
6
5
  from collections.abc import Mapping
7
6
  from dataclasses import dataclass, is_dataclass
8
7
  from enum import Enum
8
+ from functools import reduce
9
9
  from pathlib import Path
10
+ from re import (
11
+ Pattern,
12
+ compile as regexp,
13
+ error as RegExpError, # noqa: N812
14
+ escape as regex_escape,
15
+ )
10
16
  from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, Union
11
17
 
12
18
  from git import Actor, InvalidGitRepositoryError
@@ -157,6 +163,20 @@ class ChangelogConfig(BaseModel):
157
163
  insertion_flag: str = ""
158
164
  template_dir: str = "templates"
159
165
 
166
+ @field_validator("exclude_commit_patterns", mode="after")
167
+ @classmethod
168
+ def validate_match(cls, patterns: Tuple[str, ...]) -> Tuple[str, ...]:
169
+ curr_index = 0
170
+ try:
171
+ for i, pattern in enumerate(patterns):
172
+ curr_index = i
173
+ regexp(pattern)
174
+ except RegExpError as err:
175
+ raise ValueError(
176
+ f"exclude_commit_patterns[{curr_index}]: Invalid regular expression"
177
+ ) from err
178
+ return patterns
179
+
160
180
  @field_validator("changelog_file", mode="after")
161
181
  @classmethod
162
182
  def changelog_file_deprecation_warning(cls, val: str) -> str:
@@ -228,8 +248,8 @@ class BranchConfig(BaseModel):
228
248
  return ".*"
229
249
 
230
250
  try:
231
- re.compile(match)
232
- except re.error as err:
251
+ regexp(match)
252
+ except RegExpError as err:
233
253
  raise ValueError(f"Invalid regex {match!r}") from err
234
254
  return match
235
255
 
@@ -513,7 +533,7 @@ class RuntimeContext:
513
533
  assets: List[str]
514
534
  commit_author: Actor
515
535
  commit_message: str
516
- changelog_excluded_commit_patterns: Tuple[re.Pattern[str], ...]
536
+ changelog_excluded_commit_patterns: Tuple[Pattern[str], ...]
517
537
  version_declarations: Tuple[VersionDeclarationABC, ...]
518
538
  hvcs_client: hvcs.HvcsBase
519
539
  changelog_insertion_flag: str
@@ -545,7 +565,7 @@ class RuntimeContext:
545
565
  choices: Dict[str, BranchConfig], active_branch: str
546
566
  ) -> BranchConfig:
547
567
  for group, options in choices.items():
548
- if re.match(options.match, active_branch):
568
+ if regexp(options.match).match(active_branch):
549
569
  log.info(
550
570
  "Using group %r options, as %r matches %r",
551
571
  group,
@@ -639,12 +659,21 @@ class RuntimeContext:
639
659
 
640
660
  # We always exclude PSR's own release commits from the Changelog
641
661
  # when parsing commits
642
- _psr_release_commit_re = re.compile(
643
- raw.commit_message.replace(r"{version}", r"(?P<version>.*)")
662
+ psr_release_commit_regex = regexp(
663
+ reduce(
664
+ lambda regex_str, pattern: str(regex_str).replace(*pattern),
665
+ (
666
+ # replace the version holder with a regex pattern to match various versions
667
+ (regex_escape("{version}"), r"(?P<version>\d+\.\d+\.\d+\S*)"),
668
+ # TODO: add any other placeholders here
669
+ ),
670
+ # We use re.escape to ensure that the commit message is treated as a literal
671
+ regex_escape(raw.commit_message),
672
+ )
644
673
  )
645
674
  changelog_excluded_commit_patterns = (
646
- _psr_release_commit_re,
647
- *(re.compile(pattern) for pattern in raw.changelog.exclude_commit_patterns),
675
+ psr_release_commit_regex,
676
+ *(regexp(pattern) for pattern in raw.changelog.exclude_commit_patterns),
648
677
  )
649
678
 
650
679
  _commit_author_str = cls.resolve_from_env(raw.commit_author) or ""
@@ -1,9 +1,11 @@
1
- import importlib
1
+ import importlib.util
2
2
  import logging
3
+ import os
3
4
  import re
4
5
  import string
6
+ import sys
5
7
  from functools import lru_cache, wraps
6
- from pathlib import PurePosixPath
8
+ from pathlib import Path, PurePosixPath
7
9
  from typing import Any, Callable, NamedTuple, TypeVar
8
10
  from urllib.parse import urlsplit
9
11
 
@@ -67,10 +69,47 @@ def dynamic_import(import_path: str) -> Any:
67
69
  Dynamically import an object from a conventionally formatted "module:attribute"
68
70
  string
69
71
  """
70
- log.debug("Trying to import %s", import_path)
71
72
  module_name, attr = import_path.split(":", maxsplit=1)
72
- module = importlib.import_module(module_name)
73
- return getattr(module, attr)
73
+
74
+ # Check if the module is a file path, if it can be resolved and exists on disk then import as a file
75
+ module_filepath = Path(module_name).resolve()
76
+ if module_filepath.exists():
77
+ module_path = (
78
+ module_filepath.stem
79
+ if Path(module_name).is_absolute()
80
+ else str(Path(module_name).with_suffix("")).replace(os.sep, ".").lstrip(".")
81
+ )
82
+
83
+ if module_path not in sys.modules:
84
+ log.debug("Loading '%s' from file '%s'", module_path, module_filepath)
85
+ spec = importlib.util.spec_from_file_location(
86
+ module_path, str(module_filepath)
87
+ )
88
+ if spec is None:
89
+ raise ImportError(f"Could not import {module_filepath}")
90
+
91
+ module = importlib.util.module_from_spec(spec) # type: ignore[arg-type]
92
+ sys.modules.update({spec.name: module})
93
+ spec.loader.exec_module(module) # type: ignore[union-attr]
94
+
95
+ return getattr(sys.modules[module_path], attr)
96
+
97
+ # Otherwise, import as a module
98
+ try:
99
+ log.debug("Importing module '%s'", module_name)
100
+ module = importlib.import_module(module_name)
101
+ log.debug("Loading '%s' from module '%s'", attr, module_name)
102
+ return getattr(module, attr)
103
+ except TypeError as err:
104
+ raise ImportError(
105
+ str.join(
106
+ "\n",
107
+ [
108
+ str(err.args[0]),
109
+ "Verify the import format matches 'module:attribute' or 'path/to/module:attribute'",
110
+ ],
111
+ )
112
+ ) from err
74
113
 
75
114
 
76
115
  class ParsedGitUrl(NamedTuple):
@@ -270,6 +270,7 @@ def next_version(
270
270
  translator.str_to_tag(DEFAULT_VERSION)
271
271
  )
272
272
  if default_initial_version is None:
273
+ # This should never happen, but if it does, it's a bug
273
274
  raise InternalError(
274
275
  "Translator was unable to parse the embedded default version"
275
276
  )