diff --git a/.changes/unreleased/Fixes-20230201-154418.yaml b/.changes/unreleased/Fixes-20230201-154418.yaml new file mode 100644 index 00000000000..dc2099f94b1 --- /dev/null +++ b/.changes/unreleased/Fixes-20230201-154418.yaml @@ -0,0 +1,6 @@ +kind: Fixes +body: Remove pin on packaging and stop using it for prerelease comparisons +time: 2023-02-01T15:44:18.279158-05:00 +custom: + Author: gshank + Issue: "6834" diff --git a/core/dbt/semver.py b/core/dbt/semver.py index 24f00b333a1..f31840f39b7 100644 --- a/core/dbt/semver.py +++ b/core/dbt/semver.py @@ -1,10 +1,7 @@ from dataclasses import dataclass import re -import warnings from typing import List -from packaging import version as packaging_version - from dbt.exceptions import VersionsNotCompatibleError import dbt.utils @@ -70,6 +67,11 @@ class VersionSpecification(dbtClassMixin): _VERSION_REGEX = re.compile(_VERSION_REGEX_PAT_STR, re.VERBOSE) +def _cmp(a, b): + """Return negative if ab.""" + return (a > b) - (a < b) + + @dataclass class VersionSpecifier(VersionSpecification): def to_version_string(self, skip_matcher=False): @@ -142,13 +144,19 @@ def compare(self, other): return 1 if b is None: return -1 - # This suppresses the LegacyVersion deprecation warning - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - if packaging_version.parse(a) > packaging_version.parse(b): + + # Check the prerelease component only + prcmp = self._nat_cmp(a, b) + if prcmp != 0: # either -1 or 1 + return prcmp + # else is equal and will fall through + + else: # major/minor/patch, should all be numbers + if a > b: return 1 - elif packaging_version.parse(a) < packaging_version.parse(b): + elif a < b: return -1 + # else is equal and will fall through equal = ( self.matcher == Matchers.GREATER_THAN_OR_EQUAL @@ -212,6 +220,29 @@ def is_upper_bound(self): def is_exact(self): return self.matcher == Matchers.EXACT + @classmethod + def _nat_cmp(cls, a, b): + def cmp_prerelease_tag(a, b): + if isinstance(a, int) and isinstance(b, int): + return _cmp(a, b) + elif isinstance(a, int): + return -1 + elif isinstance(b, int): + return 1 + else: + return _cmp(a, b) + + a, b = a or "", b or "" + a_parts, b_parts = a.split("."), b.split(".") + a_parts = [int(x) if re.match(r"^\d+$", x) else x for x in a_parts] + b_parts = [int(x) if re.match(r"^\d+$", x) else x for x in b_parts] + for sub_a, sub_b in zip(a_parts, b_parts): + cmp_result = cmp_prerelease_tag(sub_a, sub_b) + if cmp_result != 0: + return cmp_result + else: + return _cmp(len(a), len(b)) + @dataclass class VersionRange: diff --git a/core/setup.py b/core/setup.py index 0ec398f2667..52b29b87dd7 100644 --- a/core/setup.py +++ b/core/setup.py @@ -58,7 +58,7 @@ "minimal-snowplow-tracker==0.0.2", "networkx>=2.3,<2.8.1;python_version<'3.8'", "networkx>=2.3,<3;python_version>='3.8'", - "packaging>=20.9,<22.0", + "packaging>20.9", "sqlparse>=0.2.3,<0.5", "dbt-extractor~=0.4.1", "typing-extensions>=3.7.4", diff --git a/test/unit/test_semver.py b/test/unit/test_semver.py index b36c403e3a7..45e56c18809 100644 --- a/test/unit/test_semver.py +++ b/test/unit/test_semver.py @@ -201,12 +201,16 @@ def test__resolve_to_specific_version(self): '1.1.0') def test__filter_installable(self): - assert filter_installable( + installable = filter_installable( ['1.1.0', '1.2.0a1', '1.0.0','2.1.0-alpha','2.2.0asdf','2.1.0','2.2.0','2.2.0-fishtown-beta','2.2.0-2'], install_prerelease=True - ) == ['1.0.0', '1.1.0', '1.2.0a1','2.1.0-alpha','2.1.0','2.2.0asdf','2.2.0-fishtown-beta','2.2.0-2','2.2.0'] + ) + expected = ['1.0.0', '1.1.0', '1.2.0a1','2.1.0-alpha','2.1.0','2.2.0-2','2.2.0asdf','2.2.0-fishtown-beta','2.2.0'] + assert installable == expected - assert filter_installable( + installable = filter_installable( ['1.1.0', '1.2.0a1', '1.0.0','2.1.0-alpha','2.2.0asdf','2.1.0','2.2.0','2.2.0-fishtown-beta'], install_prerelease=False - ) == ['1.0.0', '1.1.0','2.1.0','2.2.0'] + ) + expected = ['1.0.0', '1.1.0','2.1.0','2.2.0'] + assert installable == expected