diff --git a/requirements.txt b/requirements.txt index f8c96483..ee471b63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ pydantic>=1.10,!=2.0.* aiohttp~=3.8 boto3~=1.26 opencv-python-headless~=4.7 -packaging>=24.0,<26.0 plotly~=5.14 pandas~=2.0 pillow>=9.5,~=10.0 diff --git a/src/superannotate/__init__.py b/src/superannotate/__init__.py index b005463a..a52eda74 100644 --- a/src/superannotate/__init__.py +++ b/src/superannotate/__init__.py @@ -11,7 +11,7 @@ import requests from lib.core import enums -from packaging.version import parse +from lib.core.utils import parse_version from lib.core import PACKAGE_VERSION_UPGRADE from lib.core import PACKAGE_VERSION_INFO_MESSAGE from lib.core import PACKAGE_VERSION_MAJOR_UPGRADE @@ -47,15 +47,15 @@ def log_version_info(): logging.StreamHandler(sys.stdout) - local_version = parse(__version__) + local_version = parse_version(__version__) if local_version.is_prerelease: logging.info(PACKAGE_VERSION_INFO_MESSAGE.format(__version__)) req = requests.get("https://pypi.org/pypi/superannotate/json") if req.ok: releases = req.json().get("releases", []) - pip_version = parse("0") + pip_version = parse_version("0") for release in releases: - ver = parse(release) + ver = parse_version(release) if not ver.is_prerelease or local_version.is_prerelease: pip_version = max(pip_version, ver) if pip_version.major > local_version.major: diff --git a/src/superannotate/lib/core/pydantic_v1.py b/src/superannotate/lib/core/pydantic_v1.py index b1b89287..df3b621c 100644 --- a/src/superannotate/lib/core/pydantic_v1.py +++ b/src/superannotate/lib/core/pydantic_v1.py @@ -1,6 +1,7 @@ -from packaging.version import parse as parse_version +from lib.core.utils import parse_version from pydantic import VERSION + if parse_version(VERSION).major < 2: import pydantic else: diff --git a/src/superannotate/lib/core/utils.py b/src/superannotate/lib/core/utils.py index b01e3b86..9c53f1b1 100644 --- a/src/superannotate/lib/core/utils.py +++ b/src/superannotate/lib/core/utils.py @@ -1,4 +1,5 @@ import asyncio +import re import typing from threading import Thread @@ -46,3 +47,58 @@ def wrapper(func: typing.Callable): thread.start() thread.join() return response[0] + + +def parse_version(version_string): + """Smart version parsing with support for various formats""" + # Remove 'v' prefix if present + version_string = version_string.lstrip("v") + + # Extract version parts using regex + match = re.match( + r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:[-.]?(a|b|rc|dev|alpha|beta)(\d*))?", + version_string, + ) + + if not match: + raise ValueError(f"Invalid version format: {version_string}") + + major = int(match.group(1)) + minor = int(match.group(2) or 0) + patch = int(match.group(3) or 0) + pre_type = match.group(4) + pre_num = int(match.group(5) or 0) if match.group(5) else 0 + + class Version: + def __init__(self, major, minor, patch, pre_type=None, pre_num=0): + self.major = major + self.minor = minor + self.patch = patch + self.pre_type = pre_type + self.pre_num = pre_num + + @property + def is_prerelease(self): + return self.pre_type is not None + + def __str__(self): + version = f"{self.major}.{self.minor}.{self.patch}" + if self.pre_type: + version += f"-{self.pre_type}{self.pre_num}" + return version + + def __gt__(self, other): + if self.major != other.major: + return self.major > other.major + if self.minor != other.minor: + return self.minor > other.minor + if self.patch != other.patch: + return self.patch > other.patch + # Handle prerelease comparison + if self.is_prerelease and not other.is_prerelease: + return False + if not self.is_prerelease and other.is_prerelease: + return True + return self.pre_num > other.pre_num + + return Version(major, minor, patch, pre_type, pre_num)