From 88d0210399333806d59c06c5e8d2b675072b2614 Mon Sep 17 00:00:00 2001 From: Alexandre Harano Date: Sat, 1 Jul 2023 01:18:04 -0300 Subject: [PATCH 1/4] Replace FastAPI pure str comparison with SemVer comparison --- rollbar/contrib/fastapi/utils.py | 38 +++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/rollbar/contrib/fastapi/utils.py b/rollbar/contrib/fastapi/utils.py index 74186133..e8d4fe19 100644 --- a/rollbar/contrib/fastapi/utils.py +++ b/rollbar/contrib/fastapi/utils.py @@ -21,6 +21,39 @@ def __init__(self, version, reason=''): return super().__init__(err_msg) +def is_current_version_higher_or_equal(current_version, min_version): + if current_version == min_version: + return True + + for current_as_string, min_as_string in zip( + current_version.split('.'), + min_version.split('.'), + ): + if current_as_string == min_as_string: + continue + + try: + current_as_int = int(current_as_string) + except ValueError: + current_as_int = None + + try: + min_as_int = int(min_as_string) + except ValueError: + min_as_int = None + + if current_as_int is None or min_as_int is None: + # If one of the parts fails the int conversion, compare as string + return current_as_string > min_as_string + else: + if current_as_int == min_as_int: + continue + return current_as_int > min_as_int + + # Somehow the comparison didn't properly finish - defaulting to False + return False + + class fastapi_min_version: def __init__(self, min_version): self.min_version = min_version @@ -28,7 +61,10 @@ def __init__(self, min_version): def __call__(self, func): @functools.wraps(func) def wrapper(*args, **kwargs): - if fastapi.__version__ < self.min_version: + if not is_current_version_higher_or_equal( + fastapi.__version__, + self.min_version, + ): raise FastAPIVersionError( self.min_version, reason=f'to use {func.__name__}() function' ) From 4c7fd12c4af3c8dee50eebf33a38796615914b46 Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Fri, 4 Aug 2023 17:26:34 -0500 Subject: [PATCH 2/4] Added unit tests SemVer comparison. --- rollbar/test/fastapi_tests/test_utils.py | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/rollbar/test/fastapi_tests/test_utils.py b/rollbar/test/fastapi_tests/test_utils.py index db14e126..339f27fe 100644 --- a/rollbar/test/fastapi_tests/test_utils.py +++ b/rollbar/test/fastapi_tests/test_utils.py @@ -121,3 +121,33 @@ async def read_root(): app.include_router(router) self.assertFalse(has_bare_routing(app)) + + +class UtilsVersionCompareTest(BaseTest): + def test_is_current_version_higher_or_equal(self): + # Copied from https://semver.org/#spec-item-11 + versions = [ + '1.0.0-alpha', + '1.0.0-alpha.1', + '1.0.0-alpha.beta', + '1.0.0-beta', + '1.0.0-beta.2', + '1.0.0-beta.11', + '1.0.0-rc.1', + '1.0.0', + '1.1.1', + '1.100.0-beta2', + '1.100.0-beta3', + ] + + from rollbar.contrib.fastapi.utils import is_current_version_higher_or_equal + + previous_version = None + for version in versions: + print(f'{version} >= {previous_version}') + if previous_version is None: + previous_version = version + continue + with self.subTest(f'{version} >= {previous_version}'): + self.assertTrue(is_current_version_higher_or_equal(version, previous_version)) + previous_version = version From 1b65584ddf859b1ef8685169f544445097122b76 Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Fri, 1 Sep 2023 16:54:52 -0500 Subject: [PATCH 3/4] Updated the version comparison to only use the release segment. --- rollbar/contrib/fastapi/utils.py | 54 ++++++++++++++------------------ 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/rollbar/contrib/fastapi/utils.py b/rollbar/contrib/fastapi/utils.py index e8d4fe19..db4902ec 100644 --- a/rollbar/contrib/fastapi/utils.py +++ b/rollbar/contrib/fastapi/utils.py @@ -22,36 +22,30 @@ def __init__(self, version, reason=''): def is_current_version_higher_or_equal(current_version, min_version): - if current_version == min_version: - return True - - for current_as_string, min_as_string in zip( - current_version.split('.'), - min_version.split('.'), - ): - if current_as_string == min_as_string: - continue - - try: - current_as_int = int(current_as_string) - except ValueError: - current_as_int = None - - try: - min_as_int = int(min_as_string) - except ValueError: - min_as_int = None - - if current_as_int is None or min_as_int is None: - # If one of the parts fails the int conversion, compare as string - return current_as_string > min_as_string - else: - if current_as_int == min_as_int: - continue - return current_as_int > min_as_int - - # Somehow the comparison didn't properly finish - defaulting to False - return False + """ + Compare two version strings and return True if the current version is higher or equal to the minimum version. + + Note: This function only compares the release segment of the version string. + """ + def parse_version(version): + """Parse the release segment of a version string into a list of strings.""" + parsed = [''] + current_segment = 0 + for c in version: + if c.isdigit(): + parsed[current_segment] += c + elif c == '.': + current_segment += 1 + parsed.append('') + else: + break + if parsed[-1] == '': + parsed.pop() + return parsed + + current = tuple(map(int, parse_version(current_version))) + minimum = tuple(map(int, parse_version(min_version))) + return current >= minimum class fastapi_min_version: From 8af20cf492810d1045bcc0dcff4c937247658491 Mon Sep 17 00:00:00 2001 From: Daniel Morell Date: Fri, 1 Sep 2023 16:58:06 -0500 Subject: [PATCH 4/4] Fixed FastAPI tests running when FastAPI not present. --- rollbar/test/fastapi_tests/test_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rollbar/test/fastapi_tests/test_utils.py b/rollbar/test/fastapi_tests/test_utils.py index 339f27fe..549e9b8c 100644 --- a/rollbar/test/fastapi_tests/test_utils.py +++ b/rollbar/test/fastapi_tests/test_utils.py @@ -122,7 +122,9 @@ async def read_root(): self.assertFalse(has_bare_routing(app)) - +@unittest.skipUnless( + FASTAPI_INSTALLED and ALLOWED_PYTHON_VERSION, 'FastAPI requires Python3.6+' +) class UtilsVersionCompareTest(BaseTest): def test_is_current_version_higher_or_equal(self): # Copied from https://semver.org/#spec-item-11