Skip to content

Commit c9a54af

Browse files
authored
version: fix "next_" methods and increase test coverage (#434)
1 parent 3bb616b commit c9a54af

File tree

2 files changed

+186
-17
lines changed

2 files changed

+186
-17
lines changed

src/poetry/core/version/pep440/version.py

+45-12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import dataclasses
44
import functools
5+
import warnings
56

67
from typing import TYPE_CHECKING
78
from typing import Any
@@ -201,46 +202,62 @@ def is_unstable(self) -> bool:
201202
def is_stable(self) -> bool:
202203
return not self.is_unstable()
203204

205+
def _is_increment_required(self) -> bool:
206+
return self.is_stable() or (not self.is_prerelease() and self.is_postrelease())
207+
204208
def next_major(self: T) -> T:
205209
release = self.release
206-
if self.is_stable() or Release(self.release.major, 0, 0) < self.release:
207-
release = self.release.next_major()
210+
if self._is_increment_required() or Release(release.major, 0, 0) < release:
211+
release = release.next_major()
208212
return self.__class__(epoch=self.epoch, release=release)
209213

210214
def next_minor(self: T) -> T:
211215
release = self.release
212216
if (
213-
self.is_stable()
214-
or Release(self.release.major, self.release.minor, 0) < self.release
217+
self._is_increment_required()
218+
or Release(release.major, release.minor, 0) < release
215219
):
216-
release = self.release.next_minor()
220+
release = release.next_minor()
217221
return self.__class__(epoch=self.epoch, release=release)
218222

219223
def next_patch(self: T) -> T:
220-
return self.__class__(
221-
epoch=self.epoch,
222-
release=self.release.next_patch() if self.is_stable() else self.release,
223-
)
224+
release = self.release
225+
if (
226+
self._is_increment_required()
227+
or Release(release.major, release.minor, release.patch) < release
228+
):
229+
release = release.next_patch()
230+
return self.__class__(epoch=self.epoch, release=release)
224231

225232
def next_prerelease(self: T, next_phase: bool = False) -> PEP440Version:
233+
if self.is_stable():
234+
warnings.warn(
235+
"Calling next_prerelease() on a stable release is deprecated for its"
236+
" ambiguity. Use next_major(), next_minor(), etc. together with"
237+
" first_prerelease()",
238+
DeprecationWarning,
239+
stacklevel=2,
240+
)
226241
if self.is_prerelease():
227242
assert self.pre is not None
228-
pre = self.pre.next_phase() if next_phase else self.pre.next()
243+
if not self.is_devrelease() or self.is_postrelease():
244+
pre = self.pre.next_phase() if next_phase else self.pre.next()
245+
else:
246+
pre = self.pre
229247
else:
230248
pre = ReleaseTag(RELEASE_PHASE_ID_ALPHA)
231249
return self.__class__(epoch=self.epoch, release=self.release, pre=pre)
232250

233251
def next_postrelease(self: T) -> T:
234252
if self.is_postrelease():
235253
assert self.post is not None
236-
post = self.post.next()
254+
post = self.post.next() if self.dev is None else self.post
237255
else:
238256
post = ReleaseTag(RELEASE_PHASE_ID_POST)
239257
return self.__class__(
240258
epoch=self.epoch,
241259
release=self.release,
242260
pre=self.pre,
243-
dev=self.dev,
244261
post=post,
245262
)
246263

@@ -249,6 +266,13 @@ def next_devrelease(self: T) -> T:
249266
assert self.dev is not None
250267
dev = self.dev.next()
251268
else:
269+
warnings.warn(
270+
"Calling next_devrelease() on a non dev release is deprecated for its"
271+
" ambiguity. Use next_major(), next_minor(), etc. together with"
272+
" first_devrelease()",
273+
DeprecationWarning,
274+
stacklevel=2,
275+
)
252276
dev = ReleaseTag(RELEASE_PHASE_ID_DEV)
253277
return self.__class__(
254278
epoch=self.epoch,
@@ -265,6 +289,15 @@ def first_prerelease(self: T) -> T:
265289
pre=ReleaseTag(RELEASE_PHASE_ID_ALPHA),
266290
)
267291

292+
def first_devrelease(self: T) -> T:
293+
return self.__class__(
294+
epoch=self.epoch,
295+
release=self.release,
296+
pre=self.pre,
297+
post=self.post,
298+
dev=ReleaseTag(RELEASE_PHASE_ID_DEV),
299+
)
300+
268301
def replace(self: T, **kwargs: Any) -> T:
269302
return self.__class__(
270303
**{

tests/semver/test_version.py

+141-5
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,147 @@ def test_difference() -> None:
293293
@pytest.mark.parametrize(
294294
"version, expected",
295295
[
296-
("1.2.3", "1.2.3-dev.0"),
297-
("1.2.3-alpha.0", "1.2.3-alpha.0-dev.0"),
298-
("1.2.3-dev.0", "1.2.3-dev.1"),
296+
("1", "2"),
297+
("2!1", "2!2"),
298+
("1+local", "2"),
299+
("1.2", "2.0"),
300+
("1.2.3", "2.0.0"),
301+
("1.2.3.4", "2.0.0.0"),
302+
("1.dev0", "1"),
303+
("1.2.dev0", "2.0"),
304+
("1.post1", "2"),
305+
("1.2.post1", "2.0"),
306+
("1.post1.dev0", "2"),
307+
("1.2.post1.dev0", "2.0"),
308+
("1.a1", "1"),
309+
("1.2a1", "2.0"),
310+
("1.a1.post2", "1"),
311+
("1.2a1.post2", "2.0"),
312+
("1.a1.post2.dev0", "1"),
313+
("1.2a1.post2.dev0", "2.0"),
299314
],
300315
)
301-
def test_next_devrelease(version: str, expected: str) -> None:
316+
def test_next_major(version: str, expected: str) -> None:
302317
v = Version.parse(version)
303-
assert v.next_devrelease() == Version.parse(expected)
318+
assert str(v.next_major()) == expected
319+
320+
321+
@pytest.mark.parametrize(
322+
"version, expected",
323+
[
324+
("1", "1.1"),
325+
("1.2", "1.3"),
326+
("2!1.2", "2!1.3"),
327+
("1.2+local", "1.3"),
328+
("1.2.3", "1.3.0"),
329+
("1.2.3.4", "1.3.0.0"),
330+
("1.dev0", "1"),
331+
("1.2dev0", "1.2"),
332+
("1.2.3dev0", "1.3.0"),
333+
("1.post1", "1.1"),
334+
("1.2.post1", "1.3"),
335+
("1.2.3.post1", "1.3.0"),
336+
("1.post1.dev0", "1.1"),
337+
("1.2.post1.dev0", "1.3"),
338+
("1.a1", "1"),
339+
("1.2a1", "1.2"),
340+
("1.2.3a1", "1.3.0"),
341+
("1.a1.post2", "1"),
342+
("1.2a1.post2", "1.2"),
343+
("1.2.3a1.post2", "1.3.0"),
344+
("1.a1.post2.dev0", "1"),
345+
("1.2a1.post2.dev0", "1.2"),
346+
("1.2.3a1.post2.dev0", "1.3.0"),
347+
],
348+
)
349+
def test_next_minor(version: str, expected: str) -> None:
350+
v = Version.parse(version)
351+
assert str(v.next_minor()) == expected
352+
353+
354+
@pytest.mark.parametrize(
355+
"version, expected",
356+
[
357+
("1", "1.0.1"),
358+
("1.2", "1.2.1"),
359+
("1.2.3", "1.2.4"),
360+
("2!1.2.3", "2!1.2.4"),
361+
("1.2.3+local", "1.2.4"),
362+
("1.2.3.4", "1.2.4.0"),
363+
("1.dev0", "1"),
364+
("1.2dev0", "1.2"),
365+
("1.2.3dev0", "1.2.3"),
366+
("1.2.3.4dev0", "1.2.4.0"),
367+
("1.post1", "1.0.1"),
368+
("1.2.post1", "1.2.1"),
369+
("1.2.3.post1", "1.2.4"),
370+
("1.post1.dev0", "1.0.1"),
371+
("1.2.post1.dev0", "1.2.1"),
372+
("1.2.3.post1.dev0", "1.2.4"),
373+
("1.a1", "1"),
374+
("1.2a1", "1.2"),
375+
("1.2.3a1", "1.2.3"),
376+
("1.2.3.4a1", "1.2.4.0"),
377+
("1.a1.post2", "1"),
378+
("1.2a1.post2", "1.2"),
379+
("1.2.3a1.post2", "1.2.3"),
380+
("1.2.3.4a1.post2", "1.2.4.0"),
381+
("1.a1.post2.dev0", "1"),
382+
("1.2a1.post2.dev0", "1.2"),
383+
("1.2.3a1.post2.dev0", "1.2.3"),
384+
("1.2.3.4a1.post2.dev0", "1.2.4.0"),
385+
],
386+
)
387+
def test_next_patch(version: str, expected: str) -> None:
388+
v = Version.parse(version)
389+
assert str(v.next_patch()) == expected
390+
391+
392+
@pytest.mark.parametrize(
393+
"version, expected",
394+
[
395+
("1.2a1", "1.2a2"),
396+
("2!1.2a1", "2!1.2a2"),
397+
("1.2dev0", "1.2a0"),
398+
("1.2a1.dev0", "1.2a1"),
399+
("1.2a1.post1.dev0", "1.2a2"),
400+
],
401+
)
402+
def test_next_prerelease(version: str, expected: str) -> None:
403+
v = Version.parse(version)
404+
assert str(v.next_prerelease()) == expected
405+
406+
407+
@pytest.mark.parametrize(
408+
"version, expected",
409+
[
410+
("1", "1.post0"),
411+
("1.post1", "1.post2"),
412+
("9!1.2.3.4", "9!1.2.3.4.post0"),
413+
("9!1.2.3.4.post2", "9!1.2.3.4.post3"),
414+
("1.dev0", "1.post0"),
415+
("1.post1.dev0", "1.post1"),
416+
("1a1", "1a1.post0"),
417+
("1a1.dev0", "1a1.post0"),
418+
("1a1.post2", "1a1.post3"),
419+
("1a1.post2.dev0", "1a1.post2"),
420+
],
421+
)
422+
def test_next_postrelease(version: str, expected: str) -> None:
423+
v = Version.parse(version)
424+
assert str(v.next_postrelease()) == expected
425+
426+
427+
def test_next_devrelease() -> None:
428+
v = Version.parse("9!1.2.3a1.post2.dev3")
429+
assert str(v.next_devrelease()) == "9!1.2.3a1.post2.dev4"
430+
431+
432+
def test_next_firstprerelease() -> None:
433+
v = Version.parse("9!1.2.3a1.post2.dev3")
434+
assert str(v.first_prerelease()) == "9!1.2.3a0"
435+
436+
437+
def test_next_firstdevrelease() -> None:
438+
v = Version.parse("9!1.2.3a1.post2.dev3")
439+
assert str(v.first_devrelease()) == "9!1.2.3a1.post2.dev0"

0 commit comments

Comments
 (0)