Skip to content

Commit

Permalink
More updates (#2269)
Browse files Browse the repository at this point in the history
* Updated check_version

* Fixed check version

* Updated version to 2.0.0

* Thwrow warning for any reason in check version
  • Loading branch information
ternaus authored Jan 8, 2025
1 parent e4d6cce commit 72d9726
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 11 deletions.
79 changes: 69 additions & 10 deletions albumentations/check_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from __future__ import annotations

import json
import re
import urllib.request
from urllib.request import OpenerDirector
from warnings import warn
Expand Down Expand Up @@ -46,21 +49,77 @@ def parse_version(data: str) -> str:
return ""


def compare_versions(v1: tuple[int | str, ...], v2: tuple[int | str, ...]) -> bool:
"""Compare two version tuples.
Returns True if v1 > v2, False otherwise.
Special rules:
1. Release version > pre-release version (e.g., (1, 4) > (1, 4, 'beta'))
2. Numeric parts are compared numerically
3. String parts are compared lexicographically
"""
# First compare common parts
for p1, p2 in zip(v1, v2):
if p1 != p2:
# If both are same type, direct comparison works
if isinstance(p1, int) and isinstance(p2, int):
return p1 > p2
if isinstance(p1, str) and isinstance(p2, str):
return p1 > p2
# If types differ, numbers are greater (release > pre-release)
return isinstance(p1, int)

# If we get here, all common parts are equal
# Longer version is greater only if next element is a number
if len(v1) > len(v2):
return isinstance(v1[len(v2)], int)
if len(v2) > len(v1):
# v2 is longer, so v1 is greater only if v2's next part is a string (pre-release)
return isinstance(v2[len(v1)], str)

return False # Versions are equal


def parse_version_parts(version_str: str) -> tuple[int | str, ...]:
"""Convert version string to tuple of (int | str) parts following PEP 440 conventions.
Examples:
"1.4.24" -> (1, 4, 24)
"1.4beta" -> (1, 4, "beta")
"1.4.beta2" -> (1, 4, "beta", 2)
"1.4.alpha2" -> (1, 4, "alpha", 2)
"""
parts = []
# First split by dots
for part in version_str.split("."):
# Then parse each part for numbers and letters
segments = re.findall(r"([0-9]+|[a-zA-Z]+)", part)
for segment in segments:
if segment.isdigit():
parts.append(int(segment))
else:
parts.append(segment.lower())
return tuple(parts)


def check_for_updates() -> None:
try:
data = fetch_version_info()
latest_version = parse_version(data)
if latest_version and latest_version != current_version:
warn(
f"A new version of Albumentations is available: {latest_version} (you have {current_version}). " # noqa: S608
"Upgrade using: pip install -U albumentations. "
"To disable automatic update checks, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.",
UserWarning,
stacklevel=2,
)
except Exception as e: # General exception catch to ensure silent failure # noqa: BLE001
if latest_version:
latest_parts = parse_version_parts(latest_version)
current_parts = parse_version_parts(current_version)
if compare_versions(latest_parts, current_parts):
warn(
f"A new version of Albumentations is available: {latest_version!r} (you have {current_version!r}). "
"Upgrade using: pip install -U albumentations. "
"To disable automatic update checks, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.",
UserWarning,
stacklevel=2,
)
except Exception as e: # General exception catch to ensure silent failure # noqa: BLE001
warn(
f"Failed to check for updates due to an unexpected error: {e}. " # noqa: S608
f"Failed to check for updates due to error: {e}. "
"To disable automatic update checks, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.",
UserWarning,
stacklevel=2,
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ requires = [ "setuptools>=45", "wheel" ]
[project]
name = "albumentations"

version = "1.5.0"
version = "2.0.0"

description = "Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Albumentations offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks) data, with optimized performance and seamless integration into ML workflows."
readme = "README.md"
Expand Down Expand Up @@ -225,6 +225,7 @@ lint.ignore = [
"PLR2004",
"PTH123",
"S311",
"S608",
"TC001",
"TC002",
"TC003",
Expand Down
48 changes: 48 additions & 0 deletions tests/test_check_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest
Expand All @@ -7,6 +9,8 @@
fetch_version_info,
get_opener,
parse_version,
parse_version_parts,
compare_versions,
)


Expand Down Expand Up @@ -127,3 +131,47 @@ def test_check_for_updates_with_update():
with patch("albumentations.check_version.warn") as mock_warn: # Patch the imported warn
check_for_updates()
mock_warn.assert_called_once()



@pytest.mark.parametrize("version_str, expected", [
# Standard versions
("1.4.24", (1, 4, 24)),
("0.0.1", (0, 0, 1)),
("10.20.30", (10, 20, 30)),
# Pre-release versions
("1.4beta", (1, 4, "beta")),
("1.4beta2", (1, 4, "beta", 2)),
("1.4.beta2", (1, 4, "beta", 2)),
("1.4.alpha2", (1, 4, "alpha", 2)),
("1.4rc1", (1, 4, "rc", 1)),
("1.4.rc.1", (1, 4, "rc", 1)),
# Mixed case handling
("1.4Beta2", (1, 4, "beta", 2)),
("1.4ALPHA2", (1, 4, "alpha", 2)),
])
def test_parse_version_parts(version_str: str, expected: tuple[int | str, ...]) -> None:
assert parse_version_parts(version_str) == expected

# Update the test to use the new comparison function
@pytest.mark.parametrize("version1, version2, expected", [
# Pre-release ordering
("1.4beta2", "1.4beta1", True),
("1.4", "1.4beta", True),
("1.4beta", "1.4alpha", True),
("1.4alpha2", "1.4alpha1", True),
("1.4rc", "1.4beta", True),
("2.0", "2.0rc1", True),
# Standard version ordering
("1.5", "1.4", True),
("1.4.1", "1.4", True),
("1.4.24", "1.4.23", True),
])
def test_version_comparison(version1: str, version2: str, expected: bool) -> None:
"""Test that version1 > version2 matches expected result."""
v1 = parse_version_parts(version1)
v2 = parse_version_parts(version2)
assert compare_versions(v1, v2) == expected

0 comments on commit 72d9726

Please sign in to comment.