Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-looseversion for openSUSE:Factory checked in at 2023-12-09 22:49:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-looseversion (Old) and /work/SRC/openSUSE:Factory/.python-looseversion.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-looseversion" Sat Dec 9 22:49:04 2023 rev:2 rq:1131738 version:1.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-looseversion/python-looseversion.changes 2023-03-21 17:44:26.486666333 +0100 +++ /work/SRC/openSUSE:Factory/.python-looseversion.new.25432/python-looseversion.changes 2023-12-09 22:49:09.518644002 +0100 @@ -1,0 +2,10 @@ +Thu Dec 7 22:42:07 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 1.3.0: + * Restore Python 3 semantics for `LooseVersion`, creating + `LooseVersion2` to restore Python 2 semantics. + * Test on Python 3.12 + * Enable installation on Python 2+ + * Ensure consistent semantics between Python 2 and 3 + +------------------------------------------------------------------- Old: ---- 1.1.2.tar.gz New: ---- 1.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-looseversion.spec ++++++ --- /var/tmp/diff_new_pack.1JHPHk/_old 2023-12-09 22:49:10.686686172 +0100 +++ /var/tmp/diff_new_pack.1JHPHk/_new 2023-12-09 22:49:10.690686316 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-looseversion # -# Copyright (c) 2023 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,17 +12,17 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # Name: python-looseversion -Version: 1.1.2 +Version: 1.3.0 Release: 0 Summary: A backwards/forwards-compatible fork of distutils.version.LooseVersion License: PSF-2.0 Group: Development/Languages/Python -Url: https://github.com/effigies/looseversion +URL: https://github.com/effigies/looseversion Source: https://github.com/effigies/looseversion/archive/refs/tags/%{version}.tar.gz BuildRequires: %{python_module base > 3} BuildRequires: %{python_module hatchling} ++++++ 1.1.2.tar.gz -> 1.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/.github/workflows/package.yml new/looseversion-1.3.0/.github/workflows/package.yml --- old/looseversion-1.1.2/.github/workflows/package.yml 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/.github/workflows/package.yml 2023-07-05 18:05:41.000000000 +0200 @@ -20,12 +20,10 @@ uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - name: Install dev tools - run: pip install --upgrade build twine - name: Build package - run: python -m build + run: pipx run build - name: Check package metadata - run: twine check dist/* + run: pipx run twine check dist/* - name: Save packages uses: actions/upload-artifact@v3 with: @@ -36,13 +34,21 @@ runs-on: ubuntu-latest strategy: matrix: - python: [3.7, 3.8, 3.9, "3.10", "3.11", "pypy-3.7"] + python: + - "3.7" + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "pypy-3.7" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} + allow-prereleases: true - name: Install tox run: pip install --upgrade pip tox tox-gh-actions - name: Test @@ -51,31 +57,20 @@ deploy: needs: [build, test] runs-on: ubuntu-latest + permissions: + # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write steps: - name: Load packages uses: actions/download-artifact@v3 with: name: dist path: dist/ - - name: Check for PyPI tokens - id: deployable - env: - TEST_PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }} - PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} - run: | - if [ -n "$PYPI_API_TOKEN" ]; then echo "DEPLOY=true" >> $GITHUB_OUTPUT; fi - if [ -n "$TEST_PYPI_API_TOKEN" ]; then echo "TEST_DEPLOY=true" >> $GITHUB_OUTPUT; fi - name: Test PyPI upload - if: steps.deployable.outputs.TEST_DEPLOY uses: pypa/gh-action-pypi-publish@release/v1 with: - user: __token__ - password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository_url: https://test.pypi.org/legacy/ skip_existing: true - name: Upload to PyPI (on tags) - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && steps.deployable.outputs.DEPLOY + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/CHANGES.md new/looseversion-1.3.0/CHANGES.md --- old/looseversion-1.1.2/CHANGES.md 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/CHANGES.md 2023-07-05 18:05:41.000000000 +0200 @@ -2,6 +2,19 @@ ## Releases +### 1.3.0 (5 Jul 2023) + +- 2023.07.05 + - Restore Python 3 semantics for `LooseVersion`, creating `LooseVersion2` + to restore Python 2 semantics. + +### 1.2.0 (25 May 2023) + +- 2023.05.25 + - Test on Python 3.12 + - Enable installation on Python 2+ + - Ensure consistent semantics between Python 2 and 3 + ### 1.1.2 (22 Feb 2023) - 2023.02.22 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/pyproject.toml new/looseversion-1.3.0/pyproject.toml --- old/looseversion-1.1.2/pyproject.toml 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/pyproject.toml 2023-07-05 18:05:41.000000000 +0200 @@ -5,7 +5,7 @@ [project] name = "looseversion" maintainers = [{name = "Chris Markiewicz", email = "effig...@gmail.com"}] -version = "1.1.2" +version = "1.3.0" description = "Version numbering for anarchists and software realists" readme = "README.md" license = {file = "LICENSE"} @@ -15,7 +15,6 @@ "License :: OSI Approved :: Python Software Foundation License", ] urls = {Homepage = "https://github.com/effigies/looseversion"} -requires-python = ">=3" [tool.hatch.build] exclude = [".github"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/src/looseversion/__init__.py new/looseversion-1.3.0/src/looseversion/__init__.py --- old/looseversion-1.1.2/src/looseversion/__init__.py 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/src/looseversion/__init__.py 2023-07-05 18:05:41.000000000 +0200 @@ -16,8 +16,6 @@ of the same class or a string (which will be parsed to an instance of the same class, thus must follow the same rules) """ -from __future__ import annotations - import re import sys @@ -86,8 +84,26 @@ # have a conception that matches common notions about version numbers. -class LooseVersion: +if sys.version_info >= (3,): + + class _Py2Int(int): + """Integer object that compares < any string""" + + def __gt__(self, other): + if isinstance(other, str): + return False + return super().__gt__(other) + + def __lt__(self, other): + if isinstance(other, str): + return True + return super().__lt__(other) + +else: + _Py2Int = int + +class LooseVersion(object): """Version numbering for anarchists and software realists. Implements the standard interface for version number classes as described above. A version number consists of a series of numbers, @@ -119,52 +135,48 @@ of "want"). """ - component_re: re.Pattern[str] = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE) - vstring: str - version: list[int | str] + component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE) - def __init__(self, vstring: str | None = None): + def __init__(self, vstring=None): if vstring: self.parse(vstring) - def __eq__(self, other: object) -> bool: + def __eq__(self, other): c = self._cmp(other) if c is NotImplemented: return NotImplemented return c == 0 - def __lt__(self, other: object) -> bool: + def __lt__(self, other): c = self._cmp(other) if c is NotImplemented: return NotImplemented return c < 0 - def __le__(self, other: object) -> bool: + def __le__(self, other): c = self._cmp(other) if c is NotImplemented: return NotImplemented return c <= 0 - def __gt__(self, other: object) -> bool: + def __gt__(self, other): c = self._cmp(other) if c is NotImplemented: return NotImplemented return c > 0 - def __ge__(self, other: object) -> bool: + def __ge__(self, other): c = self._cmp(other) if c is NotImplemented: return NotImplemented return c >= 0 - def parse(self, vstring: str) -> None: + def parse(self, vstring): # I've given up on thinking I can reconstruct the version string # from the parsed tuple -- so I just store the string here for # use by __str__ self.vstring = vstring - components: list[str | int] = [ - x for x in self.component_re.split(vstring) if x and x != "." - ] + components = [x for x in self.component_re.split(vstring) if x and x != "."] for i, obj in enumerate(components): try: components[i] = int(obj) @@ -173,13 +185,13 @@ self.version = components - def __str__(self) -> str: + def __str__(self): return self.vstring - def __repr__(self) -> str: + def __repr__(self): return "LooseVersion ('%s')" % str(self) - def _cmp(self, other: object) -> int: + def _cmp(self, other): other = self._coerce(other) if other is NotImplemented: return NotImplemented @@ -192,12 +204,12 @@ return 1 return NotImplemented - @staticmethod - def _coerce(other: object) -> LooseVersion: - if isinstance(other, LooseVersion): + @classmethod + def _coerce(cls, other): + if isinstance(other, cls): return other elif isinstance(other, str): - return LooseVersion(other) + return cls(other) elif "distutils" in sys.modules: # Using this check to avoid importing distutils and suppressing the warning try: @@ -205,5 +217,27 @@ except ImportError: return NotImplemented if isinstance(other, deprecated): - return LooseVersion(str(other)) + return cls(str(other)) return NotImplemented + + +class LooseVersion2(LooseVersion): + """LooseVersion variant that restores Python 2 semantics + + In Python 2, comparing LooseVersions where paired components could be string + and int always resulted in the string being "greater". In Python 3, this produced + a TypeError. + """ + def parse(self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = [x for x in self.component_re.split(vstring) if x and x != "."] + for i, obj in enumerate(components): + try: + components[i] = _Py2Int(obj) + except ValueError: + pass + + self.version = components diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/src/looseversion/__init__.pyi new/looseversion-1.3.0/src/looseversion/__init__.pyi --- old/looseversion-1.1.2/src/looseversion/__init__.pyi 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/src/looseversion/__init__.pyi 2023-07-05 18:05:41.000000000 +0200 @@ -1,10 +1,10 @@ from re import Pattern -from typing import Union +from typing import List, Union class LooseVersion: component_re: Pattern[str] vstring: str - version: Union[str, int] + version: List[Union[str, int]] def __init__(self, vstring: Union[str, None] = ...) -> None: ... def __eq__(self, other: object) -> bool: ... def __lt__(self, other: object) -> bool: ... @@ -12,3 +12,5 @@ def __gt__(self, other: object) -> bool: ... def __ge__(self, other: object) -> bool: ... def parse(self, vstring: str) -> None: ... + +class LooseVersion2(LooseVersion): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/tests.py new/looseversion-1.3.0/tests.py --- old/looseversion-1.1.2/tests.py 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/tests.py 2023-07-05 18:05:41.000000000 +0200 @@ -16,8 +16,9 @@ @pytest.mark.skipif(not have_distutils, reason="Needs distutils") @pytest.mark.parametrize("v1, v2", [("0.0.0", "0.0.0"), ("0.0.0", "1.0.0")]) -def test_LooseVersion_compat(v1, v2): - vend1, vend2 = lv.LooseVersion(v1), lv.LooseVersion(v2) +@pytest.mark.parametrize("lvtype", [lv.LooseVersion, lv.LooseVersion2]) +def test_LooseVersion_compat(v1, v2, lvtype): + vend1, vend2 = lvtype(v1), lvtype(v2) with warnings.catch_warnings(): warnings.simplefilter("ignore") orig1, orig2 = dv.LooseVersion(v1), dv.LooseVersion(v2) @@ -39,18 +40,23 @@ # Adapted from Cpython:Lib/distutils/tests/test_version.py -@pytest.mark.parametrize("v1,v2,result", - [('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', 1), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', -1), - ('3.2.pl0', '3.1.1.6', 1), - ('2g6', '11g', -1), - ('0.960923', '2.2beta29', -1), - ('1.13++', '5.5.kw', -1)]) -def test_cmp(v1, v2, result): - loosev1 = lv.LooseVersion(v1) - loosev2 = lv.LooseVersion(v2) +@pytest.mark.parametrize( + "v1,v2,result", + [ + ("1.5.1", "1.5.2b2", -1), + ("161", "3.10a", 1), + ("8.02", "8.02", 0), + ("3.4j", "1996.07.12", -1), + ("3.2.pl0", "3.1.1.6", 1), + ("2g6", "11g", -1), + ("0.960923", "2.2beta29", -1), + ("1.13++", "5.5.kw", -1), + ], +) +@pytest.mark.parametrize("lvtype", [lv.LooseVersion, lv.LooseVersion2]) +def test_cmp(v1, v2, result, lvtype): + loosev1 = lvtype(v1) + loosev2 = lvtype(v2) assert loosev1._cmp(loosev2) == result assert loosev1._cmp(v2) == result assert loosev2._cmp(loosev1) == -result @@ -59,22 +65,45 @@ assert loosev2._cmp(object()) == NotImplemented -@pytest.mark.parametrize('vstring,version', +@pytest.mark.parametrize( + "vstring,version", [ - ('1.5.1', [1, 5, 1]), - ('1.5.2b2', [1, 5, 2, 'b', 2]), - ('161', [161]), - ('3.10a', [3, 10, 'a']), - ('1.13++', [1, 13, '++']), + ("1.5.1", [1, 5, 1]), + ("1.5.2b2", [1, 5, 2, "b", 2]), + ("161", [161]), + ("3.10a", [3, 10, "a"]), + ("1.13++", [1, 13, "++"]), ], ) -def test_split(vstring, version): +@pytest.mark.parametrize("lvtype", [lv.LooseVersion, lv.LooseVersion2]) +def test_split(vstring, version, lvtype): # Regression test to ensure we don't accidentally break parsing (again) # This can be changed if the version representation changes - v = lv.LooseVersion(vstring) + v = lvtype(vstring) assert v.vstring == vstring assert v.version == version -if __name__ == '__main__': +@pytest.mark.parametrize( + "v1,v2,result", + [ + ("0.3@v0.3", "0.3.1@v0.3.1", 1), + ("0.3.1@v0.3.1", "0.3@v0.3", -1), + ("13.0-beta3", "13.0.1", 1), + ("13.0.1", "13.0-beta3", -1), + ], +) +def test_py2_rules(v1, v2, result): + """Python 2 did allow strings and numbers to be compared. + Verify consistent, generally unintuitive behavior. + """ + loosev1 = lv.LooseVersion2(v1) + loosev2 = lv.LooseVersion2(v2) + assert loosev1._cmp(loosev2) == result + assert loosev1._cmp(v2) == result + assert loosev2._cmp(loosev1) == -result + assert loosev2._cmp(v1) == -result + + +if __name__ == "__main__": sys.exit(pytest.main([__file__] + sys.argv[1:])) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/looseversion-1.1.2/tox.ini new/looseversion-1.3.0/tox.ini --- old/looseversion-1.1.2/tox.ini 2023-02-22 14:55:05.000000000 +0100 +++ new/looseversion-1.3.0/tox.ini 2023-07-05 18:05:41.000000000 +0200 @@ -1,6 +1,6 @@ [tox] isolated_build = true -envlist = py{36,37,38,39,310,311,py3}, type +envlist = py{36,37,38,39,310,311,312,py3}, type skip_missing_interpreters = True [testenv] @@ -25,4 +25,5 @@ 3.9: py39 3.10: py310 3.11: py311, type + 3.12: py312 pypy-3.7: pypy3