Hello community, here is the log from the commit of package python-semver for openSUSE:Factory checked in at 2020-11-06 23:45:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-semver (Old) and /work/SRC/openSUSE:Factory/.python-semver.new.11331 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-semver" Fri Nov 6 23:45:13 2020 rev:14 rq:846445 version:2.13.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-semver/python-semver.changes 2020-07-20 21:01:32.209102987 +0200 +++ /work/SRC/openSUSE:Factory/.python-semver.new.11331/python-semver.changes 2020-11-06 23:46:12.483135840 +0100 @@ -1,0 +2,22 @@ +Fri Oct 30 11:22:16 UTC 2020 - Sebastian Wagner <sebix+novell....@sebix.at> + +- update to version 2.13.0: + - Features: + - :pr:`287`: Document how to create subclass from ``VersionInfo`` + - Bug Fixes: + - :pr:`283`: Ensure equal versions have equal hashes. + Version equality means for semver, that ``major``, + ``minor``, ``patch``, and ``prerelease`` parts are + equal in both versions you compare. The ``build`` part + is ignored. +- update to version 2.12.0: + - Bug Fixes: + - :gh:`291` (:pr:`292`): Disallow negative numbers of + major, minor, and patch for ``semver.VersionInfo`` +- update to version 2.11.0: + - Bug Fixes: + - :gh:`276` (:pr:`277`): VersionInfo.parse should be a class method + Also add authors and update changelog in :gh:`286` + - :gh:`274` (:pr:`275`): Py2 vs. Py3 incompatibility TypeError + +------------------------------------------------------------------- Old: ---- semver-2.10.2.tar.gz New: ---- semver-2.13.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-semver.spec ++++++ --- /var/tmp/diff_new_pack.vUqq8V/_old 2020-11-06 23:46:13.143134572 +0100 +++ /var/tmp/diff_new_pack.vUqq8V/_new 2020-11-06 23:46:13.147134564 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without test Name: python-semver -Version: 2.10.2 +Version: 2.13.0 Release: 0 Summary: Python helper for Semantic Versioning License: BSD-3-Clause ++++++ semver-2.10.2.tar.gz -> semver-2.13.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/CHANGELOG.rst new/semver-2.13.0/CHANGELOG.rst --- old/semver-2.10.2/CHANGELOG.rst 2020-06-15 20:33:06.000000000 +0200 +++ new/semver-2.13.0/CHANGELOG.rst 2020-10-20 22:09:30.000000000 +0200 @@ -6,6 +6,90 @@ All notable changes to this code base will be documented in this file, in every released version. +Version 2.13.0 +============== + +:Released: 2020-10-20 +:Maintainer: Tom Schraitle + +Features +-------- + +* :pr:`287`: Document how to create subclass from ``VersionInfo`` + + +Bug Fixes +--------- + +* :pr:`283`: Ensure equal versions have equal hashes. + Version equality means for semver, that ``major``, + ``minor``, ``patch``, and ``prerelease`` parts are + equal in both versions you compare. The ``build`` part + is ignored. + + +Version 2.12.0 +============== + +:Released: 2020-10-19 +:Maintainer: Tom Schraitle + +Features +-------- + +n/a + + +Bug Fixes +--------- + +* :gh:`291` (:pr:`292`): Disallow negative numbers of + major, minor, and patch for ``semver.VersionInfo`` + + +Additions +--------- + +n/a + + +Deprecations +------------ + +n/a + + +Version 2.11.0 +============== + +:Released: 2020-10-17 +:Maintainer: Tom Schraitle + +Features +-------- + +n/a + + +Bug Fixes +--------- + +* :gh:`276` (:pr:`277`): VersionInfo.parse should be a class method + Also add authors and update changelog in :gh:`286` +* :gh:`274` (:pr:`275`): Py2 vs. Py3 incompatibility TypeError + + +Additions +--------- + +n/a + + +Deprecations +------------ + +n/a + Version 2.10.2 ============== @@ -22,8 +106,8 @@ Bug Fixes --------- -:gh:`260` (:pr:`261`): Fixed ``__getitem__`` returning None on wrong parts -:pr:`263`: Doc: Add missing "install" subcommand for openSUSE +* :gh:`260` (:pr:`261`): Fixed ``__getitem__`` returning None on wrong parts +* :pr:`263`: Doc: Add missing "install" subcommand for openSUSE Additions @@ -32,7 +116,7 @@ n/a Deprecations --------- +------------ * :gh:`160` (:pr:`264`): * :func:`semver.max_ver` @@ -92,7 +176,7 @@ Deprecations --------- +------------ * :gh:`225` (:pr:`229`): Output a DeprecationWarning for the following functions: - ``semver.parse`` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/CONTRIBUTORS new/semver-2.13.0/CONTRIBUTORS --- old/semver-2.10.2/CONTRIBUTORS 2020-05-10 17:18:24.000000000 +0200 +++ new/semver-2.13.0/CONTRIBUTORS 2020-10-20 22:09:30.000000000 +0200 @@ -27,12 +27,15 @@ * Ben Finney <ben+pyt...@benfinney.id.au> * Carles Barrobés <car...@barrobes.com> * Craig Blaszczyk <masterja...@gmail.com> +* Damien Nadé <an...@users.noreply.github.com> +* Eli Bishop <eli-dar...@users.noreply.github.com> * George Sakkis <gsak...@users.noreply.github.com> * Jan Pieter Waagmeester <jie...@jieter.nl> * Jelo Agnasin <j...@icannhas.com> * Karol Werner <karol.wer...@ppkt.eu> * Peter Bittner <dja...@bittner.it> * robi-wan <robi-...@suyu.de> +* sbrudenell <sbruden...@users.noreply.github.com> * T. Jameson Little <t.jameson.lit...@gmail.com> * Tom Schraitle <tom_s...@web.de> * Thomas Laferriere <tlaferri...@users.noreply.github.com> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/PKG-INFO new/semver-2.13.0/PKG-INFO --- old/semver-2.10.2/PKG-INFO 2020-06-15 20:33:56.000000000 +0200 +++ new/semver-2.13.0/PKG-INFO 2020-10-20 22:12:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: semver -Version: 2.10.2 +Version: 2.13.0 Summary: Python helper for Semantic Versioning (http://semver.org/) Home-page: https://github.com/python-semver/python-semver Author: Kostiantyn Rybnikov diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/conftest.py new/semver-2.13.0/conftest.py --- old/semver-2.10.2/conftest.py 2020-04-26 10:46:39.000000000 +0200 +++ new/semver-2.13.0/conftest.py 2020-10-20 20:00:05.000000000 +0200 @@ -5,9 +5,11 @@ sys.path.insert(0, "docs") from coerce import coerce # noqa:E402 +from semverwithvprefix import SemVerWithVPrefix @pytest.fixture(autouse=True) def add_semver(doctest_namespace): doctest_namespace["semver"] = semver doctest_namespace["coerce"] = coerce + doctest_namespace["SemVerWithVPrefix"] = SemVerWithVPrefix diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/docs/development.rst new/semver-2.13.0/docs/development.rst --- old/semver-2.10.2/docs/development.rst 2020-04-26 10:46:39.000000000 +0200 +++ new/semver-2.13.0/docs/development.rst 2020-10-16 14:18:45.000000000 +0200 @@ -154,8 +154,9 @@ * **A docstring** - Each docstring contains a summary line, a linebreak, the description - of its arguments in `Sphinx style`_, and an optional doctest. + Each docstring contains a summary line, a linebreak, an optional + directive (see next item), the description of its arguments in + `Sphinx style`_, and an optional doctest. The docstring is extracted and reused in the :ref:`api` section. An appropriate docstring should look like this:: @@ -177,6 +178,43 @@ """ + * **An optional directive** + + If you introduce a new feature, change a function/method, or remove something, + it is a good practice to introduce Sphinx directives into the docstring. + This gives the reader an idea what version is affected by this change. + + The first required argument, ``VERSION``, defines the version when this change + was introduced. You can choose from: + + * ``.. versionadded:: VERSION`` + + Use this directive to describe a new feature. + + * ``.. versionchanged:: VERSION`` + + Use this directive to describe when something has changed, for example, + new parameters were added, changed side effects, different return values, etc. + + * ``.. deprecated:: VERSION`` + + Use this directive when a feature is deprecated. Describe what should + be used instead, if appropriate. + + + Add such a directive *after* the summary line, if needed. + An appropriate directive could look like this:: + + def to_tuple(self): + """ + Convert the VersionInfo object to a tuple. + + .. versionadded:: 2.10.0 + Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to + make this function available in the public API. + [...] + """ + * **The documentation** A docstring is good, but in most cases it's too dense. Describe how diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/docs/semverwithvprefix.py new/semver-2.13.0/docs/semverwithvprefix.py --- old/semver-2.10.2/docs/semverwithvprefix.py 1970-01-01 01:00:00.000000000 +0100 +++ new/semver-2.13.0/docs/semverwithvprefix.py 2020-10-20 20:00:05.000000000 +0200 @@ -0,0 +1,31 @@ +from semver import VersionInfo + + +class SemVerWithVPrefix(VersionInfo): + """ + A subclass of VersionInfo which allows a "v" prefix + """ + + @classmethod + def parse(cls, version): + """ + Parse version string to a VersionInfo instance. + + :param version: version string with "v" or "V" prefix + :type version: str + :raises ValueError: when version does not start with "v" or "V" + :return: a new instance + :rtype: :class:`SemVerWithVPrefix` + """ + if not version[0] in ("v", "V"): + raise ValueError( + "{v!r}: not a valid semantic version tag. Must start with 'v' or 'V'".format( + v=version + ) + ) + self = super(SemVerWithVPrefix, cls).parse(version[1:]) + return self + + def __str__(self): + # Reconstruct the tag + return "v" + super(SemVerWithVPrefix, self).__str__() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/docs/usage.rst new/semver-2.13.0/docs/usage.rst --- old/semver-2.10.2/docs/usage.rst 2020-06-15 17:33:05.000000000 +0200 +++ new/semver-2.13.0/docs/usage.rst 2020-10-20 20:00:05.000000000 +0200 @@ -45,10 +45,17 @@ A :class:`semver.VersionInfo` instance can be created in different ways: -* From a string:: +* From a string (a Unicode string in Python 2):: >>> semver.VersionInfo.parse("3.4.5-pre.2+build.4") VersionInfo(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') + >>> semver.VersionInfo.parse(u"5.3.1") + VersionInfo(major=5, minor=3, patch=1, prerelease=None, build=None) + +* From a byte string:: + + >>> semver.VersionInfo.parse(b"2.3.4") + VersionInfo(major=2, minor=3, patch=4, prerelease=None, build=None) * From individual parts by a dictionary:: @@ -56,6 +63,14 @@ >>> semver.VersionInfo(**d) VersionInfo(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4') + Keep in mind, the ``major``, ``minor``, ``patch`` parts has to + be positive. + + >>> semver.VersionInfo(-1) + Traceback (most recent call last): + ... + ValueError: 'major' is negative. A version can only be positive. + As a minimum requirement, your dictionary needs at least the ``major`` key, others can be omitted. You get a ``TypeError`` if your dictionary contains invalid keys. @@ -452,6 +467,32 @@ (for examples, see :ref:`sec_max_min`) and :func:`sorted`. +Determining Version Equality +---------------------------- + +Version equality means for semver, that major, minor, patch, and prerelease +parts are equal in both versions you compare. The build part is ignored. +For example:: + + >>> v = semver.VersionInfo.parse("1.2.3-rc4+1e4664d") + >>> v == "1.2.3-rc4+dedbeef" + True + +This also applies when a :class:`semver.VersionInfo` is a member of a set, or a +dictionary key:: + + >>> d = {} + >>> v1 = semver.VersionInfo.parse("1.2.3-rc4+1e4664d") + >>> v2 = semver.VersionInfo.parse("1.2.3-rc4+dedbeef") + >>> d[v1] = 1 + >>> d[v2] + 1 + >>> s = set() + >>> s.add(v1) + >>> v2 in s + True + + Comparing Versions through an Expression ---------------------------------------- @@ -530,6 +571,8 @@ '1.0.0' +.. _sec_dealing_with_invalid_versions: + Dealing with Invalid Versions ----------------------------- @@ -708,3 +751,39 @@ For further details, see the section `Overriding the default filter <https://docs.python.org/3/library/warnings.html#overriding-the-default-filter>`_ of the Python documentation. + + +.. _sec_creating_subclasses_from_versioninfo: + +Creating Subclasses from VersionInfo +------------------------------------ + +If you do not like creating functions to modify the behavior of semver +(as shown in section :ref:`sec_dealing_with_invalid_versions`), you can +also create a subclass of the :class:`VersionInfo` class. + +For example, if you want to output a "v" prefix before a version, +but the other behavior is the same, use the following code: + +.. literalinclude:: semverwithvprefix.py + :language: python + :lines: 4- + + +The derived class :class:`SemVerWithVPrefix` can be used like +the original class: + +.. code-block:: python + + >>> v1 = SemVerWithVPrefix.parse("v1.2.3") + >>> assert str(v1) == "v1.2.3" + >>> print(v1) + v1.2.3 + >>> v2 = SemVerWithVPrefix.parse("v2.3.4") + >>> v2 > v1 + True + >>> bad = SemVerWithVPrefix.parse("1.2.4") + Traceback (most recent call last): + ... + ValueError: '1.2.4': not a valid semantic version tag. Must start with 'v' or 'V' + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/semver.egg-info/PKG-INFO new/semver-2.13.0/semver.egg-info/PKG-INFO --- old/semver-2.10.2/semver.egg-info/PKG-INFO 2020-06-15 20:33:56.000000000 +0200 +++ new/semver-2.13.0/semver.egg-info/PKG-INFO 2020-10-20 22:12:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: semver -Version: 2.10.2 +Version: 2.13.0 Summary: Python helper for Semantic Versioning (http://semver.org/) Home-page: https://github.com/python-semver/python-semver Author: Kostiantyn Rybnikov diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/semver.egg-info/SOURCES.txt new/semver-2.13.0/semver.egg-info/SOURCES.txt --- old/semver-2.10.2/semver.egg-info/SOURCES.txt 2020-06-15 20:33:56.000000000 +0200 +++ new/semver-2.13.0/semver.egg-info/SOURCES.txt 2020-10-20 22:12:30.000000000 +0200 @@ -13,6 +13,7 @@ setup.cfg setup.py test_semver.py +test_typeerror-274.py tox.ini docs/Makefile docs/api.rst @@ -27,6 +28,7 @@ docs/pysemver.rst docs/readme.rst docs/requirements.txt +docs/semverwithvprefix.py docs/usage.rst docs/_static/css/default.css semver.egg-info/PKG-INFO diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/semver.py new/semver-2.13.0/semver.py --- old/semver-2.10.2/semver.py 2020-06-15 18:41:10.000000000 +0200 +++ new/semver-2.13.0/semver.py 2020-10-20 22:09:30.000000000 +0200 @@ -10,7 +10,11 @@ import warnings -__version__ = "2.10.2" +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + + +__version__ = "2.13.0" __author__ = "Kostiantyn Rybnikov" __author_email__ = "k...@k-bx.com" __maintainer__ = ["Sebastien Celles", "Tom Schraitle"] @@ -60,6 +64,53 @@ return (a > b) - (a < b) +if PY3: # pragma: no cover + string_types = str, bytes + text_type = str + binary_type = bytes + + def b(s): + return s.encode("latin-1") + + def u(s): + return s + + +else: # pragma: no cover + string_types = unicode, str + text_type = unicode + binary_type = str + + def b(s): + return s + + # Workaround for standalone backslash + def u(s): + return unicode(s.replace(r"\\", r"\\\\"), "unicode_escape") + + +def ensure_str(s, encoding="utf-8", errors="strict"): + # Taken from six project + """ + Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + if PY2 and isinstance(s, text_type): + s = s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + s = s.decode(encoding, errors) + return s + + def deprecated(func=None, replace=None, version=None, category=DeprecationWarning): """ Decorates a function to output a deprecation warning. @@ -144,7 +195,7 @@ @wraps(operator) def wrapper(self, other): - comparable_types = (VersionInfo, dict, tuple, list, str) + comparable_types = (VersionInfo, dict, tuple, list, text_type, binary_type) if not isinstance(other, comparable_types): raise TypeError( "other type %r must be in %r" % (type(other), comparable_types) @@ -192,9 +243,24 @@ ) def __init__(self, major, minor=0, patch=0, prerelease=None, build=None): - self._major = int(major) - self._minor = int(minor) - self._patch = int(patch) + # Build a dictionary of the arguments except prerelease and build + version_parts = { + "major": major, + "minor": minor, + "patch": patch, + } + + for name, value in version_parts.items(): + value = int(value) + version_parts[name] = value + if value < 0: + raise ValueError( + "{!r} is negative. A version can only be positive.".format(name) + ) + + self._major = version_parts["major"] + self._minor = version_parts["minor"] + self._patch = version_parts["patch"] self._prerelease = None if prerelease is None else str(prerelease) self._build = None if build is None else str(build) @@ -423,7 +489,7 @@ 0 """ cls = type(self) - if isinstance(other, str): + if isinstance(other, string_types): other = cls.parse(other) elif isinstance(other, dict): other = cls(**other) @@ -575,7 +641,7 @@ return version def __hash__(self): - return hash(self.to_tuple()) + return hash(self.to_tuple()[:4]) def finalize_version(self): """ @@ -637,8 +703,8 @@ return cmp_res in possibilities - @staticmethod - def parse(version): + @classmethod + def parse(cls, version): """ Parse version string to a VersionInfo instance. @@ -647,11 +713,15 @@ :raises: :class:`ValueError` :rtype: :class:`VersionInfo` + .. versionchanged:: 2.11.0 + Changed method from static to classmethod to + allow subclasses. + >>> semver.VersionInfo.parse('3.4.5-pre.2+build.4') VersionInfo(major=3, minor=4, patch=5, \ prerelease='pre.2', build='build.4') """ - match = VersionInfo._REGEX.match(version) + match = cls._REGEX.match(ensure_str(version)) if match is None: raise ValueError("%s is not valid SemVer string" % version) @@ -661,7 +731,7 @@ version_parts["minor"] = int(version_parts["minor"]) version_parts["patch"] = int(version_parts["patch"]) - return VersionInfo(**version_parts) + return cls(**version_parts) def replace(self, **parts): """ @@ -825,7 +895,7 @@ >>> semver.max_ver("1.0.0", "2.0.0") '2.0.0' """ - if isinstance(ver1, str): + if isinstance(ver1, string_types): ver1 = VersionInfo.parse(ver1) elif not isinstance(ver1, VersionInfo): raise TypeError() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/setup.cfg new/semver-2.13.0/setup.cfg --- old/semver-2.10.2/setup.cfg 2020-06-15 20:33:56.000000000 +0200 +++ new/semver-2.13.0/setup.cfg 2020-10-20 22:12:30.000000000 +0200 @@ -13,6 +13,7 @@ [flake8] max-line-length = 88 +ignore = F821,W503 exclude = .env, .eggs, @@ -21,6 +22,8 @@ __pycache__, build, dist + docs + conftest.py [egg_info] tag_build = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/test_semver.py new/semver-2.13.0/test_semver.py --- old/semver-2.10.2/test_semver.py 2020-06-15 17:33:05.000000000 +0200 +++ new/semver-2.13.0/test_semver.py 2020-10-20 20:00:05.000000000 +0200 @@ -74,6 +74,20 @@ @pytest.mark.parametrize( + "ver", + [ + {"major": -1}, + {"major": 1, "minor": -2}, + {"major": 1, "minor": 2, "patch": -3}, + {"major": 1, "minor": -2, "patch": 3}, + ], +) +def test_should_not_allow_negative_numbers(ver): + with pytest.raises(ValueError, match=".* is negative. .*"): + VersionInfo(**ver) + + +@pytest.mark.parametrize( "version,expected", [ # no. 1 @@ -679,6 +693,20 @@ d[v] = "" # to ensure that VersionInfo are hashable +def test_equal_versions_have_equal_hashes(): + v1 = parse_version_info("1.2.3-alpha.1.2+build.11.e0f985a") + v2 = parse_version_info("1.2.3-alpha.1.2+build.22.a589f0e") + assert v1 == v2 + assert hash(v1) == hash(v2) + d = {} + d[v1] = 1 + d[v2] = 2 + assert d[v1] == 2 + s = set() + s.add(v1) + assert v2 in s + + def test_parse_method_for_version_info(): s_version = "1.2.3-alpha.1.2+build.11.e0f985a" v = VersionInfo.parse(s_version) @@ -1128,3 +1156,21 @@ ) def test_repr(version, expected): assert repr(version) == expected + + +def test_subclass_from_versioninfo(): + class SemVerWithVPrefix(VersionInfo): + @classmethod + def parse(cls, version): + if not version[0] in ("v", "V"): + raise ValueError( + "{v!r}: version must start with 'v' or 'V'".format(v=version) + ) + return super(SemVerWithVPrefix, cls).parse(version[1:]) + + def __str__(self): + # Reconstruct the tag. + return "v" + super(SemVerWithVPrefix, self).__str__() + + v = SemVerWithVPrefix.parse("v1.2.3") + assert str(v) == "v1.2.3" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/semver-2.10.2/test_typeerror-274.py new/semver-2.13.0/test_typeerror-274.py --- old/semver-2.10.2/test_typeerror-274.py 1970-01-01 01:00:00.000000000 +0100 +++ new/semver-2.13.0/test_typeerror-274.py 2020-10-17 13:50:57.000000000 +0200 @@ -0,0 +1,102 @@ +import pytest +import sys + +import semver + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + + +def ensure_binary(s, encoding="utf-8", errors="strict"): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, semver.text_type): + return s.encode(encoding, errors) + elif isinstance(s, semver.binary_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def test_should_work_with_string_and_unicode(): + result = semver.compare(semver.u("1.1.0"), semver.b("1.2.2")) + assert result == -1 + result = semver.compare(semver.b("1.1.0"), semver.u("1.2.2")) + assert result == -1 + + +class TestEnsure: + # From six project + # grinning face emoji + UNICODE_EMOJI = semver.u("\U0001F600") + BINARY_EMOJI = b"\xf0\x9f\x98\x80" + + def test_ensure_binary_raise_type_error(self): + with pytest.raises(TypeError): + semver.ensure_str(8) + + def test_errors_and_encoding(self): + ensure_binary(self.UNICODE_EMOJI, encoding="latin-1", errors="ignore") + with pytest.raises(UnicodeEncodeError): + ensure_binary(self.UNICODE_EMOJI, encoding="latin-1", errors="strict") + + def test_ensure_binary_raise(self): + converted_unicode = ensure_binary( + self.UNICODE_EMOJI, encoding="utf-8", errors="strict" + ) + converted_binary = ensure_binary( + self.BINARY_EMOJI, encoding="utf-8", errors="strict" + ) + if semver.PY2: + # PY2: unicode -> str + assert converted_unicode == self.BINARY_EMOJI and isinstance( + converted_unicode, str + ) + # PY2: str -> str + assert converted_binary == self.BINARY_EMOJI and isinstance( + converted_binary, str + ) + else: + # PY3: str -> bytes + assert converted_unicode == self.BINARY_EMOJI and isinstance( + converted_unicode, bytes + ) + # PY3: bytes -> bytes + assert converted_binary == self.BINARY_EMOJI and isinstance( + converted_binary, bytes + ) + + def test_ensure_str(self): + converted_unicode = semver.ensure_str( + self.UNICODE_EMOJI, encoding="utf-8", errors="strict" + ) + converted_binary = semver.ensure_str( + self.BINARY_EMOJI, encoding="utf-8", errors="strict" + ) + if PY2: + # PY2: unicode -> str + assert converted_unicode == self.BINARY_EMOJI and isinstance( + converted_unicode, str + ) + # PY2: str -> str + assert converted_binary == self.BINARY_EMOJI and isinstance( + converted_binary, str + ) + else: + # PY3: str -> str + assert converted_unicode == self.UNICODE_EMOJI and isinstance( + converted_unicode, str + ) + # PY3: bytes -> str + assert converted_binary == self.UNICODE_EMOJI and isinstance( + converted_unicode, str + )