From e1ea37642d0fd9bffb865da4122706e7349afec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Thu, 29 Aug 2019 11:31:57 +0200 Subject: Restore Python2 support. --- .travis.yml | 5 +++-- ChangeLog | 4 +++- semantic_version/base.py | 27 +++++++++++++-------------- semantic_version/django_fields.py | 12 ++++++------ setup.py | 3 ++- tests/test_base.py | 39 +++++++++++++++++++++++++++++++++++++++ tests/test_match.py | 7 +++++++ tests/test_npm.py | 7 +++++++ tests/test_parsing.py | 13 +++++++++++++ tox.ini | 2 +- 10 files changed, 94 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index 735e554..258275e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,12 +9,13 @@ install: matrix: include: + - python: "2.7" + env: TOXENV=py27-django111 - python: "3.4" env: TOXENV=py34-django111 - - python: "3.5" - env: TOXENV=py35-django22 - python: "3.6" env: TOXENV=py36-django111 + - python: "3.6" env: TOXENV=py36-django22 # Pypy diff --git a/ChangeLog b/ChangeLog index f85a228..aac3bb7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,9 @@ ChangeLog 2.7.2 (unreleased) ------------------ -- Nothing changed yet. +*New:* + + * Restore support for Python 2. 2.7.1 (2019-08-28) diff --git a/semantic_version/base.py b/semantic_version/base.py index 1c4b01d..d7061c0 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -14,7 +14,7 @@ def _has_leading_zero(value): and value != '0') -class MaxIdentifier: +class MaxIdentifier(object): __slots__ = [] def __repr__(self): @@ -25,7 +25,7 @@ class MaxIdentifier: @functools.total_ordering -class NumericIdentifier: +class NumericIdentifier(object): __slots__ = ['value'] def __init__(self, value): @@ -51,7 +51,7 @@ class NumericIdentifier: @functools.total_ordering -class AlphaIdentifier: +class AlphaIdentifier(object): __slots__ = ['value'] def __init__(self, value): @@ -76,7 +76,7 @@ class AlphaIdentifier: return NotImplemented -class Version: +class Version(object): version_re = re.compile(r'^(\d+)\.(\d+)\.(\d+)(?:-([0-9a-zA-Z.-]+))?(?:\+([0-9a-zA-Z.-]+))?$') partial_version_re = re.compile(r'^(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:-([0-9a-zA-Z.-]*))?(?:\+([0-9a-zA-Z.-]*))?$') @@ -84,7 +84,6 @@ class Version: def __init__( self, version_string=None, - *, major=None, minor=None, patch=None, @@ -404,7 +403,7 @@ class Version: def precedence_key(self): if self.prerelease: prerelease_key = tuple( - NumericIdentifier(part) if part.isdecimal() else AlphaIdentifier(part) + NumericIdentifier(part) if re.match(r'^[0-9]+$', part) else AlphaIdentifier(part) for part in self.prerelease ) else: @@ -468,7 +467,7 @@ class Version: return self.precedence_key >= other.precedence_key -class SpecItem: +class SpecItem(object): """A requirement specification.""" KIND_ANY = '*' @@ -576,7 +575,7 @@ def validate(version_string): DEFAULT_SYNTAX = 'simple' -class BaseSpec: +class BaseSpec(object): """A specification of compatible versions. Usage: @@ -606,7 +605,7 @@ class BaseSpec: return subclass def __init__(self, expression): - super().__init__() + super(BaseSpec, self).__init__() self.expression = expression self.clause = self._parse_to_clause(expression) @@ -659,7 +658,7 @@ class BaseSpec: return '<%s: %r>' % (self.__class__.__name__, self.expression) -class Clause: +class Clause(object): __slots__ = [] def match(self, version): @@ -685,7 +684,7 @@ class AnyOf(Clause): __slots__ = ['clauses'] def __init__(self, *clauses): - super().__init__() + super(AnyOf, self).__init__() self.clauses = frozenset(clauses) def match(self, version): @@ -739,7 +738,7 @@ class AllOf(Clause): __slots__ = ['clauses'] def __init__(self, *clauses): - super().__init__() + super(AllOf, self).__init__() self.clauses = frozenset(clauses) def match(self, version): @@ -878,7 +877,7 @@ class Range(Matcher): __slots__ = ['operator', 'target', 'prerelease_policy', 'build_policy'] def __init__(self, operator, target, prerelease_policy=PRERELEASE_NATURAL, build_policy=BUILD_IMPLICIT): - super().__init__() + super(Range, self).__init__() if target.build and operator not in (self.OP_EQ, self.OP_NEQ): raise ValueError( "Invalid range %s%s: build numbers have no ordering." @@ -1155,7 +1154,7 @@ class LegacySpec(SimpleSpec): stacklevel=2, ) expression = ','.join(expressions) - super().__init__(expression) + super(LegacySpec, self).__init__(expression) def __iter__(self): warnings.warn( diff --git a/semantic_version/django_fields.py b/semantic_version/django_fields.py index db7e606..97f9ce1 100644 --- a/semantic_version/django_fields.py +++ b/semantic_version/django_fields.py @@ -14,7 +14,7 @@ class SemVerField(models.CharField): def __init__(self, *args, **kwargs): kwargs.setdefault('max_length', 200) - super().__init__(*args, **kwargs) + super(SemVerField, self).__init__(*args, **kwargs) def from_db_value(self, value, expression, connection, context): """Convert from the database format. @@ -36,7 +36,7 @@ class SemVerField(models.CharField): return str(value) def run_validators(self, value): - return super().run_validators(str(value)) + return super(SemVerField, self).run_validators(str(value)) class VersionField(SemVerField): @@ -54,11 +54,11 @@ class VersionField(SemVerField): stacklevel=2, ) self.coerce = kwargs.pop('coerce', False) - super().__init__(*args, **kwargs) + super(VersionField, self).__init__(*args, **kwargs) def deconstruct(self): """Handle django.db.migrations.""" - name, path, args, kwargs = super().deconstruct() + name, path, args, kwargs = super(VersionField, self).deconstruct() kwargs['partial'] = self.partial kwargs['coerce'] = self.coerce return name, path, args, kwargs @@ -83,11 +83,11 @@ class SpecField(SemVerField): def __init__(self, *args, **kwargs): self.syntax = kwargs.pop('syntax', base.DEFAULT_SYNTAX) - super().__init__(*args, **kwargs) + super(SpecField, self).__init__(*args, **kwargs) def deconstruct(self): """Handle django.db.migrations.""" - name, path, args, kwargs = super().deconstruct() + name, path, args, kwargs = super(SpecField, self).deconstruct() if self.syntax != base.DEFAULT_SYNTAX: kwargs['syntax'] = self.syntax return name, path, args, kwargs diff --git a/setup.py b/setup.py index 2c52217..7ad8d5f 100755 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ setup( url='https://github.com/rbarrois/python-semanticversion', download_url='http://pypi.python.org/pypi/semantic_version/', packages=['semantic_version'], - python_requires=">=3.4", + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", setup_requires=[ 'setuptools>=0.8', ], @@ -61,6 +61,7 @@ setup( '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', diff --git a/tests/test_base.py b/tests/test_base.py index 5a6497b..cd0b5f5 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -6,6 +6,7 @@ """Test the various functions from 'base'.""" import unittest +import sys from semantic_version import base @@ -13,6 +14,12 @@ from semantic_version import base class TopLevelTestCase(unittest.TestCase): """Test module-level functions.""" + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + versions = ( ('0.1.0', '0.1.1', -1), ('0.1.1', '0.1.1', 0), @@ -89,6 +96,12 @@ class TopLevelTestCase(unittest.TestCase): class VersionTestCase(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + versions = { '1.0.0-alpha': (1, 0, 0, ('alpha',), ()), '1.0.0-alpha.1': (1, 0, 0, ('alpha', '1'), ()), @@ -199,6 +212,7 @@ class VersionTestCase(unittest.TestCase): ])) ) + @unittest.skipIf(sys.version_info[0] <= 2, "Comparisons don't raise TypeError in Python 2") def test_invalid_comparisons(self): v = base.Version('0.1.0') with self.assertRaises(TypeError): @@ -367,6 +381,12 @@ class VersionTestCase(unittest.TestCase): class SpecItemTestCase(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + invalids = [ '<=0.1.1+build3', '<=0.1.1+', @@ -540,6 +560,12 @@ class SpecItemTestCase(unittest.TestCase): class CoerceTestCase(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + examples = { # Dict of target: [list of equivalents] '0.0.0': ('0', '0.0', '0.0.0', '0.0.0+', '0-'), @@ -564,6 +590,19 @@ class CoerceTestCase(unittest.TestCase): 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), + ) + examples = { '>=0.1.1,<0.1.2': ['>=0.1.1', '<0.1.2'], '>=0.1.0,!=0.1.3-rc1,<0.1.3': ['>=0.1.0', '!=0.1.3-rc1', '<0.1.3'], diff --git a/tests/test_match.py b/tests/test_match.py index 4bf7162..f807828 100755 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -4,11 +4,18 @@ # This code is distributed under the two-clause BSD License. import unittest +import sys import semantic_version class MatchTestCase(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + invalid_specs = [ '', '!0.1', diff --git a/tests/test_npm.py b/tests/test_npm.py index 86cbc76..7bed337 100644 --- a/tests/test_npm.py +++ b/tests/test_npm.py @@ -6,11 +6,18 @@ """Test NPM-style specifications.""" import unittest +import sys from semantic_version import base class NpmSpecTests(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + examples = { # range: [matchings], [failings] '>=1.2.7': ( diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 0da679f..01b7b5e 100755 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -5,11 +5,18 @@ import itertools import unittest +import sys import semantic_version class ParsingTestCase(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + invalids = [ None, '', @@ -66,6 +73,12 @@ class ParsingTestCase(unittest.TestCase): class ComparisonTestCase(unittest.TestCase): + if sys.version_info[0] <= 2: + import contextlib + @contextlib.contextmanager + def subTest(self, **kwargs): + yield + order = [ '1.0.0-alpha', '1.0.0-alpha.1', diff --git a/tox.ini b/tox.ini index 9ec0fb3..2841dbb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{34,35,36,37}-django111 + py{27,34,35,36,37}-django111 py{35,36,37}-django22 pypy3-django{111,22} lint -- cgit v1.2.1