From 4bd98036c548d530489210310f6eb0fb90d0cd19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Fri, 6 Sep 2019 13:11:18 +0200 Subject: [PATCH 01/50] Preparing release 2.8.2 --- ChangeLog | 2 +- semantic_version/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 49cb45d..aefc5a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ ChangeLog ========= -2.8.2 (unreleased) +2.8.2 (2019-09-06) ------------------ *Bugfix:* diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index 30bb0ef..d3aaa8c 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.2.dev0' +__version__ = '2.8.2' From ee4a3043bf47f41af055dd87a5efd05538e6b5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Fri, 6 Sep 2019 13:11:31 +0200 Subject: [PATCH 02/50] Back to development: 2.8.3 --- ChangeLog | 6 ++++++ semantic_version/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index aefc5a5..6f63511 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +2.8.3 (unreleased) +------------------ + +- Nothing changed yet. + + 2.8.2 (2019-09-06) ------------------ diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index d3aaa8c..03a22ce 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.2' +__version__ = '2.8.3.dev0' From cfb86ab34051d2cf935b112d4b7a5e7800e703b3 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 12 Sep 2019 23:51:49 +0000 Subject: [PATCH 03/50] Correct typo in SimpleSpec description --- docs/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference.rst b/docs/reference.rst index f187667..da08a22 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -470,7 +470,7 @@ Each of those ``Spec`` classes provides a shared set of methods to work with ver * A clause of ``<=XXX`` will match versions that match ``0.1.2`` will match versions strictly above ``0.1.2``, including all prereleases of ``0.1.3``. * A clause of ``>0.1.2-rc.3`` will match versions strictly above ``0.1.2-rc.3``, including matching prereleases of ``0.1.2``: ``0.1.2-rc.10`` is included; - * A clause of ``<=XXX`` will match versions that match ``>XXX`` or ``==XXX`` + * A clause of ``>=XXX`` will match versions that match ``>XXX`` or ``==XXX`` .. rubric:: Extensions From 371faaf6453b07f5316e356e68d7f26ae44b0df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 21 Nov 2019 12:42:50 +0100 Subject: [PATCH 04/50] Add Clause.prettyprint() for debug. This function allows developers to preview the structure of the resulting clause parsed from a spec, usable with `print(spec.clause.prettyprint())`. Apply typical PEP8 indentation rules. --- semantic_version/base.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/semantic_version/base.py b/semantic_version/base.py index 078d83d..4327fa1 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -673,6 +673,19 @@ def __or__(self, other): def __eq__(self, other): raise NotImplementedError() + def prettyprint(self, indent='\t'): + """Pretty-print the clause. + """ + return '\n'.join(self._pretty()).replace('\t', indent) + + def _pretty(self): + """Actual pretty-printing logic. + + Yields: + A list of string. Indentation is performed with \t. + """ + yield repr(self) + def __ne__(self, other): return not self == other @@ -733,6 +746,15 @@ def __or__(self, other): def __repr__(self): return 'AnyOf(%s)' % ', '.join(sorted(repr(c) for c in self.clauses)) + def _pretty(self): + yield 'AnyOF(' + for clause in self.clauses: + lines = list(clause._pretty()) + for line in lines[:-1]: + yield '\t' + line + yield '\t' + lines[-1] + ',' + yield ')' + class AllOf(Clause): __slots__ = ['clauses'] @@ -789,6 +811,15 @@ def __or__(self, other): def __repr__(self): return 'AllOf(%s)' % ', '.join(sorted(repr(c) for c in self.clauses)) + def _pretty(self): + yield 'AllOF(' + for clause in self.clauses: + lines = list(clause._pretty()) + for line in lines[:-1]: + yield '\t' + line + yield '\t' + lines[-1] + ',' + yield ')' + class Matcher(Clause): __slots__ = [] From da55cf15dd71a0d9a839e7507e4fc69396c8326d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 21 Nov 2019 12:45:28 +0100 Subject: [PATCH 05/50] Fix NpmSpec prerelease-handling. Thanks to Nathan Walters for spotting this. Npm ranges with a `=X.Y.0 && =X.Y.0-* && `_: + Fix handling of prerelease ranges within `NpmSpec` 2.8.2 (2019-09-06) diff --git a/semantic_version/base.py b/semantic_version/base.py index 4327fa1..1b0bac5 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -1273,7 +1273,7 @@ def parse(cls, expression): prerelease_policy=Range.PRERELEASE_ALWAYS, )) elif clause.operator in (Range.OP_LT, Range.OP_LTE): - prerelease_clauses.append(cls.range( + prerelease_clauses.append(Range( operator=Range.OP_GTE, target=Version( major=clause.target.major, @@ -1281,6 +1281,7 @@ def parse(cls, expression): patch=0, prerelease=(), ), + prerelease_policy=Range.PRERELEASE_ALWAYS, )) prerelease_clauses.append(clause) non_prerel_clauses.append(cls.range( diff --git a/tests/test_npm.py b/tests/test_npm.py index 7bed337..da3f5ba 100644 --- a/tests/test_npm.py +++ b/tests/test_npm.py @@ -40,6 +40,10 @@ def subTest(self, **kwargs): ['1.2.3-alpha.3', '1.2.3-alpha.7', '3.4.5'], ['1.2.3-alpha.2', '3.4.5-alpha.9'], ), + '>1.2.3-alpha <1.2.3-beta': ( + ['1.2.3-alpha.0', '1.2.3-alpha.1'], + ['1.2.3', '1.2.3-beta.0', '1.2.3-bravo'], + ), '1.2.3 - 2.3.4': ( ['1.2.3', '1.2.99', '2.2.0', '2.3.4', '2.3.4+b42'], ['1.2.0', '1.2.3-alpha.1', '2.3.5'], From 7d6bbc2ec58f91d203b12ba4b3a430dabaec8f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 21 Nov 2019 13:18:34 +0100 Subject: [PATCH 06/50] Preparing release 2.8.3 --- ChangeLog | 2 +- semantic_version/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1158103..832795f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ ChangeLog ========= -2.8.3 (unreleased) +2.8.3 (2019-11-21) ------------------ *New:* diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index 03a22ce..d72291f 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.3.dev0' +__version__ = '2.8.3' From 58550c0110d224231a7f36fe0f1c13df8dbbb87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 21 Nov 2019 13:18:46 +0100 Subject: [PATCH 07/50] Back to development: 2.8.4 --- ChangeLog | 6 ++++++ semantic_version/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 832795f..b777535 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +2.8.4 (unreleased) +------------------ + +- Nothing changed yet. + + 2.8.3 (2019-11-21) ------------------ diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index d72291f..ddec7bb 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.3' +__version__ = '2.8.4.dev0' From 25db2464933444f7d332edba94bcee48fa8c0793 Mon Sep 17 00:00:00 2001 From: Thijs Damsma Date: Mon, 16 Dec 2019 14:57:42 +0100 Subject: [PATCH 08/50] Update README.rst resolves #87 --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ef824ea..40959c4 100644 --- a/README.rst +++ b/README.rst @@ -171,8 +171,8 @@ The :class:`SimpleSpec` object describes a range of accepted versions: >>> s = SimpleSpec('>=0.1.1') # At least 0.1.1 >>> s.match(Version('0.1.1')) True - >>> s.match(Version('0.1.1-alpha1')) # pre-release satisfy version spec - True + >>> s.match(Version('0.1.1-alpha1')) # pre-release doesn't satisfy version spec + False >>> s.match(Version('0.1.0')) False From 2fb427d0d9d9c70d08a707c6fb5bcc2ae2c4023b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sat, 21 Dec 2019 15:51:24 +0100 Subject: [PATCH 09/50] Properly coerce versions with leading zeroes. A leading zero is forbidden in the SemVer spec, but could be valid under other schemes; when coercing, it can easily be removed. Closes #89, thanks to Andrew Ni for the report. --- ChangeLog | 6 +++++- semantic_version/base.py | 8 ++++++++ tests/test_base.py | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index b777535..a9efaad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,11 @@ ChangeLog 2.8.4 (unreleased) ------------------ -- Nothing changed yet. +*Bugfix:* + + * `#89 `_: + Properly coerce versions with leading zeroes in components (e.g. + ``1.01.007``) 2.8.3 (2019-11-21) diff --git a/semantic_version/base.py b/semantic_version/base.py index 1b0bac5..7fd871e 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -244,6 +244,14 @@ def coerce(cls, version_string, partial=False): while version.count('.') < 2: version += '.0' + # Strip leading zeros in components + # Version is of the form nn, nn.pp or nn.pp.qq + version = '.'.join( + # If the part was '0', we end up with an empty string. + part.lstrip('0') or '0' + for part in version.split('.') + ) + if match.end() == len(version_string): return Version(version, partial=partial) diff --git a/tests/test_base.py b/tests/test_base.py index 2c0830f..c9770c9 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -569,7 +569,7 @@ def subTest(self, **kwargs): examples = { # Dict of target: [list of equivalents] '0.0.0': ('0', '0.0', '0.0.0', '0.0.0+', '0-'), - '0.1.0': ('0.1', '0.1+', '0.1-', '0.1.0'), + '0.1.0': ('0.1', '0.1+', '0.1-', '0.1.0', '0.01.0', '000.0001.0000000000'), '0.1.0+2': ('0.1.0+2', '0.1.0.2'), '0.1.0+2.3.4': ('0.1.0+2.3.4', '0.1.0+2+3+4', '0.1.0.2+3+4'), '0.1.0+2-3.4': ('0.1.0+2-3.4', '0.1.0+2-3+4', '0.1.0.2-3+4', '0.1.0.2_3+4'), From db7ac214cc5b47c223406cd74529698b765d68f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sat, 21 Dec 2019 16:02:24 +0100 Subject: [PATCH 10/50] Preparing release 2.8.4 --- ChangeLog | 2 +- semantic_version/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index a9efaad..698dcf6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ ChangeLog ========= -2.8.4 (unreleased) +2.8.4 (2019-12-21) ------------------ *Bugfix:* diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index ddec7bb..ccd9b94 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.4.dev0' +__version__ = '2.8.4' From eda1ac8c22a99dbd01cd57fbcd459856b469ae46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sat, 21 Dec 2019 16:02:40 +0100 Subject: [PATCH 11/50] Back to development: 2.8.5 --- ChangeLog | 6 ++++++ semantic_version/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 698dcf6..b7f41c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +2.8.5 (unreleased) +------------------ + +- Nothing changed yet. + + 2.8.4 (2019-12-21) ------------------ diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index ccd9b94..38250c5 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.4' +__version__ = '2.8.5.dev0' From 0811fca1c435c1477ee1465578580b491cba6c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 29 Apr 2020 11:46:24 +0200 Subject: [PATCH 12/50] Fix wildcard matching for SimpleSpec. Including docs and tests. Closes #98. --- ChangeLog | 5 ++++- docs/reference.rst | 9 +++++++++ semantic_version/base.py | 2 +- tests/test_base.py | 10 ++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index b7f41c6..7056f1a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,10 @@ ChangeLog 2.8.5 (unreleased) ------------------ -- Nothing changed yet. +*Bugfix:* + + * `98 `_: + Properly handle wildcards in ``SimpleSpec`` (e.g. ``==1.2.*``). 2.8.4 (2019-12-21) diff --git a/docs/reference.rst b/docs/reference.rst index da08a22..951bd9a 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -472,6 +472,15 @@ Each of those ``Spec`` classes provides a shared set of methods to work with ver * A clause of ``>0.1.2-rc.3`` will match versions strictly above ``0.1.2-rc.3``, including matching prereleases of ``0.1.2``: ``0.1.2-rc.10`` is included; * A clause of ``>=XXX`` will match versions that match ``>XXX`` or ``==XXX`` + ..rubric:: Wildcards + + * A clause of ``==0.1.*`` is equivalent to ``>=0.1.0,<0.2.0`` + * A clause of ``>=0.1.*`` is equivalent to ``>=0.1.0`` + * A clause of ``==1.*`` or ``==1.*.*`` is equivalent to ``>=1.0.0,<2.0.0`` + * A clause of ``>=1.*`` or ``>=1.*.*`` is equivalent to ``>=1.0.0`` + * A clause of ``==*`` maps to ``>=0.0.0`` + * A clause of ``>=*`` maps to ``>=0.0.0`` + .. rubric:: Extensions Additionnally, python-semanticversion supports extensions from specific packaging platforms: diff --git a/semantic_version/base.py b/semantic_version/base.py index 7fd871e..871ccb0 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -1123,7 +1123,7 @@ def parse_block(cls, expr): elif minor is None: return Range(Range.OP_GTE, target) & Range(Range.OP_LT, target.next_major()) elif patch is None: - return Range(Range.OP_GTE, target) & Range(Range.OP_LT, target.next_patch()) + return Range(Range.OP_GTE, target) & Range(Range.OP_LT, target.next_minor()) elif build == '': return Range(Range.OP_EQ, target, build_policy=Range.BUILD_STRICT) else: diff --git a/tests/test_base.py b/tests/test_base.py index c9770c9..d0a0e81 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -644,6 +644,16 @@ def test_parsing_split(self): ['0.1.1', '0.1.1+4'], ['0.1.1-alpha', '0.1.2-alpha', '0.1.2', '1.3.4'], ), + # 0.1.x + '==0.1.*': ( + ['0.1.1', '0.1.1+4', '0.1.0', '0.1.99'], + ['0.1.0-alpha', '0.0.1', '0.2.0'], + ), + # 1.x.x + '==1.*': ( + ['1.1.1', '1.1.0+4', '1.1.0', '1.99.99'], + ['1.0.0-alpha', '0.1.0', '2.0.0'], + ), # At least 0.1.0 with pre-releases, less than 0.1.4 excluding pre-releases, # neither 0.1.3-rc1 nor any build of that version, # not 0.1.0+b3 precisely From 9e968aef1a8aee4039b3052acc6f439b03bc315c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 29 Apr 2020 11:51:19 +0200 Subject: [PATCH 13/50] Preparing release 2.8.5 --- ChangeLog | 2 +- semantic_version/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7056f1a..3df9e13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ ChangeLog ========= -2.8.5 (unreleased) +2.8.5 (2020-04-29) ------------------ *Bugfix:* diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index 38250c5..9de7ff1 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.5.dev0' +__version__ = '2.8.5' From 23513836fb6273ef4deea7cd4059956f68c26217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 29 Apr 2020 11:56:04 +0200 Subject: [PATCH 14/50] Back to development: 2.8.6 --- ChangeLog | 6 ++++++ semantic_version/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3df9e13..ea12236 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +2.8.6 (unreleased) +------------------ + +- Nothing changed yet. + + 2.8.5 (2020-04-29) ------------------ diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index 9de7ff1..3e3f789 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,4 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.5' +__version__ = '2.8.6.dev0' From a7742f06bcc507d659b97f1513781ddfddeff734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 7 Jul 2020 09:33:19 +0200 Subject: [PATCH 15/50] Add tests for Django's get_or_create. Related to issue #97. --- tests/test_django.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_django.py b/tests/test_django.py index 5fff8a9..3361a9b 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -261,3 +261,17 @@ def test_db_interaction(self): obj2 = models.VersionModel.objects.get(pk=o2.pk) self.assertEqual(o2.version, obj2.version) + + def test_get_or_create(self): + o1, created = models.VersionModel.objects.get_or_create(version=Version('0.1.1'), spec=SimpleSpec('==0.4.3')) + self.assertTrue(created) + self.assertIsNotNone(o1.pk) + self.assertEqual(Version('0.1.1'), o1.version) + self.assertEqual(SimpleSpec('==0.4.3'), o1.spec) + + o2, created = models.VersionModel.objects.get_or_create(version=Version('0.1.1'), spec=SimpleSpec('==0.4.3')) + self.assertFalse(created) + self.assertEqual(o1, o2) + self.assertEqual(o1.pk, o2.pk) + self.assertEqual(Version('0.1.1'), o2.version) + self.assertEqual(SimpleSpec('==0.4.3'), o2.spec) From 7ccc7244103d84e228f23b0bffa225c2d2f3d295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 7 Jul 2020 09:58:53 +0200 Subject: [PATCH 16/50] Linting. Add missing blank lines. --- tests/test_base.py | 6 ++++++ tests/test_match.py | 1 + tests/test_npm.py | 1 + tests/test_parsing.py | 2 ++ 4 files changed, 10 insertions(+) diff --git a/tests/test_base.py b/tests/test_base.py index d0a0e81..4a844c3 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -16,6 +16,7 @@ class TopLevelTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield @@ -98,6 +99,7 @@ def test_validate_invalid(self): class VersionTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield @@ -383,6 +385,7 @@ def test_bump_prerelease_versions(self): class SpecItemTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield @@ -562,6 +565,7 @@ def test_hash(self): class CoerceTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield @@ -592,12 +596,14 @@ def test_invalid(self): class SpecTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield def assertCountEqual(self, a, b): import collections + self.assertEqual( collections.Counter(a), collections.Counter(b), diff --git a/tests/test_match.py b/tests/test_match.py index f807828..6851eb2 100755 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -12,6 +12,7 @@ class MatchTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield diff --git a/tests/test_npm.py b/tests/test_npm.py index da3f5ba..2102cd8 100644 --- a/tests/test_npm.py +++ b/tests/test_npm.py @@ -14,6 +14,7 @@ class NpmSpecTests(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 01b7b5e..45d22c6 100755 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -13,6 +13,7 @@ class ParsingTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield @@ -75,6 +76,7 @@ def test_kwargs(self): class ComparisonTestCase(unittest.TestCase): if sys.version_info[0] <= 2: import contextlib + @contextlib.contextmanager def subTest(self, **kwargs): yield From 88b077b1cc19a4754ef004ff0ca07c3d2bb8ef19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 8 Jul 2020 14:04:12 +0200 Subject: [PATCH 17/50] Fix SimpleSpec doc in README.rst. As caught by Emilio Reyes. Closes #103. --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 40959c4..1cb21e5 100644 --- a/README.rst +++ b/README.rst @@ -181,8 +181,10 @@ Simpler test syntax is also available using the ``in`` keyword: .. code-block:: pycon >>> s = SimpleSpec('==0.1.1') - >>> Version('0.1.1-alpha1') in s + >>> Version('0.1.1+git7ccc72') in s # build variants are equivalent to full versions True + >>> Version('0.1.1-alpha1') in s # pre-release variants don't match the full version. + False >>> Version('0.1.2') in s False From 8ed1ec495a716c2e2dc7fb6df28c086e35a8950e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 9 Aug 2020 12:52:30 +0200 Subject: [PATCH 18/50] Fix mixup between 'patch' and 'build' in README. Thanks to Clare Macrae for catching this! Closes #104. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1cb21e5..020b6b3 100644 --- a/README.rst +++ b/README.rst @@ -152,7 +152,7 @@ Finally, one may create a :class:`Version` with named components instead: Version('0.1.2') In that case, ``major``, ``minor`` and ``patch`` are mandatory, and must be integers. -``prerelease`` and ``patch``, if provided, must be tuples of strings: +``prerelease`` and ``build``, if provided, must be tuples of strings: .. code-block:: pycon From 306fbe1b2222d218c62c2ddb32d91eeea8957951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 11:20:51 +0100 Subject: [PATCH 19/50] Move package metadata to setup.cfg Instead of relying on setup.py, use setup.cfg. Dev/doc requirements have been moved there as well, under the `doc` and `dev` extras. --- Makefile | 2 +- requirements.txt | 1 - requirements_dev.txt | 14 -------- requirements_test.txt | 3 -- semantic_version/__init__.py | 10 +++++- setup.cfg | 57 ++++++++++++++++++++++++++---- setup.py | 67 +----------------------------------- tox.ini | 5 ++- 8 files changed, 64 insertions(+), 95 deletions(-) delete mode 100644 requirements.txt delete mode 100644 requirements_dev.txt delete mode 100644 requirements_test.txt diff --git a/Makefile b/Makefile index c94363c..811d72a 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ clean: update: pip install --upgrade pip setuptools - pip install --upgrade -r requirements_dev.txt + pip install --upgrade -e .[dev,doc] pip freeze diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e18794f..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -# No hard external requirements. diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index 741f337..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Requirements for local development --e . --r requirements_test.txt - -Django>=1.11 - -coverage -wheel -tox - -Sphinx -sphinx_rtd_theme - -zest.releaser[recommended] diff --git a/requirements_test.txt b/requirements_test.txt deleted file mode 100644 index 35012df..0000000 --- a/requirements_test.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Common requirements for running tests -check_manifest -flake8 diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py index 3e3f789..1528bda 100644 --- a/semantic_version/__init__.py +++ b/semantic_version/__init__.py @@ -7,4 +7,12 @@ __author__ = "Raphaël Barrois " -__version__ = '2.8.6.dev0' +try: + # Python 3.8+ + from importlib.metadata import version + + __version__ = version("semantic_version") +except ImportError: + import pkg_resources + + __version__ = pkg_resources.get_distribution("semantic_version").version diff --git a/setup.cfg b/setup.cfg index e6ff787..3e5e632 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,15 +1,60 @@ -[bdist_wheel] -universal = 1 - [metadata] +name = semantic_version +version = 2.8.6.dev0 +description = A library implementing the 'SemVer' scheme. +long_description = file: README.rst +# https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data +long_description_content_type = text/x-rst +author = Raphaël Barrois +author_email = raphael.barrois+semver@polytechnique.org +url = https://github.com/rbarrois/python-semanticversion +keywords = semantic version, versioning, version +license = BSD license_file = LICENSE +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Topic :: Software Development :: Libraries :: Python Modules + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Topic :: Software Development :: Libraries :: Python Modules + +[options] +zip_safe = false +packages = semantic_version +python_requires = >= 2.7 +install_requires = + +[options.extras_require] +dev = + Django>=1.11 + # Runners + tox + # Quality + check_manifest + coverage + flake8 + # Packaging + wheel + zest.releaser[recommended] + +doc = + Sphinx + sphinx_rtd_theme + +[bdist_wheel] +universal = 1 [zest.releaser] ; semver-style versions version-levels = 3 -; Version flag location (we use __version__) -python-file-with-version = semantic_version/__init__.py - [distutils] index-servers = pypi diff --git a/setup.py b/setup.py index 7ad8d5f..02a989b 100755 --- a/setup.py +++ b/setup.py @@ -3,71 +3,6 @@ # Copyright (c) The python-semanticversion project -import codecs -import os -import re - from setuptools import setup -root_dir = os.path.abspath(os.path.dirname(__file__)) - - -def get_version(package_name): - version_re = re.compile(r"^__version__ = [\"']([\w_.-]+)[\"']$") - package_components = package_name.split('.') - init_path = os.path.join(root_dir, *(package_components + ['__init__.py'])) - with codecs.open(init_path, 'r', 'utf-8') as f: - for line in f: - match = version_re.match(line[:-1]) - if match: - return match.groups()[0] - return '0.1.0' - - -def clean_readme(fname): - """Cleanup README.rst for proper PyPI formatting.""" - with codecs.open(fname, 'r', 'utf-8') as f: - return ''.join( - re.sub(r':\w+:`([^`]+?)( <[^<>]+>)?`', r'``\1``', line) - for line in f - if not (line.startswith('.. currentmodule') or line.startswith('.. toctree')) - ) - - -PACKAGE = 'semantic_version' - - -setup( - name=PACKAGE, - version=get_version(PACKAGE), - author="Raphaël Barrois", - author_email="raphael.barrois+semver@polytechnique.org", - description="A library implementing the 'SemVer' scheme.", - long_description=clean_readme('README.rst'), - license='BSD', - keywords=['semantic version', 'versioning', 'version'], - url='https://github.com/rbarrois/python-semanticversion', - download_url='http://pypi.python.org/pypi/semantic_version/', - packages=['semantic_version'], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", - setup_requires=[ - 'setuptools>=0.8', - ], - zip_safe=False, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Software Development :: Libraries :: Python Modules' - ], - test_suite='tests', -) +setup() diff --git a/tox.ini b/tox.ini index 2841dbb..d5cab59 100644 --- a/tox.ini +++ b/tox.ini @@ -8,8 +8,8 @@ envlist = toxworkdir = {env:TOX_WORKDIR:.tox} [testenv] +extras = dev deps = - -rrequirements_test.txt django111: Django>=1.11,<1.12 django22: Django>=2.2,<2.3 @@ -17,8 +17,7 @@ whitelist_externals = make commands = make test [testenv:lint] -deps = - -rrequirements_test.txt +extras = dev whitelist_externals = make commands = make lint From a146f483686ffcb76f2a33b17fead72b872b3f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 11:22:10 +0100 Subject: [PATCH 20/50] Move flake8 configuration to setup.cfg --- .flake8 | 4 ---- Makefile | 4 ++-- setup.cfg | 5 +++++ 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index eceea15..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -# Ignore "and" at start of line. -ignore = W503 -max-line-length = 120 diff --git a/Makefile b/Makefile index 811d72a..6871485 100644 --- a/Makefile +++ b/Makefile @@ -59,8 +59,8 @@ check-manifest: # Note: we run the linter in two runs, because our __init__.py files has specific warnings we want to exclude # DOC: Verify code quality flake8: - $(FLAKE8) --config .flake8 --exclude $(PACKAGE)/__init__.py $(PACKAGE) $(TESTS_DIR) setup.py - $(FLAKE8) --config .flake8 --ignore F401 $(PACKAGE)/__init__.py + $(FLAKE8) --exclude $(PACKAGE)/__init__.py $(PACKAGE) $(TESTS_DIR) setup.py + $(FLAKE8) --ignore F401 $(PACKAGE)/__init__.py # DOC: Run tests with coverage collection coverage: diff --git a/setup.cfg b/setup.cfg index 3e5e632..778ba33 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,3 +58,8 @@ version-levels = 3 [distutils] index-servers = pypi + +[flake8] +# Ignore "and" at start of line. +ignore = W503 +max-line-length = 120 From c2273d911a8543e566d93ef51f90d9316ad43996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 11:22:23 +0100 Subject: [PATCH 21/50] Replace setup.py test with nose2 We can't use python -m unittest discover directly, as it mistakenly tries to import the source code of the Django test app without going through the setup_django module first. --- Makefile | 2 +- setup.cfg | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6871485..c617168 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ testall: # DOC: Run tests for the currently installed version test: - python -Wdefault setup.py test + python -Wdefault -m nose2 # DOC: Perform code quality tasks lint: check-manifest flake8 diff --git a/setup.cfg b/setup.cfg index 778ba33..799dba3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,6 +36,7 @@ install_requires = dev = Django>=1.11 # Runners + nose2 tox # Quality check_manifest From 9b1681d6b407746898b18276eac0b0b48ee543fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 12:24:06 +0100 Subject: [PATCH 22/50] Upgrade pip on travis Avoids issues with too old pip being unable to download wheels for cryptography. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 258275e..542956a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ script: - tox install: + - pip install --upgrade pip - pip install tox matrix: From d15b65b8331f2c8b783964fac4326d44170cf4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 15:41:02 +0100 Subject: [PATCH 23/50] Prepare switch to github actions --- .github/workflows/check.yml | 31 ++++++++++++++++++++++++++++++ .github/workflows/test.yml | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 .github/workflows/check.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..dc16848 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,31 @@ +name: Check + +on: + - push + - pull_request + +jobs: + build: + name: ${{ matrix.tox-environment }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + tox-environment: + - lint + + env: + TOXENV: ${{ matrix.tox-environment }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + + - name: Install dependencies + run: python -m pip install tox + + - name: Run + run: tox diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d5fe93f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test + +on: + - push + - pull_request + +jobs: + build: + name: Python ${{ matrix.python-version }} / ${{ matrix.tox-environment }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + python-version: + - 3.4 + - 3.6 + - pypy3 + tox-environment: + - django11 + - django22 + + env: + TOXENV: ${{ matrix.tox-environment }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: python -m pip install tox + + - name: Run tests + run: tox From f06225034ed7a553bd7b720c76c6d1538c3c4786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 15:46:21 +0100 Subject: [PATCH 24/50] Exclude invalid Django/Python combinations --- .github/workflows/test.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5fe93f..5c82537 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: jobs: build: - name: Python ${{ matrix.python-version }} / ${{ matrix.tox-environment }} + name: Python ${{ matrix.python-version }} / ${{ matrix.django-family }} runs-on: ubuntu-latest strategy: @@ -16,12 +16,17 @@ jobs: - 3.4 - 3.6 - pypy3 - tox-environment: - - django11 - - django22 + django-family: + - 111 + - 22 + exclude: + - python-version: pypy3 + django-family: 111 + - python-version: 3.4 + django-family: 22 env: - TOXENV: ${{ matrix.tox-environment }} + TOXENV: django${{ matrix.django-family }} steps: - uses: actions/checkout@v2 From e6c30193448fa9fea0cf4f90927bdbd83688429e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 15:52:25 +0100 Subject: [PATCH 25/50] Restrict colorama on Python 3.4 --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 799dba3..eb2e497 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,6 +45,8 @@ dev = # Packaging wheel zest.releaser[recommended] + readme_renderer<25.0; python_version == "3.4" + colorama<=0.4.1; python_version == "3.4" doc = Sphinx From 505f9d96beb118bc14f768e76932a281fdb18541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 16:11:14 +0100 Subject: [PATCH 26/50] Drop support for Travis-CI Also replace the badge with Github Actions --- .travis.yml | 32 -------------------------------- README.rst | 4 ++-- 2 files changed, 2 insertions(+), 34 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 542956a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -sudo: false -language: python - -script: - - tox - -install: - - pip install --upgrade pip - - pip install tox - -matrix: - include: - - python: "2.7" - env: TOXENV=py27-django111 - - python: "3.4" - env: TOXENV=py34-django111 - - python: "3.6" - env: TOXENV=py36-django111 - - python: "3.6" - env: TOXENV=py36-django22 - - # Pypy - - python: "pypy3" - env: TOXENV=pypy3-django22 - - # Linting - - python: "3.6" - env: TOXENV=lint - -notifications: - email: false - irc: "irc.freenode.org#xelnext" diff --git a/README.rst b/README.rst index 020b6b3..03d1691 100644 --- a/README.rst +++ b/README.rst @@ -4,8 +4,8 @@ python-semanticversion This small python library provides a few tools to handle `SemVer`_ in Python. It follows strictly the 2.0.0 version of the SemVer scheme. -.. image:: https://secure.travis-ci.org/rbarrois/python-semanticversion.png?branch=master - :target: http://travis-ci.org/rbarrois/python-semanticversion/ +.. image:: https://github.com/rbarrois/python-semanticversion/actions/workflows/test.yml/badge.svg + :target: https://github.com/rbarrois/python-semanticversion/actions/workflows/test.yml .. image:: https://img.shields.io/pypi/v/semantic_version.svg :target: https://python-semanticversion.readthedocs.io/en/latest/changelog.html From ce5b49fe642a07ff51a41952e62f8cfdaa892bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 16:35:01 +0100 Subject: [PATCH 27/50] Add support for Python 3.7 / 3.8 --- .github/workflows/test.yml | 5 +++++ ChangeLog | 4 +++- setup.cfg | 1 + tox.ini | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c82537..473ce6d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,6 +19,11 @@ jobs: django-family: - 111 - 22 + include: + - python-version: 3.7 + django-family: 22 + - python-version: 3.8 + django-family: 22 exclude: - python-version: pypy3 django-family: 111 diff --git a/ChangeLog b/ChangeLog index ea12236..80fa0fd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,9 @@ ChangeLog 2.8.6 (unreleased) ------------------ -- Nothing changed yet. +*New:* + + * Add support for Python 3.7 / Python 3.8 2.8.5 (2020-04-29) diff --git a/setup.cfg b/setup.cfg index eb2e497..6973ec1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,6 +24,7 @@ classifiers = Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 Topic :: Software Development :: Libraries :: Python Modules [options] diff --git a/tox.ini b/tox.ini index d5cab59..ef6d7b9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = py{27,34,35,36,37}-django111 - py{35,36,37}-django22 + py{35,36,37,38}-django22 pypy3-django{111,22} lint From 3db47018c75f9fc8d853665e97a0021462b39ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 16:40:50 +0100 Subject: [PATCH 28/50] Add support for Django 3.1 --- .github/workflows/test.yml | 6 ++++++ ChangeLog | 1 + tox.ini | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 473ce6d..a4a1d3d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,12 @@ jobs: django-family: 22 - python-version: 3.8 django-family: 22 + - python-version: 3.7 + django-family: 31 + - python-version: 3.8 + django-family: 31 + - python-version: pypy3 + django-family: 31 exclude: - python-version: pypy3 django-family: 111 diff --git a/ChangeLog b/ChangeLog index 80fa0fd..6b23a8a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ ChangeLog *New:* + * Add support for Django 3.1 * Add support for Python 3.7 / Python 3.8 diff --git a/tox.ini b/tox.ini index ef6d7b9..f1cb1be 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,8 @@ envlist = py{27,34,35,36,37}-django111 py{35,36,37,38}-django22 - pypy3-django{111,22} + py{36,37,38,39}-django31 + pypy3-django{111,22,31} lint toxworkdir = {env:TOX_WORKDIR:.tox} @@ -12,6 +13,7 @@ extras = dev deps = django111: Django>=1.11,<1.12 django22: Django>=2.2,<2.3 + django31: Django>=3.1,<3.2 whitelist_externals = make commands = make test From fa092f786e69c88c5a278309c6e2fb78d81b5975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 2 Mar 2021 16:44:44 +0100 Subject: [PATCH 29/50] Add support for Python 3.9 --- .github/workflows/test.yml | 33 ++++++++++++++++++++------------- ChangeLog | 2 +- setup.cfg | 1 + 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a4a1d3d..67e1fbf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,29 +12,36 @@ jobs: strategy: fail-fast: false matrix: - python-version: - - 3.4 - - 3.6 - - pypy3 - django-family: - - 111 - - 22 include: + # Django 1.11: 3.4, 3.7 + - python-version: 2.7 + django-family: 111 + - python-version: 3.4 + django-family: 111 + - python-version: 3.7 + django-family: 111 + - python-version: pypy3 + django-family: 111 + + # Django 2.2: 3.5, 3.7, 3.8 + - python-version: 3.5 + django-family: 22 - python-version: 3.7 django-family: 22 - python-version: 3.8 django-family: 22 - - python-version: 3.7 + - python-version: pypy3 + django-family: 22 + + # Django 3.1: Python 3.6, 3.8, 3.9 + - python-version: 3.6 django-family: 31 - python-version: 3.8 django-family: 31 - - python-version: pypy3 + - python-version: 3.9 django-family: 31 - exclude: - python-version: pypy3 - django-family: 111 - - python-version: 3.4 - django-family: 22 + django-family: 31 env: TOXENV: django${{ matrix.django-family }} diff --git a/ChangeLog b/ChangeLog index 6b23a8a..c8453e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,7 +7,7 @@ ChangeLog *New:* * Add support for Django 3.1 - * Add support for Python 3.7 / Python 3.8 + * Add support for Python 3.7 / 3.8 / 3.9 2.8.5 (2020-04-29) diff --git a/setup.cfg b/setup.cfg index 6973ec1..42360d4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,6 +25,7 @@ classifiers = Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Topic :: Software Development :: Libraries :: Python Modules [options] From f67ece3b0aaac69f4d331b213090ed78e6ba46d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Mon, 8 Nov 2021 14:23:34 +0100 Subject: [PATCH 30/50] Remove ambiguous floats in .yml files `3.8` is interpreted as a floating point number, but we view it as a string identifier; quote all python version numbers. --- .github/workflows/test.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67e1fbf..5101fd2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,33 +14,33 @@ jobs: matrix: include: # Django 1.11: 3.4, 3.7 - - python-version: 2.7 + - python-version: "2.7" django-family: 111 - - python-version: 3.4 + - python-version: "3.4" django-family: 111 - - python-version: 3.7 + - python-version: "3.7" django-family: 111 - - python-version: pypy3 + - python-version: "pypy3" django-family: 111 # Django 2.2: 3.5, 3.7, 3.8 - - python-version: 3.5 + - python-version: "3.5" django-family: 22 - - python-version: 3.7 + - python-version: "3.7" django-family: 22 - - python-version: 3.8 + - python-version: "3.8" django-family: 22 - - python-version: pypy3 + - python-version: "pypy3" django-family: 22 # Django 3.1: Python 3.6, 3.8, 3.9 - - python-version: 3.6 + - python-version: "3.6" django-family: 31 - - python-version: 3.8 + - python-version: "3.8" django-family: 31 - - python-version: 3.9 + - python-version: "3.9" django-family: 31 - - python-version: pypy3 + - python-version: "pypy3" django-family: 31 env: From 63720c70dd538234e75914e2afb80b94d5e88a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Mon, 8 Nov 2021 14:17:52 +0100 Subject: [PATCH 31/50] Extend test matrix for Python 3.10 / Django 3.2 Django >=3.2.9 is compatible with Python 3.10 --- .github/workflows/test.yml | 12 +++++++----- setup.cfg | 1 + tox.ini | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5101fd2..7eb4638 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,15 +33,17 @@ jobs: - python-version: "pypy3" django-family: 22 - # Django 3.1: Python 3.6, 3.8, 3.9 + # Django 3.2: Python 3.6, 3.8, 3.9, 3.10 - python-version: "3.6" - django-family: 31 + django-family: 32 - python-version: "3.8" - django-family: 31 + django-family: 32 - python-version: "3.9" - django-family: 31 + django-family: 32 + - python-version: "3.10" + django-family: 32 - python-version: "pypy3" - django-family: 31 + django-family: 32 env: TOXENV: django${{ matrix.django-family }} diff --git a/setup.cfg b/setup.cfg index 42360d4..e7fd5e9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Software Development :: Libraries :: Python Modules [options] diff --git a/tox.ini b/tox.ini index f1cb1be..31e6e34 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ envlist = py{27,34,35,36,37}-django111 py{35,36,37,38}-django22 - py{36,37,38,39}-django31 - pypy3-django{111,22,31} + py{36,37,38,39,310}-django32 + pypy3-django{111,22,32} lint toxworkdir = {env:TOX_WORKDIR:.tox} @@ -13,7 +13,7 @@ extras = dev deps = django111: Django>=1.11,<1.12 django22: Django>=2.2,<2.3 - django31: Django>=3.1,<3.2 + django32: Django>=3.2,<3.3 whitelist_externals = make commands = make test From 8abf0030e215804eb84e63a9233010abc02e05a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Mon, 8 Nov 2021 14:18:29 +0100 Subject: [PATCH 32/50] Add support for Django 4.0 The gettext_lazy function has a different name between Django 2.x and 4.x; use the right one according to the version. Closes #113, #121 --- .github/workflows/test.yml | 8 ++++++++ ChangeLog | 4 ++-- semantic_version/django_fields.py | 8 +++++++- tox.ini | 2 ++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7eb4638..02cb641 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,6 +45,14 @@ jobs: - python-version: "pypy3" django-family: 32 + # Django 4.0: Python 3.8, 3.9, 3.10 + - python-version: "3.8" + django-family: 40 + - python-version: "3.9" + django-family: 40 + - python-version: "3.10" + django-family: 40 + env: TOXENV: django${{ matrix.django-family }} diff --git a/ChangeLog b/ChangeLog index c8453e5..211be6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,8 +6,8 @@ ChangeLog *New:* - * Add support for Django 3.1 - * Add support for Python 3.7 / 3.8 / 3.9 + * Add support for Django 3.1, 3.2, 4.0 + * Add support for Python 3.7 / 3.8 / 3.9 / 3.10 2.8.5 (2020-04-29) diff --git a/semantic_version/django_fields.py b/semantic_version/django_fields.py index caa7a8c..e5bd7eb 100644 --- a/semantic_version/django_fields.py +++ b/semantic_version/django_fields.py @@ -4,8 +4,14 @@ import warnings +import django from django.db import models -from django.utils.translation import ugettext_lazy as _ + +if django.VERSION >= (3, 0): + # See https://docs.djangoproject.com/en/dev/releases/3.0/#features-deprecated-in-3-0 + from django.utils.translation import gettext_lazy as _ +else: + from django.utils.translation import ugettext_lazy as _ from . import base diff --git a/tox.ini b/tox.ini index 31e6e34..26cbebe 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = py{27,34,35,36,37}-django111 py{35,36,37,38}-django22 py{36,37,38,39,310}-django32 + py{38,39,310}-django40 pypy3-django{111,22,32} lint @@ -14,6 +15,7 @@ deps = django111: Django>=1.11,<1.12 django22: Django>=2.2,<2.3 django32: Django>=3.2,<3.3 + django40: Django>=4.0a1,<4.1 whitelist_externals = make commands = make test From 81a4730778fba6b5c76242d3c8da6dace7e2ec0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Mon, 8 Nov 2021 14:37:11 +0100 Subject: [PATCH 33/50] Remove Python 3.4 from testing matrix No longer available on GitHub. --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 02cb641..3c9918b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,10 +13,10 @@ jobs: fail-fast: false matrix: include: - # Django 1.11: 3.4, 3.7 + # Django 1.11: 3.5, 3.7 - python-version: "2.7" django-family: 111 - - python-version: "3.4" + - python-version: "3.5" django-family: 111 - python-version: "3.7" django-family: 111 From 7e59a4b2e82abe4338e307b9fe49b072c9537a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 6 Feb 2022 19:40:53 +0100 Subject: [PATCH 34/50] Improve documentation - The README is now a standalone document, also included as an "introduction" page; - A new "guide" section provides more details on most features; - A couple of typos were fixed. The main goal was to make the README file perfectly suitable for rendering on PyPI, while keeping its content available on the standard documentation on ReadTheDocs. --- README.rst | 205 +++++++++---------------- docs/django.rst | 2 +- docs/guide.rst | 346 ++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 31 +++- docs/introduction.rst | 1 + docs/reference.rst | 34 +++-- 6 files changed, 467 insertions(+), 152 deletions(-) create mode 100644 docs/guide.rst mode change 120000 => 100644 docs/index.rst create mode 120000 docs/introduction.rst diff --git a/README.rst b/README.rst index 03d1691..4735cce 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -python-semanticversion -====================== +Introduction +============ This small python library provides a few tools to handle `SemVer`_ in Python. It follows strictly the 2.0.0 version of the SemVer scheme. @@ -57,20 +57,18 @@ Import it in your code: import semantic_version -.. currentmodule:: semantic_version - This module provides classes to handle semantic versions: -- :class:`Version` represents a version number (``0.1.1-alpha+build.2012-05-15``) -- :class:`BaseSpec`-derived classes represent requirement specifications (``>=0.1.1,<0.3.0``): +- ``Version`` represents a version number (``0.1.1-alpha+build.2012-05-15``) +- ``BaseSpec``-derived classes represent requirement specifications (``>=0.1.1,<0.3.0``): - - :class:`SimpleSpec` describes a natural description syntax - - :class:`NpmSpec` is used for NPM-style range descriptions. + - ``SimpleSpec`` describes a natural description syntax + - ``NpmSpec`` is used for NPM-style range descriptions. Versions -------- -Defining a :class:`Version` is quite simple: +Defining a ``Version`` is quite simple: .. code-block:: pycon @@ -90,7 +88,7 @@ Defining a :class:`Version` is quite simple: >>> list(v) [0, 1, 1, [], []] -If the provided version string is invalid, a :exc:`ValueError` will be raised: +If the provided version string is invalid, a ``ValueError`` will be raised: .. code-block:: pycon @@ -104,7 +102,39 @@ If the provided version string is invalid, a :exc:`ValueError` will be raised: ValueError: Invalid version string: '0.1' -Obviously, :class:`Versions ` can be compared: +One may also create a ``Version`` with named components: + +.. code-block:: pycon + + >>> semantic_version.Version(major=0, minor=1, patch=2) + Version('0.1.2') + +In that case, ``major``, ``minor`` and ``patch`` are mandatory, and must be integers. +``prerelease`` and ``build``, if provided, must be tuples of strings: + +.. code-block:: pycon + + >>> semantic_version.Version(major=0, minor=1, patch=2, prerelease=('alpha', '2')) + Version('0.1.2-alpha.2') + + +Some user-supplied input might not match the semantic version scheme. +For such cases, the ``Version.coerce`` method will try to convert any +version-like string into a valid semver version: + +.. code-block:: pycon + + >>> Version.coerce('0') + Version('0.0.0') + >>> Version.coerce('0.1.2.3.4') + Version('0.1.2+3.4') + >>> Version.coerce('0.1.2a3') + Version('0.1.2-a3') + +Working with versions +""""""""""""""""""""" + +Obviously, versions can be compared: .. code-block:: pycon @@ -133,38 +163,31 @@ You can also get a new version that represents a bump in one of the version leve >>> str(new_v) '1.1.2' -It is also possible to check whether a given string is a proper semantic version string: - - -.. code-block:: pycon - - >>> semantic_version.validate('0.1.3') - True - >>> semantic_version.validate('0a2') - False -Finally, one may create a :class:`Version` with named components instead: - -.. code-block:: pycon - - >>> semantic_version.Version(major=0, minor=1, patch=2) - Version('0.1.2') - -In that case, ``major``, ``minor`` and ``patch`` are mandatory, and must be integers. -``prerelease`` and ``build``, if provided, must be tuples of strings: +Requirement specification +------------------------- -.. code-block:: pycon +python-semanticversion provides a couple of ways to describe a range of accepted +versions: - >>> semantic_version.Version(major=0, minor=1, patch=2, prerelease=('alpha', '2')) - Version('0.1.2-alpha.2') +- The ``SimpleSpec`` class provides a simple, easily understood scheme -- + somewhat inspired from PyPI range notations; +- The ``NpmSpec`` class supports the whole NPM range specification scheme: + .. code-block:: pycon -Requirement specification -------------------------- + >>> Version('0.1.2') in NpmSpec('0.1.0-alpha.2 .. 0.2.4') + True + >>> Version('0.1.2') in NpmSpec('>=0.1.1 <0.1.3 || 2.x') + True + >>> Version('2.3.4') in NpmSpec('>=0.1.1 <0.1.3 || 2.x') + True -The :class:`SimpleSpec` object describes a range of accepted versions: +The ``SimpleSpec`` scheme +""""""""""""""""""""""""" +Basic usage is simply a comparator and a base version: .. code-block:: pycon @@ -176,6 +199,12 @@ The :class:`SimpleSpec` object describes a range of accepted versions: >>> s.match(Version('0.1.0')) False +Combining specifications can be expressed as follows: + + .. code-block:: pycon + + >>> SimpleSpec('>=0.1.1,<0.3.0') + Simpler test syntax is also available using the ``in`` keyword: .. code-block:: pycon @@ -189,17 +218,16 @@ Simpler test syntax is also available using the ``in`` keyword: False -Combining specifications can be expressed as follows: - - .. code-block:: pycon +Refer to the full documentation at +https://python-semanticversion.readthedocs.io/en/latest/ for more details on the +``SimpleSpec`` scheme. - >>> SimpleSpec('>=0.1.1,<0.3.0') Using a specification """"""""""""""""""""" -The :func:`SimpleSpec.filter` method filters an iterable of :class:`Version`: +The ``SimpleSpec.filter`` method filters an iterable of ``Version``: .. code-block:: pycon @@ -222,82 +250,6 @@ It is also possible to select the 'best' version from such iterables: Version('0.3.0') -Coercing an arbitrary version string -"""""""""""""""""""""""""""""""""""" - -Some user-supplied input might not match the semantic version scheme. -For such cases, the :meth:`Version.coerce` method will try to convert any -version-like string into a valid semver version: - -.. code-block:: pycon - - >>> Version.coerce('0') - Version('0.0.0') - >>> Version.coerce('0.1.2.3.4') - Version('0.1.2+3.4') - >>> Version.coerce('0.1.2a3') - Version('0.1.2-a3') - - -Including pre-release identifiers in specifications -""""""""""""""""""""""""""""""""""""""""""""""""""" - -When testing a :class:`Version` against a :class:`SimpleSpec`, comparisons are -adjusted for common user expectations; thus, a pre-release version (``1.0.0-alpha``) -will not satisfy the ``==1.0.0`` :class:`SimpleSpec`. - -Pre-release identifiers will only be compared if included in the :class:`BaseSpec` -definition or (for the empty pre-release number) if a single dash is appended -(``1.0.0-``): - - -.. code-block:: pycon - - >>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0') # No pre-release identifier - False - >>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0-') # Include pre-release in checks - True - - -Including build metadata in specifications -"""""""""""""""""""""""""""""""""""""""""" - -Build metadata has no ordering; thus, the only meaningful comparison including -build metadata is equality. - - -.. code-block:: pycon - - >>> Version('1.0.0+build2') in SimpleSpec('<=1.0.0') # Build metadata ignored - True - >>> Version('1.0.0+build1') in SimpleSpec('==1.0.0+build2') # Include build in checks - False - - -NPM-based ranges ----------------- - -The :class:`NpmSpec` class handles NPM-style ranges: - -.. code-block:: pycon - - >>> Version('1.2.3') in NpmSpec('1.2.2 - 1.4') - True - >>> Version('1.2.3') in NpmSpec('<1.x || >=1.2.3') - True - -Refer to https://docs.npmjs.com/misc/semver.html for a detailed description of NPM -range syntax. - - -Using with Django -================= - -The :mod:`semantic_version.django_fields` module provides django fields to -store :class:`Version` or :class:`BaseSpec` objects. - -More documentation is available in the :doc:`django` section. - Contributing ============ @@ -323,26 +275,5 @@ When submitting patches or pull requests, you should respect the following rules # -*- encoding: utf-8 -*- # Copyright (c) The python-semanticversion project - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - reference - django - changelog - credits - - .. _SemVer: http://semver.org/ .. _PyPI: http://pypi.python.org/ - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/django.rst b/docs/django.rst index 34a0fe3..befa50a 100644 --- a/docs/django.rst +++ b/docs/django.rst @@ -37,4 +37,4 @@ with their :attr:`~django.db.models.CharField.max_length` defaulting to 200. The syntax to use for the field; defaults to ``'simple'``. - .. versionaddedd:: 2.7 + .. versionadded:: 2.7 diff --git a/docs/guide.rst b/docs/guide.rst new file mode 100644 index 0000000..7780a32 --- /dev/null +++ b/docs/guide.rst @@ -0,0 +1,346 @@ +Guide +===== + +.. currentmodule:: semantic_version + +This module covers the 2.0.0 version of the SemVer scheme, with additional +extensions: + +- Coercing any version string into a SemVer version, through + :meth:`Version.coerce`; +- Comparing versions; +- Computing next versions; +- Modelling version range specifcations, and choosing the best match -- for both + its custom logic, and NPM semantics (custom range specification schemes can + be added). + + +Version basics +-------------- + +Building :class:`Version` instances +""""""""""""""""""""""""""""""""""" + +The core of the module is the :class:`Version` class; it is usually instantiated +from a version string: + +.. code-block:: pycon + + >>> import semantic_version as semver + >>> v = semver.Version("0.1.1") + +The version's components are available through its attributes: + +* :attr:`~Version.major`, :attr:`~Version.minor`, :attr:`~Version.patch` are + integers: + + .. code-block:: pycon + + >>> v.major + 0 + >>> v.minor + 1 + >>> v.patch + 1 + + +* The :attr:`~Version.prerelease` and :attr:`~Version.build` attributes are + iterables of text elements: + + .. code-block:: pycon + + >>> v2 = semver.Version("0.1.1-dev+23.git2") + >>> v2.prerelease + ["dev"] + >>> v2.build + ["23", "git2"] + + +One may also build a :class:`Version` from named components directly: + +.. code-block:: pycon + + >>> semantic_version.Version(major=0, minor=1, patch=2) + Version('0.1.2') + + +In that case, ``major``, ``minor`` and ``patch`` are mandatory, and must be integers. +``prerelease`` and ``build``, if provided, must be tuples of strings: + +.. code-block:: pycon + + >>> semantic_version.Version(major=0, minor=1, patch=2, prerelease=('alpha', '2')) + Version('0.1.2-alpha.2') + + +If the provided version string is invalid, a :exc:`ValueError` will be raised: + +.. code-block:: pycon + + >>> semver.Version('0.1') + Traceback (most recent call last): + File "", line 1, in + File "/Users/rbarrois/dev/semantic_version/src/semantic_version/base.py", line 64, in __init__ + major, minor, patch, prerelease, build = self.parse(version_string, partial) + File "/Users/rbarrois/dev/semantic_version/src/semantic_version/base.py", line 86, in parse + raise ValueError('Invalid version string: %r' % version_string) + ValueError: Invalid version string: '0.1' + + +Working with non-SemVer version strings +""""""""""""""""""""""""""""""""""""""" + +Some user-supplied input might not match the semantic version scheme. +For such cases, the ``Version.coerce`` method will try to convert any +version-like string into a valid semver version: + +.. code-block:: pycon + + >>> semver.Version.coerce('0') + Version('0.0.0') + >>> semver.Version.coerce('0.1.2.3.4') + Version('0.1.2+3.4') + >>> semver.Version.coerce('0.1.2a3') + Version('0.1.2-a3') + + +Comparing versions +"""""""""""""""""" + +Versions can be compared, following the SemVer scheme: + +.. code-block:: pycon + + >>> semver.Version("0.1.0") < semver.Version("0.1.1") + True + >>> max( + ... semver.Version("0.1.0"), + ... semver.Version("0.2.2"), + ... semver.Version("0.1.1"), + ... semver.Version("0.2.2-rc1"), + ... ) + Version("0.2.2") + + +.. note:: + + As defined in SemVer, build metadata is ignored in comparisons, + but not in equalities: + + .. code-block:: pycon + + >>> semver.Version("0.1.2") <= semver.Version("0.1.2+git2") + True + >>> semver.Version("0.1.2") >= semver.Version("0.1.2+git2") + True + >>> semver.Version("0.1.2") == semver.Version("0.1.2+git2") + False + + +Iterating versions +"""""""""""""""""" + +One can get a new version that represents a bump in one of the version levels +through the :meth:`Version.next_major`, :meth:`Version.next_minor` or +:meth:`Version.next_patch` functions: + +.. code-block:: pycon + + >>> v = semver.Version('0.1.1+build') + >>> new_v = v.next_major() + >>> str(new_v) + '1.0.0' + >>> v = semver.Version('1.1.1+build') + >>> new_v = v.next_minor() + >>> str(new_v) + '1.2.0' + >>> v = semver.Version('1.1.1+build') + >>> new_v = v.next_patch() + >>> str(new_v) + '1.1.2' + +.. note:: + + * If the version includes :attr:`~Version.build` or + :attr:`~Version.prerelease` metadata, that value will be empty in the + next version; + * The next patch following a version with a pre-release identifier + is the same version with its prerelease and build identifiers removed: + ``Version("0.1.1-rc1").next_patch() == Version("0.1.1")`` + * Pre-release and build naming schemes are often custom and specific + to a project's internal design; thus, the library can't provide a + ``next_xxx`` method for those fields. + +One may also truncate versions through the :meth:`Version.truncate` method, +removing components beyond the selected level: + +.. code-block:: pycon + + >>> v = semver.Version("0.1.2-dev+git3") + >>> v.truncate("prerelease") + Version("0.1.2-dev") + >>> v.truncate("minor") + Version("0.1.0") + + +Range specifications +-------------------- + +Comparing version numbers isn't always enough; in many situations, one needs to +define a *range of acceptable versions*. + +That notion is not defined in SemVer; moreover, several systems exists, with +their own notations. + +The ``semantic_version`` package provides a couple of implementations for these +notions: + +- :class:`SimpleSpec` is a simple implementation, with reasonable expectations; +- :class:`NpmSpec` sticks to the NPM specification. + +Further schemes can be built in a similar manner, relying on the :class:`BaseSpec` +class for basics. + +Core API +"""""""" + +The core API is provided by the :class:`BaseSpec` class. + +.. note:: + + These examples use :class:`SimpleSpec` in order to be easily reproduced + by users, but only exhibit the standard parts of the interface. + +It is possible to check whether a given :class:`Version` matches a +:class:`BaseSpec` through :meth:`~BaseSpec.match`: + +.. code-block:: pycon + + >>> s = semver.SimpleSpec(">=0.1.1") + >>> s.match(Version("0.1.1")) + True + >>> s.match(Version("0.1.0")) + False + +This feature is also available through the ``in`` keyword: + +.. code-block:: pycon + + >>> s = semver.SimpleSpec(">=0.1.1") + >>> Version("0.1.1") in s + True + >>> Version("0.1.0") in s + False + +A specification can filter compatible values from an iterable of versions +with :meth:`~BaseSpec.filter`: + +.. code-block:: pycon + + >>> s = semver.SimpleSpec(">=0.2.1") + >>> versions = [ + ... Version("0.1.0"), + ... Version("0.2.0"), + ... Version("0.3.0"), + ... Version("0.4.0"), + ... ] + >>> list(s.filter(versions)) + [Version("0.3.0"), Version("0.4.0")] + +It can also select the "best" version from such an iterable through +:meth:`~BaseSpec.select`: + +.. code-block:: pycon + + >>> s = semver.SimpleSpec(">=0.2.1") + >>> versions = [ + ... Version("0.1.0"), + ... Version("0.2.0"), + ... Version("0.3.0"), + ... Version("0.4.0"), + ... ] + >>> s.select(versions) + Version("0.4.0") + + +The :class:`SimpleSpec` scheme +"""""""""""""""""""""""""""""" + +The :class:`SimpleSpec` provides a hopefully intuitive version range +specification scheme: + +- A specification expression is composed of comma-separated clauses; +- Each clause can be: + + - An equality match (``==`` or ``!=``); + - A comparison (``>``, ``>=``, ``<`` , ``<=``); + - A compatible release clause, PyPI style (``~=2.2`` for ``>=2.2.0,<3.0.0``); + - An NPM style clause: + + - ``~1.2.3`` for ``>=1.2.3,<1.3.0``; + - ``^1.3.4`` for ``>=1.3.4,<2.0.0``; + +- The range in each clause may include a wildcard: + + * ``==0.1.*`` maps to ``>=0.1.0,<0.2.0``; + * ``==1.*`` or ``==1.*.*`` map to ``>=1.0.0,<2.0.0`` + + +.. rubric:: Special matching rules + +When testing a :class:`Version` against a :class:`SimpleSpec`, comparisons are +adjusted for common user expectations; thus, a pre-release version (``1.0.0-alpha``) +will not satisfy the ``==1.0.0`` :class:`SimpleSpec`. + +Pre-release identifiers will only be compared if included in the :class:`BaseSpec` +definition or (for the empty pre-release number) if a single dash is appended +(``1.0.0-``): + + +.. code-block:: pycon + + >>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0') # No pre-release identifier + False + >>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0-') # Include pre-release in checks + True + + +Build metadata has no ordering; thus, the only meaningful comparison including +build metadata is equality: + + +.. code-block:: pycon + + >>> Version('1.0.0+build2') in SimpleSpec('<=1.0.0') # Build metadata ignored + True + >>> Version('1.0.0+build1') in SimpleSpec('==1.0.0+build2') # Include build in checks + False + +.. note:: + + The full documentation is provided in the reference section + for the :class:`SimpleSpec` class. + + +The :class:`NpmSpec` scheme +""""""""""""""""""""""""""" + +The :class:`NpmSpec` class implements the full NPM specification (from +https://docs.npmjs.com/misc/semver.html): + +.. code-block:: pycon + + >>> semver.Version("0.1.2") in semver.NpmSpec("0.1.0-alpha.2 .. 0.2.4") + True + >>> semver.Version('0.1.2') in semver.NpmSpec('>=0.1.1 <0.1.3 || 2.x') + True + >>> semver.Version('2.3.4') in semver.NpmSpec('>=0.1.1 <0.1.3 || 2.x') + True + +Using with Django +----------------- + +The :mod:`semantic_version.django_fields` module provides django fields to +store :class:`Version` or :class:`BaseSpec` objects. + +More documentation is available in the :doc:`django` section. diff --git a/docs/index.rst b/docs/index.rst deleted file mode 120000 index 89a0106..0000000 --- a/docs/index.rst +++ /dev/null @@ -1 +0,0 @@ -../README.rst \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..135ee68 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,30 @@ +====================== +python-semanticversion +====================== + +.. include:: introduction.rst + + +Contents +======== + +.. toctree:: + :maxdepth: 2 + + introduction + guide + reference + django + changelog + credits + + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/introduction.rst b/docs/introduction.rst new file mode 120000 index 0000000..89a0106 --- /dev/null +++ b/docs/introduction.rst @@ -0,0 +1 @@ +../README.rst \ No newline at end of file diff --git a/docs/reference.rst b/docs/reference.rst index 951bd9a..93a29eb 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -66,6 +66,7 @@ Representing a version (the Version class) ------------------------------------------ .. class:: Version(version_string[, partial=False]) + :noindex: Object representation of a `SemVer`_-compliant version. @@ -90,15 +91,6 @@ Representing a version (the Version class) .. rubric:: Attributes - .. attribute:: partial - - ``bool``, whether this is a 'partial' or a complete version number. - Partial version number may lack :attr:`minor` or :attr:`patch` version numbers. - - .. deprecated:: 2.7 - The ability to define a partial version will be removed in version 3.0. - Use :class:`SimpleSpec` instead: ``SimpleSpec('1.x.x')``. - .. attribute:: major ``int``, the major version number @@ -140,6 +132,15 @@ Representing a version (the Version class) Note that the :attr:`~Version.build` isn't included in the precedence_key computatin. + .. attribute:: partial + + ``bool``, whether this is a 'partial' or a complete version number. + Partial version number may lack :attr:`minor` or :attr:`patch` version numbers. + + .. deprecated:: 2.7 + The ability to define a partial version will be removed in version 3.0. + Use :class:`SimpleSpec` instead: ``SimpleSpec('1.x.x')``. + .. rubric:: Methods @@ -173,7 +174,7 @@ Representing a version (the Version class) >>> Version('1.1.0-alpha').next_minor() Version('1.1.0') - .. method:: next_patch(self): + .. method:: next_patch(self) Return the next patch version, i.e the smallest version strictly greater than the current one with empty :attr:`prerelease` and :attr:`build`. @@ -231,6 +232,7 @@ Representing a version (the Version class) - For non-:attr:`partial` versions, compare using the `SemVer`_ scheme - If any compared object is :attr:`partial`: + - Begin comparison using the `SemVer`_ scheme - If a component (:attr:`minor`, :attr:`patch`, :attr:`prerelease` or :attr:`build`) was absent from the :attr:`partial` :class:`Version` -- represented with :obj:`None` @@ -472,7 +474,7 @@ Each of those ``Spec`` classes provides a shared set of methods to work with ver * A clause of ``>0.1.2-rc.3`` will match versions strictly above ``0.1.2-rc.3``, including matching prereleases of ``0.1.2``: ``0.1.2-rc.10`` is included; * A clause of ``>=XXX`` will match versions that match ``>XXX`` or ``==XXX`` - ..rubric:: Wildcards + .. rubric:: Wildcards * A clause of ``==0.1.*`` is equivalent to ``>=0.1.0,<0.2.0`` * A clause of ``>=0.1.*`` is equivalent to ``>=0.1.0`` @@ -554,7 +556,7 @@ Each of those ``Spec`` classes provides a shared set of methods to work with ver , )> - Its keeps a list of :class:`SpecItem` objects, based on the initial expression + It keeps a list of :class:`SpecItem` objects, based on the initial expression components. .. method:: __iter__(self) @@ -705,7 +707,13 @@ Each of those ``Spec`` classes provides a shared set of methods to work with ver >>> Version('1.0.1') in Spec('!=1.0.1') False - The kind of 'Almost equal to' specifications + .. data:: KIND_COMPATIBLE + + The kind of `compatible release clauses`_ + specifications:: + + >>> Version('1.1.2') in Spec('~=1.1.0') + True From 4fcc1475e0161bdf9fa3737023068afae15c62f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 6 Feb 2022 19:43:31 +0100 Subject: [PATCH 35/50] docs: Update CI location --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4735cce..8686366 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,7 @@ Links - Package on `PyPI`_: http://pypi.python.org/pypi/semantic_version/ - Doc on `ReadTheDocs `_: https://python-semanticversion.readthedocs.io/ - Source on `GitHub `_: http://github.com/rbarrois/python-semanticversion/ -- Build on `Travis CI `_: http://travis-ci.org/rbarrois/python-semanticversion/ +- Build on Github Actions: https://github.com/rbarrois/python-semanticversion/actions - Semantic Version specification: `SemVer`_ From 2713cf34f37214f2b3acfa79e8909d0d03dcabac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 6 Feb 2022 19:44:18 +0100 Subject: [PATCH 36/50] doc: Remove remaining Sphinx markup from README --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 8686366..0170dee 100644 --- a/README.rst +++ b/README.rst @@ -266,9 +266,9 @@ When submitting patches or pull requests, you should respect the following rules - Coding conventions are based on :pep:`8` - The whole test suite must pass after adding the changes - The test coverage for a new feature must be 100% -- New features and methods should be documented in the :doc:`reference` section - and included in the :doc:`changelog` -- Include your name in the :ref:`contributors` section +- New features and methods should be documented in the ``reference`` section + and included in the ``changelog`` +- Include your name in the ``contributors`` section .. note:: All files should contain the following header:: From 5f2bf3539ea877540f52ac47d5317ee1b17ba761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 6 Feb 2022 19:45:11 +0100 Subject: [PATCH 37/50] Preparing release 2.9.0 --- ChangeLog | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 211be6b..55343d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ ChangeLog ========= -2.8.6 (unreleased) +2.9.0 (2022-02-06) ------------------ *New:* diff --git a/setup.cfg b/setup.cfg index e7fd5e9..7dfce30 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = semantic_version -version = 2.8.6.dev0 +version = 2.9.0 description = A library implementing the 'SemVer' scheme. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From c61278bd35f2059ad3c7fc196a4b06eae34f3b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 6 Feb 2022 19:45:25 +0100 Subject: [PATCH 38/50] Back to development: 2.9.1 --- ChangeLog | 6 ++++++ setup.cfg | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 55343d3..da05e60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +2.9.1 (unreleased) +------------------ + +- Nothing changed yet. + + 2.9.0 (2022-02-06) ------------------ diff --git a/setup.cfg b/setup.cfg index 7dfce30..4bac2c1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = semantic_version -version = 2.9.0 +version = 2.9.1.dev0 description = A library implementing the 'SemVer' scheme. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From 8a7162fc01b33964688a5be41df3865bceb875c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Sun, 6 Feb 2022 19:52:05 +0100 Subject: [PATCH 39/50] docs: Update reference to NPM range specification --- ChangeLog | 2 +- docs/guide.rst | 2 +- docs/reference.rst | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index da05e60..0d30c7e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -100,7 +100,7 @@ Backwards compatibility has been kept, but users should adjust their code for th * Allow creation of a ``Version`` directly from parsed components, as keyword arguments (``Version(major=1, minor=2, patch=3)``) * Add ``Version.truncate()`` to build a truncated copy of a ``Version`` - * Add ``NpmSpec(...)``, following strict NPM matching rules (https://docs.npmjs.com/misc/semver) + * Add ``NpmSpec(...)``, following strict NPM matching rules (https://github.com/npm/node-semver#ranges) * Add ``Spec.parse('xxx', syntax='')`` for simpler multi-syntax support * Add ``Version().precedence_key``, for use in ``sort(versions, key=lambda v: v.precedence_key)`` calls. The contents of this attribute is an implementation detail. diff --git a/docs/guide.rst b/docs/guide.rst index 7780a32..9a7c5cd 100644 --- a/docs/guide.rst +++ b/docs/guide.rst @@ -326,7 +326,7 @@ The :class:`NpmSpec` scheme """"""""""""""""""""""""""" The :class:`NpmSpec` class implements the full NPM specification (from -https://docs.npmjs.com/misc/semver.html): +https://github.com/npm/node-semver#ranges): .. code-block:: pycon diff --git a/docs/reference.rst b/docs/reference.rst index 93a29eb..6d1101b 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -336,7 +336,7 @@ In order to solve this problem, each `SemVer`_-based package management platform python-semanticversion provides a couple of implementations of those range definition syntaxes: - ``'simple'`` (through :class:`SimpleSpec`): A python-semanticversion specific syntax, which supports simple / intuitive patterns, and some NPM-inspired extensions; -- ``'npm'`` (through :class:`NpmSpec`): The NPM syntax, based on https://docs.npmjs.com/misc/semver.html +- ``'npm'`` (through :class:`NpmSpec`): The NPM syntax, based on https://github.com/npm/node-semver#ranges - More might be added in the future. Each of those ``Spec`` classes provides a shared set of methods to work with versions: @@ -518,7 +518,7 @@ Each of those ``Spec`` classes provides a shared set of methods to work with ver .. versionadded:: 2.7 - A NPM-compliant version matching engine, based on the https://docs.npmjs.com/misc/semver.html specification. + A NPM-compliant version matching engine, based on the https://github.com/npm/node-semver#ranges specification. .. code-block:: pycon From 47be07eb4a632850b28cc584b4caa54ed02cd924 Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Wed, 17 Nov 2021 14:47:39 +0100 Subject: [PATCH 40/50] Update the Version.parse() to match the code The docstring was lying by saying a Version object was returned. Rather this function returns a tuple of version parts. Signed-off-by: Philippe Ombredanne --- semantic_version/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/semantic_version/base.py b/semantic_version/base.py index 871ccb0..82a9af0 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -290,7 +290,8 @@ def coerce(cls, version_string, partial=False): @classmethod def parse(cls, version_string, partial=False, coerce=False): - """Parse a version string into a Version() object. + """Parse a version string into a tuple of components: + (major, minor, patch, prerelease, build). Args: version_string (str), the version string to parse From 7dcc42d2a828adbbeb6f8a23cdca40a3c61782bc Mon Sep 17 00:00:00 2001 From: Alex Hogen Date: Wed, 9 Mar 2022 16:38:29 -0800 Subject: [PATCH 41/50] Fix pip install name in README --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 0170dee..c50163a 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ It follows strictly the 2.0.0 version of the SemVer scheme. Links ----- -- Package on `PyPI`_: http://pypi.python.org/pypi/semantic_version/ +- Package on `PyPI`_: https://pypi.org/project/semantic-version/ - Doc on `ReadTheDocs `_: https://python-semanticversion.readthedocs.io/ - Source on `GitHub `_: http://github.com/rbarrois/python-semanticversion/ - Build on Github Actions: https://github.com/rbarrois/python-semanticversion/actions @@ -40,7 +40,7 @@ Install the package from `PyPI`_, using pip: .. code-block:: sh - pip install semantic_version + pip install semantic-version Or from GitHub: From 57c78e7307792879dce33734c11e7774383b9d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 26 May 2022 15:28:23 +0200 Subject: [PATCH 42/50] Guarantee a stable ordering with build metadata Sorting any permutation of Version objects should always yield the same result, even if those hold some build metadata. To that end, the "precedence_key" is now used exclusively for sorting; direct comparisons between Version objects still ignores the "build" metadata, using a different precedence key. For performance improvements, both precedence keys are cached. Closes: #132 --- ChangeLog | 10 +++++++--- docs/reference.rst | 11 ++++++++++- semantic_version/base.py | 42 +++++++++++++++++++++++++++++++++------- tests/test_base.py | 14 ++++++++++++++ 4 files changed, 66 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0d30c7e..110b054 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,14 @@ ChangeLog ========= -2.9.1 (unreleased) ------------------- +2.10.0 (unreleased) +------------------- + +*New:* -- Nothing changed yet. + * `132 `_: + Ensure sorting a collection of versions is always stable, even with + build metadata. 2.9.0 (2022-02-06) diff --git a/docs/reference.rst b/docs/reference.rst index 6d1101b..b2946d9 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -130,7 +130,16 @@ Representing a version (the Version class) The actual value of the attribute is considered an implementation detail; the only guarantee is that ordering versions by their precedence_key will comply with semver precedence rules. - Note that the :attr:`~Version.build` isn't included in the precedence_key computatin. + + .. warning:: + + .. versionchanged:: 2.10.0 + + The :attr:`~Version.build` is included in the precedence_key computation, but + only for ordering stability. + The only guarantee is that, for a given release of python-semanticversion, two versions' + :attr:`~Version.precedence_key` will always compare in the same direction if they include + build metadata; that ordering is an implementation detail and shouldn't be relied upon. .. attribute:: partial diff --git a/semantic_version/base.py b/semantic_version/base.py index 82a9af0..777c27a 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -118,6 +118,12 @@ def __init__( self.partial = partial + # Cached precedence keys + # _cmp_precedence_key is used for semver-precedence comparison + self._cmp_precedence_key = self._build_precedence_key(with_build=False) + # _sort_precedence_key is used for self.precedence_key, esp. for sorted(...) + self._sort_precedence_key = self._build_precedence_key(with_build=True) + @classmethod def _coerce(cls, value, allow_none=False): if value is None and allow_none: @@ -408,11 +414,15 @@ def __hash__(self): # at least a field being `None`. return hash((self.major, self.minor, self.patch, self.prerelease, self.build)) - @property - def precedence_key(self): + def _build_precedence_key(self, with_build=False): + """Build a precedence key. + + The "build" component should only be used when sorting an iterable + of versions. + """ if self.prerelease: prerelease_key = tuple( - NumericIdentifier(part) if re.match(r'^[0-9]+$', part) else AlphaIdentifier(part) + NumericIdentifier(part) if part.isdigit() else AlphaIdentifier(part) for part in self.prerelease ) else: @@ -420,13 +430,31 @@ def precedence_key(self): MaxIdentifier(), ) + if not with_build: + return ( + self.major, + self.minor, + self.patch, + prerelease_key, + ) + + build_key = tuple( + NumericIdentifier(part) if part.isdigit() else AlphaIdentifier(part) + for part in self.build or () + ) + return ( self.major, self.minor, self.patch, prerelease_key, + build_key, ) + @property + def precedence_key(self): + return self._sort_precedence_key + def __cmp__(self, other): if not isinstance(other, self.__class__): return NotImplemented @@ -458,22 +486,22 @@ def __ne__(self, other): def __lt__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return self.precedence_key < other.precedence_key + return self._cmp_precedence_key < other._cmp_precedence_key def __le__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return self.precedence_key <= other.precedence_key + return self._cmp_precedence_key <= other._cmp_precedence_key def __gt__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return self.precedence_key > other.precedence_key + return self._cmp_precedence_key > other._cmp_precedence_key def __ge__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return self.precedence_key >= other.precedence_key + return self._cmp_precedence_key >= other._cmp_precedence_key class SpecItem(object): diff --git a/tests/test_base.py b/tests/test_base.py index 4a844c3..e6a3733 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -229,6 +229,20 @@ def test_invalid_comparisons(self): self.assertTrue(v != '0.1.0') self.assertFalse(v == '0.1.0') + def test_stable_ordering(self): + a = [ + base.Version('0.1.0'), + base.Version('0.1.0+a'), + base.Version('0.1.0+a.1'), + base.Version('0.1.1-a1'), + ] + b = [a[1], a[3], a[0], a[2]] + + self.assertEqual( + sorted(a, key=lambda v: v.precedence_key), + sorted(b, key=lambda v: v.precedence_key), + ) + def test_bump_clean_versions(self): # We Test each property explicitly as the == comparator for versions # does not distinguish between prerelease or builds for equality. From e49b5b065b845cd7798c0219e0fa8986c75f6a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 26 May 2022 15:35:14 +0200 Subject: [PATCH 43/50] Preparing release 2.10.0 --- ChangeLog | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 110b054..586ea8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ ChangeLog ========= -2.10.0 (unreleased) +2.10.0 (2022-05-26) ------------------- *New:* diff --git a/setup.cfg b/setup.cfg index 4bac2c1..7fcd9e3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = semantic_version -version = 2.9.1.dev0 +version = 2.10.0 description = A library implementing the 'SemVer' scheme. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From c2ec5114a7865c5fccd745329ecdc34d912d9b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 26 May 2022 15:35:31 +0200 Subject: [PATCH 44/50] Back to development: 2.10.1 --- ChangeLog | 6 ++++++ setup.cfg | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 586ea8e..d58715d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ChangeLog ========= +2.10.1 (unreleased) +------------------- + +- Nothing changed yet. + + 2.10.0 (2022-05-26) ------------------- diff --git a/setup.cfg b/setup.cfg index 7fcd9e3..3e38471 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = semantic_version -version = 2.10.0 +version = 2.10.1.dev0 description = A library implementing the 'SemVer' scheme. long_description = file: README.rst # https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data From e00fb6da0b0a2cec8fdb8ca89ba78b086157f6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 28 Feb 2023 11:01:01 +0100 Subject: [PATCH 45/50] Test and fix Version.truncate() Calling `Version.truncate("build")` should return a fresh instance, as caught in #141 Closes: #141, #142 --- ChangeLog | 5 ++++- semantic_version/base.py | 9 ++++++++- tests/test_base.py | 10 ++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index d58715d..6c0f593 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,10 @@ ChangeLog 2.10.1 (unreleased) ------------------- -- Nothing changed yet. +*Bugfix:* + + * `141 `_: + Ensure we return a new instance for ``Version.truncate("build")``. 2.10.0 (2022-05-26) diff --git a/semantic_version/base.py b/semantic_version/base.py index 777c27a..1c10155 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -181,7 +181,14 @@ def next_patch(self): def truncate(self, level='patch'): """Return a new Version object, truncated up to the selected level.""" if level == 'build': - return self + return Version( + major=self.major, + minor=self.minor, + patch=self.patch, + prerelease=self.prerelease, + build=self.build, + partial=self.partial, + ) elif level == 'prerelease': return Version( major=self.major, diff --git a/tests/test_base.py b/tests/test_base.py index e6a3733..73d1b08 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -395,6 +395,16 @@ def test_bump_prerelease_versions(self): self.assertEqual(v.prerelease, ()) self.assertEqual(v.build, ()) + def test_truncate(self): + v = base.Version("3.2.1-pre+build") + self.assertEqual(v.truncate("build"), v) + self.assertIsNot(v.truncate("build"), v) + self.assertEqual(v.truncate("prerelease"), base.Version("3.2.1-pre")) + self.assertEqual(v.truncate("patch"), base.Version("3.2.1")) + self.assertEqual(v.truncate(), base.Version("3.2.1")) + self.assertEqual(v.truncate("minor"), base.Version("3.2.0")) + self.assertEqual(v.truncate("major"), base.Version("3.0.0")) + class SpecItemTestCase(unittest.TestCase): if sys.version_info[0] <= 2: From adaed0da56ac5d1fecb08d772cd1b5546044d793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 28 Feb 2023 11:12:52 +0100 Subject: [PATCH 46/50] Update the support matrix - Add Django 4.1 - Add Python 3.11 - Drop old versions of Python --- .github/workflows/test.yml | 34 ++++++---------------------------- setup.cfg | 1 + tox.ini | 12 ++++-------- 3 files changed, 11 insertions(+), 36 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c9918b..2165bc7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,29 +13,7 @@ jobs: fail-fast: false matrix: include: - # Django 1.11: 3.5, 3.7 - - python-version: "2.7" - django-family: 111 - - python-version: "3.5" - django-family: 111 - - python-version: "3.7" - django-family: 111 - - python-version: "pypy3" - django-family: 111 - - # Django 2.2: 3.5, 3.7, 3.8 - - python-version: "3.5" - django-family: 22 - - python-version: "3.7" - django-family: 22 - - python-version: "3.8" - django-family: 22 - - python-version: "pypy3" - django-family: 22 - - # Django 3.2: Python 3.6, 3.8, 3.9, 3.10 - - python-version: "3.6" - django-family: 32 + # Django 3.2: Python 3.8, 3.9, 3.10 - python-version: "3.8" django-family: 32 - python-version: "3.9" @@ -45,13 +23,13 @@ jobs: - python-version: "pypy3" django-family: 32 - # Django 4.0: Python 3.8, 3.9, 3.10 - - python-version: "3.8" - django-family: 40 + # Django 4.1: Python 3.9, 3.10, 3.11 - python-version: "3.9" - django-family: 40 + django-family: 41 - python-version: "3.10" - django-family: 40 + django-family: 41 + - python-version: "3.11" + django-family: 41 env: TOXENV: django${{ matrix.django-family }} diff --git a/setup.cfg b/setup.cfg index 3e38471..46a2673 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Topic :: Software Development :: Libraries :: Python Modules [options] diff --git a/tox.ini b/tox.ini index 26cbebe..77d6d1a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,8 @@ [tox] envlist = - py{27,34,35,36,37}-django111 - py{35,36,37,38}-django22 - py{36,37,38,39,310}-django32 - py{38,39,310}-django40 - pypy3-django{111,22,32} + py{37,38,39,310}-django32 + py{38,39,310,311}-django41 + pypy3-django{32} lint toxworkdir = {env:TOX_WORKDIR:.tox} @@ -12,10 +10,8 @@ toxworkdir = {env:TOX_WORKDIR:.tox} [testenv] extras = dev deps = - django111: Django>=1.11,<1.12 - django22: Django>=2.2,<2.3 django32: Django>=3.2,<3.3 - django40: Django>=4.0a1,<4.1 + django41: Django>=4.1,<4.2 whitelist_externals = make commands = make test From 61dc8432720b5345384d2b8dbd98df0748ff4eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 28 Feb 2023 11:16:35 +0100 Subject: [PATCH 47/50] Update github actions versions --- .github/workflows/check.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index dc16848..2fe3b5e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -19,10 +19,10 @@ jobs: TOXENV: ${{ matrix.tox-environment }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 - name: Install dependencies run: python -m pip install tox diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2165bc7..f69954a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,10 +35,10 @@ jobs: TOXENV: django${{ matrix.django-family }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} From 26f77e3d8b4eab5c7bab58880afd990b61be897f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 28 Feb 2023 11:26:26 +0100 Subject: [PATCH 48/50] Update tox.ini for newer tox versions Keep compatibility with older versions' naming scheme as well. --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 77d6d1a..369cf12 100644 --- a/tox.ini +++ b/tox.ini @@ -13,11 +13,13 @@ deps = django32: Django>=3.2,<3.3 django41: Django>=4.1,<4.2 +allowlist_externals = make whitelist_externals = make commands = make test [testenv:lint] extras = dev +allowlist_externals = make whitelist_externals = make commands = make lint From 9acf55af9d6361a3eac576f9df076507165e8cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 28 Feb 2023 11:30:19 +0100 Subject: [PATCH 49/50] Remove pypy3 from Github Actions matrix --- .github/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f69954a..50eee10 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,8 +20,6 @@ jobs: django-family: 32 - python-version: "3.10" django-family: 32 - - python-version: "pypy3" - django-family: 32 # Django 4.1: Python 3.9, 3.10, 3.11 - python-version: "3.9" From 2cbbee3154d9011cee873ae3a020cd17c669f6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 28 Feb 2023 11:51:59 +0100 Subject: [PATCH 50/50] Simplify subclassing Version Fixes: #112 --- ChangeLog | 6 ++++++ semantic_version/base.py | 24 ++++++++++++------------ tests/test_base.py | 9 +++++++++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6c0f593..2bf2e2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,12 @@ ChangeLog 2.10.1 (unreleased) ------------------- +*Minor:* + + * `112 `_: + Functions returning a new ``Version`` instance reuse the current class, + helping with subclassing. + *Bugfix:* * `141 `_: diff --git a/semantic_version/base.py b/semantic_version/base.py index 1c10155..6be5624 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -132,14 +132,14 @@ def _coerce(cls, value, allow_none=False): def next_major(self): if self.prerelease and self.minor == self.patch == 0: - return Version( + return self.__class__( major=self.major, minor=0, patch=0, partial=self.partial, ) else: - return Version( + return self.__class__( major=self.major + 1, minor=0, patch=0, @@ -148,14 +148,14 @@ def next_major(self): def next_minor(self): if self.prerelease and self.patch == 0: - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=0, partial=self.partial, ) else: - return Version( + return self.__class__( major=self.major, minor=self.minor + 1, patch=0, @@ -164,14 +164,14 @@ def next_minor(self): def next_patch(self): if self.prerelease: - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=self.patch, partial=self.partial, ) else: - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=self.patch + 1, @@ -181,7 +181,7 @@ def next_patch(self): def truncate(self, level='patch'): """Return a new Version object, truncated up to the selected level.""" if level == 'build': - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=self.patch, @@ -190,7 +190,7 @@ def truncate(self, level='patch'): partial=self.partial, ) elif level == 'prerelease': - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=self.patch, @@ -198,21 +198,21 @@ def truncate(self, level='patch'): partial=self.partial, ) elif level == 'patch': - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=self.patch, partial=self.partial, ) elif level == 'minor': - return Version( + return self.__class__( major=self.major, minor=self.minor, patch=None if self.partial else 0, partial=self.partial, ) elif level == 'major': - return Version( + return self.__class__( major=self.major, minor=None if self.partial else 0, patch=None if self.partial else 0, @@ -266,7 +266,7 @@ def coerce(cls, version_string, partial=False): ) if match.end() == len(version_string): - return Version(version, partial=partial) + return cls(version, partial=partial) rest = version_string[match.end():] diff --git a/tests/test_base.py b/tests/test_base.py index 73d1b08..4136045 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -405,6 +405,15 @@ def test_truncate(self): self.assertEqual(v.truncate("minor"), base.Version("3.2.0")) self.assertEqual(v.truncate("major"), base.Version("3.0.0")) + def test_subclass(self): + """Custom subclasses of Version returns instances of themselves.""" + class MyVersion(base.Version): + pass + + v = MyVersion("3.2.1-pre") + subv = v.truncate() + self.assertEqual(type(subv), MyVersion) + class SpecItemTestCase(unittest.TestCase): if sys.version_info[0] <= 2: