python-kacl 0.7.4__tar.gz → 0.7.6__tar.gz

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.
Files changed (33) hide show
  1. {python_kacl-0.7.4/python_kacl.egg-info → python_kacl-0.7.6}/PKG-INFO +1 -1
  2. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/__init__.py +1 -1
  3. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/document.py +23 -14
  4. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/kacl_cli.py +7 -2
  5. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/version.py +34 -4
  6. {python_kacl-0.7.4 → python_kacl-0.7.6}/pyproject.toml +1 -1
  7. {python_kacl-0.7.4 → python_kacl-0.7.6/python_kacl.egg-info}/PKG-INFO +1 -1
  8. {python_kacl-0.7.4 → python_kacl-0.7.6}/tests/test_cli_workflow.py +41 -0
  9. {python_kacl-0.7.4 → python_kacl-0.7.6}/tests/test_kacl.py +23 -0
  10. {python_kacl-0.7.4 → python_kacl-0.7.6}/LICENSE +0 -0
  11. {python_kacl-0.7.4 → python_kacl-0.7.6}/MANIFEST.in +0 -0
  12. {python_kacl-0.7.4 → python_kacl-0.7.6}/README.md +0 -0
  13. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/__main__.py +0 -0
  14. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/changes.py +0 -0
  15. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/config/kacl-default.yml +0 -0
  16. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/config.py +0 -0
  17. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/element.py +0 -0
  18. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/exception.py +0 -0
  19. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/jira_client.py +0 -0
  20. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/link_provider.py +0 -0
  21. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/parser.py +0 -0
  22. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/serializer.py +0 -0
  23. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/utils.py +0 -0
  24. {python_kacl-0.7.4 → python_kacl-0.7.6}/kacl/validation.py +0 -0
  25. {python_kacl-0.7.4 → python_kacl-0.7.6}/python_kacl.egg-info/SOURCES.txt +0 -0
  26. {python_kacl-0.7.4 → python_kacl-0.7.6}/python_kacl.egg-info/dependency_links.txt +0 -0
  27. {python_kacl-0.7.4 → python_kacl-0.7.6}/python_kacl.egg-info/entry_points.txt +0 -0
  28. {python_kacl-0.7.4 → python_kacl-0.7.6}/python_kacl.egg-info/not-zip-safe +0 -0
  29. {python_kacl-0.7.4 → python_kacl-0.7.6}/python_kacl.egg-info/requires.txt +0 -0
  30. {python_kacl-0.7.4 → python_kacl-0.7.6}/python_kacl.egg-info/top_level.txt +0 -0
  31. {python_kacl-0.7.4 → python_kacl-0.7.6}/requirements.txt +0 -0
  32. {python_kacl-0.7.4 → python_kacl-0.7.6}/setup.cfg +0 -0
  33. {python_kacl-0.7.4 → python_kacl-0.7.6}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-kacl
3
- Version: 0.7.4
3
+ Version: 0.7.6
4
4
  Summary: Python module and CLI tool for validating and modifying Changelogs in "keep-a-changelog" format"
5
5
  Author-email: Matthias Schmieder <schmieder.matthias@gmail.com>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  # Version of the python-kacl package
2
- __version__ = "0.7.4"
2
+ __version__ = "0.7.6"
3
3
 
4
4
  from kacl.document import KACLDocument
5
5
  from kacl.serializer import KACLMarkdownSerializer
@@ -74,20 +74,29 @@ class KACLDocument:
74
74
  if not os.path.isdir(local_stash_dir):
75
75
  return
76
76
 
77
- # read all md files in the stash directory
78
- for f in os.listdir(local_stash_dir):
79
- if f.endswith(".md"):
80
- md_file = os.path.join(local_stash_dir, f)
81
- with open(md_file, "r") as fh:
82
- data = fh.read()
83
- try:
84
- doc = KACLDocument.parse(data, None)
85
- # stash documents must be validated against the same
86
- # config as the changelog they belong to
87
- doc.config = self.config
88
- self.stash.append(doc)
89
- except Exception:
90
- continue
77
+ # read all md files in the stash directory (including legacy
78
+ # sub-directories created by branch names containing a slash)
79
+ for md_file in self.stash_files():
80
+ with open(md_file, "r") as fh:
81
+ data = fh.read()
82
+ try:
83
+ doc = KACLDocument.parse(data, None)
84
+ # stash documents must be validated against the same
85
+ # config as the changelog they belong to
86
+ doc.config = self.config
87
+ self.stash.append(doc)
88
+ except Exception:
89
+ continue
90
+
91
+ def stash_files(self):
92
+ """Returns the paths of all stash files, including nested ones."""
93
+ stash_dir = self._ensure_stash_directory()
94
+ files = []
95
+ for root, _, names in os.walk(stash_dir):
96
+ for name in sorted(names):
97
+ if name.endswith(".md"):
98
+ files.append(os.path.join(root, name))
99
+ return sorted(files)
91
100
 
92
101
  def _ensure_stash_directory(self):
93
102
  # the stash needs always to be relative to the current file_path
@@ -545,11 +545,16 @@ def release(
545
545
 
546
546
  # delete all file in the stash directory
547
547
  stash_files = []
548
- for f in os.listdir(kacl_changelog.stash_directory):
549
- stash_file = os.path.join(kacl_changelog.stash_directory, f)
548
+ for stash_file in kacl_changelog.stash_files():
550
549
  os.remove(stash_file)
551
550
  stash_files.append(stash_file)
552
551
 
552
+ # remove sub-directories left empty (legacy branch names with slashes)
553
+ stash_dir = kacl_changelog.stash_directory
554
+ for root, _, _ in os.walk(stash_dir, topdown=False):
555
+ if root != stash_dir and not os.listdir(root):
556
+ os.rmdir(root)
557
+
553
558
  if commit or tag or kacl_config.git_create_commit or kacl_config.git_create_tag:
554
559
  vcs_context = {
555
560
  "latest_version": latest_version,
@@ -105,6 +105,24 @@ class KACLVersion(KACLElement):
105
105
  self.__sections[sec.title()] = sec
106
106
  return self.__sections
107
107
 
108
+ def duplicate_sections(self):
109
+ """Returns every section element whose title was already used earlier in this version."""
110
+ if not self.body().strip():
111
+ return []
112
+
113
+ seen = set()
114
+ duplicates = []
115
+ for section in KACLParser.parse_header(
116
+ text=self.body(),
117
+ start_depth=3,
118
+ end_depth=3,
119
+ line_offset=self.line_number(),
120
+ ):
121
+ if section.title() in seen:
122
+ duplicates.append(section)
123
+ seen.add(section.title())
124
+ return duplicates
125
+
108
126
  def changes(self, section):
109
127
  sections = self.sections()
110
128
  if sections and section in sections:
@@ -200,11 +218,23 @@ class KACLVersion(KACLElement):
200
218
  try:
201
219
  sections = self.sections()
202
220
  except ValueError as e:
203
- errors.append(
204
- KACLValidationError(
205
- error_message=str(e), line_number=self.line_number()
221
+ duplicates = self.duplicate_sections()
222
+ if not duplicates:
223
+ errors.append(
224
+ KACLValidationError(
225
+ error_message=str(e), line_number=self.line_number()
226
+ )
227
+ )
228
+ for duplicate in duplicates:
229
+ errors.append(
230
+ KACLValidationError(
231
+ line=duplicate.raw(),
232
+ line_number=duplicate.line_number(),
233
+ error_message=f"Version '{self.version()}' has multiple sections with title '{duplicate.title()}'",
234
+ start_character_pos=0,
235
+ end_character_pos=len(duplicate.raw()),
236
+ )
206
237
  )
207
- )
208
238
  return errors
209
239
 
210
240
  for title, element in sections.items():
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-kacl"
7
- version = "0.7.4"
7
+ version = "0.7.6"
8
8
  description = "Python module and CLI tool for validating and modifying Changelogs in \"keep-a-changelog\" format\""
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-kacl
3
- Version: 0.7.4
3
+ Version: 0.7.6
4
4
  Summary: Python module and CLI tool for validating and modifying Changelogs in "keep-a-changelog" format"
5
5
  Author-email: Matthias Schmieder <schmieder.matthias@gmail.com>
6
6
  License: MIT
@@ -182,3 +182,44 @@ def test_integration_workflow_config(tmp_path, snapshot):
182
182
 
183
183
  assert result.exit_code == 0, result.output
184
184
  snapshot_directory(snapshot=snapshot, directory_path=project_root_path)
185
+
186
+
187
+ def test_release_with_nested_stash_directory(tmp_path):
188
+ runner = CliRunner()
189
+ resources_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data/")
190
+ changelog_file = os.path.join(resources_dir, "CHANGELOG_with_changes.md")
191
+ config = os.path.join(resources_dir, "stash-config.yml")
192
+
193
+ with runner.isolated_filesystem(temp_dir=tmp_path) as project_root_path:
194
+ shutil.copyfile(changelog_file, os.path.join(project_root_path, "CHANGELOG.md"))
195
+
196
+ # stash file for a branch like "refactor/foo" written by older versions
197
+ result = runner.invoke(
198
+ cli,
199
+ [
200
+ "-c",
201
+ config,
202
+ "-f",
203
+ "CHANGELOG.md",
204
+ "add",
205
+ "--stash",
206
+ "-m",
207
+ "Changed",
208
+ "x",
209
+ ],
210
+ catch_exceptions=False,
211
+ )
212
+ assert result.exit_code == 0, result.output
213
+ stash_dir = os.path.join(project_root_path, ".kacl_stash")
214
+ flat = os.path.join(stash_dir, os.listdir(stash_dir)[0])
215
+ nested_dir = os.path.join(stash_dir, "refactor")
216
+ os.makedirs(nested_dir)
217
+ shutil.move(flat, os.path.join(nested_dir, "foo.md"))
218
+
219
+ result = runner.invoke(
220
+ cli,
221
+ ["-c", config, "-f", "CHANGELOG.md", "release", "99.0.0", "-m"],
222
+ catch_exceptions=False,
223
+ )
224
+ assert result.exit_code == 0, result.output
225
+ assert os.listdir(stash_dir) == []
@@ -657,3 +657,26 @@ def test_release_no_unreleased_multiple_versions(tmp_path):
657
657
  second_version_after = versions_after[1]
658
658
  # The link might be updated during auto-link, but shouldn't be broken
659
659
  assert second_version_after.link() is not None
660
+
661
+
662
+ def test_validate_reports_every_duplicated_section():
663
+ changelog = kacl.parse(
664
+ "# Changelog\n\n"
665
+ "All notable changes to this project will be documented in this file.\n\n"
666
+ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), "
667
+ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n"
668
+ "## [Unreleased]\n\n"
669
+ "### Added\n\n- a\n\n"
670
+ "### Changed\n\n- b\n\n"
671
+ "### Added\n\n- a again\n\n"
672
+ "### Changed\n\n- b again\n\n"
673
+ )
674
+ errors = [
675
+ e
676
+ for e in changelog.validate().errors()
677
+ if "multiple sections" in e.error_message()
678
+ ]
679
+ assert [(e.line_number(), e.line()) for e in errors] == [
680
+ (17, "### Added"),
681
+ (21, "### Changed"),
682
+ ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes