ebump 0.2.0__tar.gz → 0.2.2__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.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ebump
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Easy version bumping
5
5
  Author-email: Jorge Morgado Vega <jorge.morgadov@gmail.com>
6
6
  License: MIT
7
- Requires-Python: >=3.13
7
+ Requires-Python: >=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Dynamic: license-file
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "ebump"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Easy version bumping"
5
5
  readme = "README.md"
6
- requires-python = ">= 3.13"
6
+ requires-python = ">= 3.11"
7
7
  authors = [{ name = "Jorge Morgado Vega", email = "jorge.morgadov@gmail.com" }]
8
8
  license = { text = "MIT" }
9
9
 
@@ -37,7 +37,7 @@ ebump = "ebump.main:main"
37
37
  [tool.ruff]
38
38
  line-length = 88
39
39
  indent-width = 4
40
- target-version = "py313"
40
+ target-version = "py311"
41
41
 
42
42
  [tool.ruff.format]
43
43
  quote-style = "double"
@@ -78,7 +78,7 @@ unfixable = []
78
78
 
79
79
 
80
80
  [tool.mypy]
81
- python_version = "3.13"
81
+ python_version = "3.11"
82
82
  files = ["src", "tests"]
83
83
  warn_unused_configs = true
84
84
  warn_redundant_casts = true
@@ -0,0 +1 @@
1
+ __version__ = "0.2.2"
@@ -12,7 +12,10 @@ def bump(curr_ver: Version, part: PartType, tag: TagType | None = None) -> Versi
12
12
  )
13
13
 
14
14
  assert part == PartType.TAG, "Invalid part type for bumping"
15
+ return _bump_tag(curr_ver, tag)
15
16
 
17
+
18
+ def _bump_tag(curr_ver: Version, tag: TagType | None) -> Version:
16
19
  if curr_ver.tag == TagType.FINAL:
17
20
  if tag is not None and tag != TagType.FINAL:
18
21
  raise ValueError(f"Cannot bump to tag {tag._value_} on a final version.")
@@ -10,11 +10,11 @@ from ebump.version import VERSION_RE, TagType, Version
10
10
  class Config:
11
11
  root: Path
12
12
  current_version: Version
13
- re_patterns: dict[Path, list[re.Pattern]]
13
+ re_patterns: dict[Path, list[re.Pattern[str]]]
14
14
 
15
15
 
16
16
  def _ensure_pyproject_pattern(
17
- pyproject_path: Path, re_patterns: dict[Path, list[re.Pattern]]
17
+ pyproject_path: Path, re_patterns: dict[Path, list[re.Pattern[str]]]
18
18
  ) -> None:
19
19
  pyproject_pattern = re.compile(
20
20
  r"^version\s*=\s*\"" + VERSION_RE.pattern + r"\"\s*$"
@@ -52,7 +52,7 @@ def parse_config(root: Path) -> Config:
52
52
  config_patterns = (
53
53
  pyproject_dict.get("tool", {}).get("ebump", {}).get("patterns", {})
54
54
  )
55
- re_patterns: dict[Path, list[re.Pattern]] = {}
55
+ re_patterns: dict[Path, list[re.Pattern[str]]] = {}
56
56
 
57
57
  for str_path, patterns in config_patterns.items():
58
58
  path = (root / Path(str_path)).absolute()
@@ -60,7 +60,7 @@ def parse_config(root: Path) -> Config:
60
60
  raise FileNotFoundError(
61
61
  f"File {path} specified in pyproject.toml does not exist in project root {root.absolute()!s}"
62
62
  )
63
- path_re_patterns: list[re.Pattern] = []
63
+ path_re_patterns: list[re.Pattern[str]] = []
64
64
  for pattern in patterns:
65
65
  if "{version}" not in pattern:
66
66
  raise ValueError(
@@ -24,8 +24,8 @@ TAG_OPTS = {"alpha", "beta", "dev", "rc", "post", "final"}
24
24
  def _show_diff(old_lines: list[str], new_lines: list[str]) -> None:
25
25
  for old_line, new_line in zip(old_lines, new_lines, strict=False):
26
26
  if old_line != new_line:
27
- sys.stdout.write(f"- {old_line}\n")
28
- sys.stdout.write(f"+ {new_line}\n")
27
+ sys.stdout.write(f"- {old_line}")
28
+ sys.stdout.write(f"+ {new_line}")
29
29
 
30
30
 
31
31
  def set_version(cfg: Config, new_version: Version, dry_run: bool) -> None:
@@ -76,11 +76,14 @@ Bad examples:
76
76
  formatter_class=argparse.RawDescriptionHelpFormatter,
77
77
  )
78
78
 
79
+ bump_opts = list(PART_OPTS) + list(TAG_OPTS)
80
+ # Checked after parsing: before Python 3.12, argparse rejects an empty
81
+ # nargs="*" positional when it declares choices.
79
82
  parser.add_argument(
80
83
  "bump",
81
84
  nargs="*",
82
85
  default=None,
83
- choices=list(PART_OPTS) + list(TAG_OPTS),
86
+ metavar="{" + ",".join(bump_opts) + "}",
84
87
  help=f"Version part to bump {PART_OPTS}, and/or specific pre-release tag {TAG_OPTS}",
85
88
  )
86
89
 
@@ -105,6 +108,12 @@ Bad examples:
105
108
  )
106
109
 
107
110
  args = parser.parse_args()
111
+ invalid_opts = [opt for opt in args.bump if opt not in bump_opts]
112
+ if invalid_opts:
113
+ parser.error(
114
+ f"argument bump: invalid choice: {invalid_opts[0]!r} "
115
+ f"(choose from {', '.join(map(repr, bump_opts))})"
116
+ )
108
117
  bump_args = args.bump
109
118
  dry_run = args.dry_run
110
119
 
@@ -11,20 +11,21 @@ RewriteData = NamedTuple(
11
11
  ("path", Path),
12
12
  ("old_lines", list[str]),
13
13
  ("new_lines", list[str]),
14
- ("line_sep", str),
15
14
  ],
16
15
  )
17
16
 
18
17
 
19
18
  def rewrite_lines(
20
- old_lines: list[str], new_version: str, patterns: list[re.Pattern]
19
+ old_lines: list[str], new_version: str, patterns: list[re.Pattern[str]]
21
20
  ) -> list[str]:
22
21
  used_patterns = set()
23
22
  new_lines = old_lines[:]
24
23
  for i, line in enumerate(old_lines):
24
+ stripped = line.rstrip("\r\n")
25
+ ending = line[len(stripped) :]
25
26
  new_line = line
26
27
  for patt_idx, pattern in enumerate(patterns):
27
- match = pattern.match(line)
28
+ match = pattern.match(stripped)
28
29
  if match is None:
29
30
  continue
30
31
 
@@ -38,13 +39,14 @@ def rewrite_lines(
38
39
  start_idx = match.start(group_idx)
39
40
  end_idx = match.end(group_idx)
40
41
  if last_idx < start_idx:
41
- new_line += line[last_idx:start_idx]
42
+ new_line += stripped[last_idx:start_idx]
42
43
 
43
44
  new_line += new_version
44
45
  last_idx = end_idx
45
46
 
46
- if last_idx < len(line):
47
- new_line += line[last_idx:]
47
+ if last_idx < len(stripped):
48
+ new_line += stripped[last_idx:]
49
+ new_line += ending
48
50
  break
49
51
 
50
52
  new_lines[i] = new_line
@@ -60,25 +62,14 @@ def rewrite_lines(
60
62
  return new_lines
61
63
 
62
64
 
63
- def detect_line_sep(content: str) -> str:
64
- """Parse line separator from content."""
65
- if "\r\n" in content:
66
- return "\r\n"
67
- if "\r" in content:
68
- return "\r"
69
- return "\n"
70
-
71
-
72
65
  def rewrite_file(
73
- path: Path, new_version: str, patterns: list[re.Pattern]
66
+ path: Path, new_version: str, patterns: list[re.Pattern[str]]
74
67
  ) -> RewriteData:
75
- content = path.read_text(newline="")
76
- line_sep = detect_line_sep(content)
77
- old_lines = content.split(line_sep)
68
+ with path.open(newline="") as file:
69
+ content = file.read()
70
+ old_lines = content.splitlines(keepends=True)
78
71
  new_lines = rewrite_lines(old_lines, new_version, patterns)
79
- return RewriteData(
80
- path=path, old_lines=old_lines, new_lines=new_lines, line_sep=line_sep
81
- )
72
+ return RewriteData(path=path, old_lines=old_lines, new_lines=new_lines)
82
73
 
83
74
 
84
75
  def rewrite_files(config: Config, new_version: str) -> list[RewriteData]:
@@ -90,5 +81,5 @@ def rewrite_files(config: Config, new_version: str) -> list[RewriteData]:
90
81
 
91
82
  def perform_rewrites(rewrite_data_list: list[RewriteData]) -> None:
92
83
  for rewrite_data in rewrite_data_list:
93
- new_content = rewrite_data.line_sep.join(rewrite_data.new_lines)
94
- rewrite_data.path.write_text(new_content)
84
+ new_content = "".join(rewrite_data.new_lines)
85
+ rewrite_data.path.write_text(new_content, newline="")
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import enum
4
4
  import re
5
- from typing import NamedTuple, cast
5
+ from typing import NamedTuple
6
6
 
7
7
 
8
8
  class PartType(enum.Enum):
@@ -22,9 +22,7 @@ class TagType(enum.Enum):
22
22
 
23
23
  TAGS_ORDER = {tag: i for i, tag in enumerate(TagType)}
24
24
 
25
-
26
25
  VERSION_RE = re.compile(r"(\d+\.\d+\.\d+(?:-(alpha|beta|dev|rc|post)\d*)?)")
27
-
28
26
  VERSION_GROUPS_RE = re.compile(
29
27
  r"(?P<major>\d+)"
30
28
  r"\."
@@ -61,7 +59,7 @@ class Version(NamedTuple):
61
59
  tag = TagType(match.group("tag") or "final")
62
60
  tag_num = int(match.group("tag_num") or 0)
63
61
 
64
- return Version(major, minor, patch, cast(TagType, tag), tag_num)
62
+ return Version(major, minor, patch, tag, tag_num)
65
63
 
66
64
  def __eq__(self, other: object) -> bool:
67
65
  return self._compare(other) == 0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ebump
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Easy version bumping
5
5
  Author-email: Jorge Morgado Vega <jorge.morgadov@gmail.com>
6
6
  License: MIT
7
- Requires-Python: >=3.13
7
+ Requires-Python: >=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Dynamic: license-file
@@ -17,4 +17,5 @@ src/ebump.egg-info/top_level.txt
17
17
  tests/test_cli.py
18
18
  tests/test_config.py
19
19
  tests/test_patterns.py
20
+ tests/test_rewrite.py
20
21
  tests/test_version.py
@@ -2,6 +2,8 @@
2
2
  Tests for ebump CLI.
3
3
  """
4
4
 
5
+ from pathlib import Path
6
+
5
7
  from conftest import make_pyproject, read_version, run_ebump
6
8
 
7
9
  # ---------------------------------------------------------------------------
@@ -10,12 +12,12 @@ from conftest import make_pyproject, read_version, run_ebump
10
12
 
11
13
 
12
14
  class TestShowVersion:
13
- def test_shows_current_version(self, project):
15
+ def test_shows_current_version(self, project: Path) -> None:
14
16
  result = run_ebump([], project)
15
17
  assert result.returncode == 0
16
18
  assert "1.0.0" in result.stdout
17
19
 
18
- def test_shows_pre_release_version(self, project):
20
+ def test_shows_pre_release_version(self, project: Path) -> None:
19
21
  make_pyproject("2.3.0-beta1", project)
20
22
  result = run_ebump([], project)
21
23
  assert result.returncode == 0
@@ -28,42 +30,42 @@ class TestShowVersion:
28
30
 
29
31
 
30
32
  class TestBumpPatch:
31
- def test_patch_increments_patch(self, project):
33
+ def test_patch_increments_patch(self, project: Path) -> None:
32
34
  run_ebump(["patch"], project)
33
35
  assert read_version(project) == "1.0.1"
34
36
 
35
- def test_patch_resets_prerelease_tag(self, project):
37
+ def test_patch_resets_prerelease_tag(self, project: Path) -> None:
36
38
  make_pyproject("1.0.0-beta2", project)
37
39
  run_ebump(["patch"], project)
38
40
  assert read_version(project) == "1.0.1"
39
41
 
40
- def test_patch_dry_run_does_not_change(self, project):
42
+ def test_patch_dry_run_does_not_change(self, project: Path) -> None:
41
43
  run_ebump(["patch", "--dry-run"], project)
42
44
  assert read_version(project) == "1.0.0"
43
45
 
44
46
 
45
47
  class TestBumpMinor:
46
- def test_minor_increments_minor_resets_patch(self, project):
48
+ def test_minor_increments_minor_resets_patch(self, project: Path) -> None:
47
49
  make_pyproject("1.2.3", project)
48
50
  run_ebump(["minor"], project)
49
51
  assert read_version(project) == "1.3.0"
50
52
 
51
- def test_minor_with_beta_tag(self, project):
53
+ def test_minor_with_beta_tag(self, project: Path) -> None:
52
54
  run_ebump(["minor", "beta"], project)
53
55
  assert read_version(project) == "1.1.0-beta0"
54
56
 
55
- def test_minor_dry_run(self, project):
57
+ def test_minor_dry_run(self, project: Path) -> None:
56
58
  run_ebump(["minor", "--dry-run"], project)
57
59
  assert read_version(project) == "1.0.0"
58
60
 
59
61
 
60
62
  class TestBumpMajor:
61
- def test_major_increments_major_resets_rest(self, project):
63
+ def test_major_increments_major_resets_rest(self, project: Path) -> None:
62
64
  make_pyproject("1.5.4", project)
63
65
  run_ebump(["major"], project)
64
66
  assert read_version(project) == "2.0.0"
65
67
 
66
- def test_major_with_alpha_tag(self, project):
68
+ def test_major_with_alpha_tag(self, project: Path) -> None:
67
69
  run_ebump(["major", "alpha"], project)
68
70
  assert read_version(project) == "2.0.0-alpha0"
69
71
 
@@ -74,45 +76,45 @@ class TestBumpMajor:
74
76
 
75
77
 
76
78
  class TestBumpTag:
77
- def test_tag_increments_existing_tag_number(self, project):
79
+ def test_tag_increments_existing_tag_number(self, project: Path) -> None:
78
80
  make_pyproject("1.0.0-beta1", project)
79
81
  run_ebump(["tag"], project)
80
82
  assert read_version(project) == "1.0.0-beta2"
81
83
 
82
- def test_tag_with_no_tag_exits_error(self, project):
84
+ def test_tag_with_no_tag_exits_error(self, project: Path) -> None:
83
85
  assert read_version(project) == "1.0.0"
84
86
  result = run_ebump(["tag"], project)
85
87
  assert result.returncode != 0
86
88
  assert "Cannot bump tag number on a final version" in result.stderr
87
89
 
88
- def test_tag_beta_when_at_alpha_promotes_to_beta(self, project):
90
+ def test_tag_beta_when_at_alpha_promotes_to_beta(self, project: Path) -> None:
89
91
  make_pyproject("1.0.0-alpha3", project)
90
92
  run_ebump(["tag", "beta"], project)
91
93
  assert read_version(project) == "1.0.0-beta0"
92
94
 
93
- def test_tag_beta_when_at_beta_increments(self, project):
95
+ def test_tag_beta_when_at_beta_increments(self, project: Path) -> None:
94
96
  make_pyproject("1.0.0-beta0", project)
95
97
  run_ebump(["tag", "beta"], project)
96
98
  assert read_version(project) == "1.0.0-beta1"
97
99
 
98
100
 
99
101
  class TestTagShorthand:
100
- def test_alpha_when_already_alpha_increments(self, project):
102
+ def test_alpha_when_already_alpha_increments(self, project: Path) -> None:
101
103
  make_pyproject("1.0.0-alpha4", project)
102
104
  run_ebump(["alpha"], project)
103
105
  assert read_version(project) == "1.0.0-alpha5"
104
106
 
105
- def test_beta_when_at_alpha_promotes(self, project):
107
+ def test_beta_when_at_alpha_promotes(self, project: Path) -> None:
106
108
  make_pyproject("1.0.0-alpha5", project)
107
109
  run_ebump(["beta"], project)
108
110
  assert read_version(project) == "1.0.0-beta0"
109
111
 
110
- def test_final_from_rc_removes_tag(self, project):
112
+ def test_final_from_rc_removes_tag(self, project: Path) -> None:
111
113
  make_pyproject("1.0.0-rc2", project)
112
114
  run_ebump(["final"], project)
113
115
  assert read_version(project) == "1.0.0"
114
116
 
115
- def test_final_when_already_final_is_noop(self, project):
117
+ def test_final_when_already_final_is_noop(self, project: Path) -> None:
116
118
  result = run_ebump(["final"], project)
117
119
  assert result.returncode == 0
118
120
  assert read_version(project) == "1.0.0"
@@ -125,84 +127,61 @@ class TestTagShorthand:
125
127
 
126
128
 
127
129
  class TestErrorCases:
128
- def test_bump_to_tag_on_final_is_error(self, project):
130
+ def test_bump_to_tag_on_final_is_error(self, project: Path) -> None:
129
131
  make_pyproject("1.0.0", project)
130
132
  result = run_ebump(["beta"], project)
131
133
  assert result.returncode != 0
132
134
  assert "Cannot bump to tag beta on a final version" in result.stderr
133
135
 
134
- def test_two_parts_is_an_error(self, project):
136
+ def test_two_parts_is_an_error(self, project: Path) -> None:
135
137
  result = run_ebump(["patch", "minor"], project)
136
138
  assert result.returncode != 0
137
139
 
138
- def test_two_tags_is_an_error(self, project):
140
+ def test_two_tags_is_an_error(self, project: Path) -> None:
139
141
  result = run_ebump(["alpha", "beta"], project)
140
142
  assert result.returncode != 0
141
143
 
142
- def test_three_args_is_an_error(self, project):
144
+ def test_three_args_is_an_error(self, project: Path) -> None:
143
145
  result = run_ebump(["minor", "alpha", "beta"], project)
144
146
  assert result.returncode != 0
145
147
 
146
- def test_missing_config_exits_with_error(self, tmp_path):
148
+ def test_missing_config_exits_with_error(self, tmp_path: Path) -> None:
147
149
  # A directory with no pyproject.toml at all
148
150
  result = run_ebump([], tmp_path)
149
151
  assert result.returncode != 0
150
152
 
151
- def test_invalid_bumping_exits_with_error(self, project):
153
+ def test_invalid_bumping_exits_with_error(self, project: Path) -> None:
152
154
  make_pyproject("1.0.0-beta2", project)
153
155
  result = run_ebump(["alpha"], project)
154
156
  assert result.returncode != 0
155
157
 
156
158
 
157
- class TestRewritePatterns:
158
- def test_pattern_rewrite_same_line_endings(self, project):
159
- make_pyproject(
160
- "1.0.0",
161
- project,
162
- custom_pyproject_content='[project]\r\nname = "test_project"\r\nversion = "1.0.0"\r\n',
163
- )
164
- run_ebump(["patch"], project)
165
- assert (project / "pyproject.toml").read_text(
166
- newline=""
167
- ) == '[project]\r\nname = "test_project"\r\nversion = "1.0.1"\r\n'
168
-
169
- make_pyproject(
170
- "1.0.0",
171
- project,
172
- custom_pyproject_content='[project]\rname = "test_project"\rversion = "1.0.0"\r',
173
- )
174
- run_ebump(["patch"], project)
175
- assert (project / "pyproject.toml").read_text(
176
- newline=""
177
- ) == '[project]\rname = "test_project"\rversion = "1.0.1"\r'
178
-
179
-
180
159
  class TestVersionSet:
181
- def test_version_set(self, project):
160
+ def test_version_set(self, project: Path) -> None:
182
161
  make_pyproject("1.0.0", project)
183
162
  result = run_ebump(["--set", "1.0.1-alpha1"], project)
184
163
  assert result.returncode == 0
185
164
  assert read_version(project) == "1.0.1-alpha1"
186
165
 
187
- def test_version_set_dry_run(self, project):
166
+ def test_version_set_dry_run(self, project: Path) -> None:
188
167
  make_pyproject("1.0.0", project)
189
168
  result = run_ebump(["--set", "1.0.1-alpha1", "--dry-run"], project)
190
169
  assert result.returncode == 0
191
170
  assert read_version(project) == "1.0.0"
192
171
 
193
- def test_version_set_invalid_version(self, project):
172
+ def test_version_set_invalid_version(self, project: Path) -> None:
194
173
  make_pyproject("1.0.0", project)
195
174
  result = run_ebump(["--set", "not_a_version"], project)
196
175
  assert result.returncode != 0
197
176
  assert "Invalid version string: not_a_version" in result.stderr
198
177
 
199
- def test_version_set_forcedly_overwrite(self, project):
178
+ def test_version_set_forcedly_overwrite(self, project: Path) -> None:
200
179
  make_pyproject("3.0.0", project)
201
180
  result = run_ebump(["--set", "2.0.0", "--force"], project)
202
181
  assert result.returncode == 0
203
182
  assert read_version(project) == "2.0.0"
204
183
 
205
- def test_versino_set_invalid_no_force(self, project):
184
+ def test_versino_set_invalid_no_force(self, project: Path) -> None:
206
185
  make_pyproject("3.0.0", project)
207
186
  result = run_ebump(["--set", "2.0.0"], project)
208
187
  assert result.returncode != 0
@@ -1,4 +1,5 @@
1
1
  import textwrap
2
+ from pathlib import Path
2
3
 
3
4
  import pytest
4
5
  from conftest import make_pyproject
@@ -8,12 +9,12 @@ from ebump.version import VERSION_RE
8
9
 
9
10
 
10
11
  class TestConfig:
11
- def test_config_no_patterns(self, project):
12
+ def test_config_no_patterns(self, project: Path) -> None:
12
13
  make_pyproject("1.0.0", project)
13
14
  cfg = parse_config(project)
14
15
  assert cfg.current_version == "1.0.0"
15
16
 
16
- def test_config_with_patterns(self, project):
17
+ def test_config_with_patterns(self, project: Path) -> None:
17
18
  make_pyproject(
18
19
  "2.3.0-beta1",
19
20
  project,
@@ -28,7 +29,7 @@ class TestConfig:
28
29
  assert len(patterns) == 1
29
30
  assert patterns[0].pattern == r"^version = " + VERSION_RE.pattern + "$"
30
31
 
31
- def test_config_no_pyproject_version(self, project):
32
+ def test_config_no_pyproject_version(self, project: Path) -> None:
32
33
  make_pyproject(
33
34
  "",
34
35
  project,
@@ -40,7 +41,7 @@ class TestConfig:
40
41
  with pytest.raises(ValueError, match=r"No version found in pyproject.toml.*"):
41
42
  parse_config(project)
42
43
 
43
- def test_config_invalid_pyproject_version(self, project):
44
+ def test_config_invalid_pyproject_version(self, project: Path) -> None:
44
45
  make_pyproject(
45
46
  "not_a_version",
46
47
  project,
@@ -55,7 +56,7 @@ class TestConfig:
55
56
  ):
56
57
  parse_config(project)
57
58
 
58
- def test_config_pattern_invalid_file(self, project):
59
+ def test_config_pattern_invalid_file(self, project: Path) -> None:
59
60
  make_pyproject(
60
61
  "1.0.0",
61
62
  project,
@@ -1,8 +1,10 @@
1
+ from pathlib import Path
2
+
1
3
  from conftest import make_pyproject, run_ebump
2
4
 
3
5
 
4
6
  class TestPatterns:
5
- def test_pattern_simple(self, project):
7
+ def test_pattern_simple(self, project: Path) -> None:
6
8
  make_pyproject(
7
9
  "1.0.0",
8
10
  project,
@@ -12,7 +14,7 @@ class TestPatterns:
12
14
  run_ebump(["patch"], project)
13
15
  assert (project / "custom_file.md").read_text() == "version = 1.0.1"
14
16
 
15
- def test_pattern_multiple_on_same_line(self, project):
17
+ def test_pattern_multiple_on_same_line(self, project: Path) -> None:
16
18
  make_pyproject(
17
19
  "1.0.0",
18
20
  project,
@@ -22,7 +24,7 @@ class TestPatterns:
22
24
  run_ebump(["patch"], project)
23
25
  assert (project / "custom_file.md").read_text() == "version = 1.0.1 and 1.0.1"
24
26
 
25
- def test_multiple_but_only_change_one(self, project):
27
+ def test_multiple_but_only_change_one(self, project: Path) -> None:
26
28
  make_pyproject(
27
29
  "1.0.0",
28
30
  project,
@@ -34,7 +36,7 @@ class TestPatterns:
34
36
  project / "custom_file.md"
35
37
  ).read_text() == "version = 1.0.1 version = 1.0.0"
36
38
 
37
- def test_pattern_not_found(self, project):
39
+ def test_pattern_not_found(self, project: Path) -> None:
38
40
  make_pyproject(
39
41
  "1.0.0",
40
42
  project,
@@ -45,7 +47,7 @@ class TestPatterns:
45
47
  assert result.returncode != 0
46
48
  assert "Some patterns were not found" in result.stderr
47
49
 
48
- def test_pattern_does_not_contain_placeholder(self, project):
50
+ def test_pattern_does_not_contain_placeholder(self, project: Path) -> None:
49
51
  make_pyproject(
50
52
  "1.0.0",
51
53
  project,
@@ -0,0 +1,95 @@
1
+ from pathlib import Path
2
+
3
+ from conftest import make_pyproject, read_raw_text, run_ebump
4
+
5
+
6
+ class TestRewritePatterns:
7
+ def test_pattern_rewrite_same_line_endings(self, project: Path) -> None:
8
+ make_pyproject(
9
+ "1.0.0",
10
+ project,
11
+ custom_pyproject_content='[project]\r\nname = "test_project"\r\nversion = "1.0.0"\r\n',
12
+ )
13
+ run_ebump(["patch"], project)
14
+ assert (
15
+ read_raw_text(project / "pyproject.toml")
16
+ == '[project]\r\nname = "test_project"\r\nversion = "1.0.1"\r\n'
17
+ )
18
+
19
+ make_pyproject(
20
+ "1.0.0",
21
+ project,
22
+ custom_pyproject_content='[project]\rname = "test_project"\rversion = "1.0.0"\r',
23
+ )
24
+ run_ebump(["patch"], project)
25
+ assert (
26
+ read_raw_text(project / "pyproject.toml")
27
+ == '[project]\rname = "test_project"\rversion = "1.0.1"\r'
28
+ )
29
+
30
+ def test_mixed_line_endings_preserved(self, project: Path) -> None:
31
+ content = '[project]\r\nname = "test_project"\nversion = "1.0.0"\r\n'
32
+ make_pyproject("1.0.0", project, custom_pyproject_content=content)
33
+
34
+ run_ebump(["patch"], project)
35
+
36
+ assert read_raw_text(project / "pyproject.toml") == (
37
+ '[project]\r\nname = "test_project"\nversion = "1.0.1"\r\n'
38
+ )
39
+
40
+ def test_no_trailing_newline(self, project: Path) -> None:
41
+ content = '[project]\nname = "test_project"\nversion = "1.0.0"'
42
+ make_pyproject("1.0.0", project, custom_pyproject_content=content)
43
+
44
+ run_ebump(["patch"], project)
45
+
46
+ # should NOT add a newline
47
+ assert read_raw_text(project / "pyproject.toml") == (
48
+ '[project]\nname = "test_project"\nversion = "1.0.1"'
49
+ )
50
+
51
+ def test_preserve_multiple_trailing_newlines(self, project: Path) -> None:
52
+ content = '[project]\nname = "test_project"\nversion = "1.0.0"\n\n'
53
+ make_pyproject("1.0.0", project, custom_pyproject_content=content)
54
+
55
+ run_ebump(["patch"], project)
56
+
57
+ assert read_raw_text(project / "pyproject.toml") == (
58
+ '[project]\nname = "test_project"\nversion = "1.0.1"\n\n'
59
+ )
60
+
61
+ def test_preserve_blank_lines_between(self, project: Path) -> None:
62
+ content = '[project]\n\nname = "test_project"\n\nversion = "1.0.0"\n'
63
+ make_pyproject("1.0.0", project, custom_pyproject_content=content)
64
+
65
+ run_ebump(["patch"], project)
66
+
67
+ assert read_raw_text(project / "pyproject.toml") == (
68
+ '[project]\n\nname = "test_project"\n\nversion = "1.0.1"\n'
69
+ )
70
+
71
+ def test_crlf_not_doubled(self, project: Path) -> None:
72
+ # regression test for Windows double-spacing bug
73
+ content = '[project]\r\nname = "test_project"\r\nversion = "1.0.0"\r\n'
74
+ make_pyproject("1.0.0", project, custom_pyproject_content=content)
75
+
76
+ run_ebump(["patch"], project)
77
+
78
+ result = read_raw_text(project / "pyproject.toml")
79
+
80
+ # ensure no accidental \r\r\n sequences
81
+ assert "\r\r\n" not in result
82
+ assert result == '[project]\r\nname = "test_project"\r\nversion = "1.0.1"\r\n'
83
+
84
+ def test_multiple_version_occurrences_same_line(self, project: Path) -> None:
85
+ content = '[project]\nversion = "1.0.0"\n# here 1.0.0 also 1.0.0 here\n\n[tool.ebump.patterns]\n"pyproject.toml" = ["^# here {version} also {version} here$"]\n'
86
+ make_pyproject("1.0.0", project, custom_pyproject_content=content)
87
+
88
+ result = run_ebump(["patch"], project)
89
+
90
+ assert result.returncode == 0, (
91
+ "ebump failed with output:\n" + result.stdout + "\n" + result.stderr
92
+ )
93
+ assert read_raw_text(project / "pyproject.toml") == (
94
+ '[project]\nversion = "1.0.1"\n# here 1.0.1 also 1.0.1 here\n\n[tool.ebump.patterns]\n"pyproject.toml" = ["^# here {version} also {version} here$"]\n'
95
+ )
@@ -4,7 +4,7 @@ from ebump.version import TagType, Version
4
4
 
5
5
 
6
6
  class TestVersion:
7
- def test_version_creation(self):
7
+ def test_version_creation(self) -> None:
8
8
  version = Version(1, 2, 3, TagType.BETA, 4)
9
9
  assert version.major == 1
10
10
  assert version.minor == 2
@@ -12,7 +12,7 @@ class TestVersion:
12
12
  assert version.tag == TagType.BETA
13
13
  assert version.tag_num == 4
14
14
 
15
- def test_version_parsing(self):
15
+ def test_version_parsing(self) -> None:
16
16
  version_str = "1.2.3-beta4"
17
17
  version = Version.parse(version_str)
18
18
  assert version.major == 1
@@ -21,7 +21,7 @@ class TestVersion:
21
21
  assert version.tag == TagType.BETA
22
22
  assert version.tag_num == 4
23
23
 
24
- def test_version_comparison(self):
24
+ def test_version_comparison(self) -> None:
25
25
  v0 = Version.parse("0.0.1-alpha0")
26
26
  v1 = Version.parse("0.0.1-beta0")
27
27
  v2 = Version.parse("0.0.1-beta1")
@@ -39,15 +39,15 @@ class TestVersion:
39
39
  assert v2 > v0
40
40
  assert Version.parse("0.0.2") > v3
41
41
 
42
- def test_version_parsing_error(self):
42
+ def test_version_parsing_error(self) -> None:
43
43
  with pytest.raises(ValueError, match=r"Invalid version string: not_a_version"):
44
44
  Version.parse("not_a_version")
45
45
 
46
- def test_version_comp_with_string(self):
46
+ def test_version_comp_with_string(self) -> None:
47
47
  v = Version.parse("1.0.0")
48
48
  assert v == "1.0.0"
49
49
 
50
- def test_version_comp_with_non_version(self):
50
+ def test_version_comp_with_non_version(self) -> None:
51
51
  v = Version.parse("1.0.0")
52
52
  with pytest.raises(TypeError):
53
53
  _ = v == 123
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
File without changes
File without changes
File without changes
File without changes
File without changes