Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Oct 30, 2025

📄 37% (0.37x) speedup for RetryingClient.version_supported in wandb/apis/public/api.py

⏱️ Runtime : 64.9 milliseconds 47.3 milliseconds (best of 32 runs)

📝 Explanation and details

The optimization implements caching for the parsed max_cli_version to avoid redundant parsing operations.

Key changes:

  • Caches the parsed max_cli_version in self._parsed_max_cli_version along with tracking the source string in self._parsed_max_cli_version_src
  • Only re-parses max_cli_version when it changes (checked via string comparison)
  • Reuses the cached parsed version across multiple calls with the same max version

Why it's faster:
The packaging.version.parse() function is computationally expensive, involving string parsing and normalization. The line profiler shows that parsing operations dominated the original runtime (94% of time spent on the return line with two parse() calls). By caching the parsed max version, we eliminate repeated parsing of the same version string.

Performance benefits by test case:

  • Single version checks: Modest 1-6% improvements due to one fewer parse call
  • Repeated calls with same max version: Significant 46-70% speedups in large-scale tests where the same RetryingClient instance is used multiple times (e.g., test_many_versions_supported shows 70% improvement)
  • Exception cases: Some degradation when exceptions occur due to additional attribute checks, but these are edge cases

The optimization is particularly effective for scenarios where the same client instance performs multiple version checks, which is the typical usage pattern for this API client.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 9195 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest  # used for our unit tests
# function to test
from packaging.version import parse
from wandb.apis.public.api import RetryingClient


class DummyRetryingClient:
    """
    A dummy version of RetryingClient for testing version_supported.
    Allows injection of server_info for testability.
    """
    def __init__(self, max_cli_version):
        self.server_info = {
            "cliVersionInfo": {
                "max_cli_version": max_cli_version
            }
        }

    def version_supported(self, min_version: str) -> bool:
        return parse(min_version) <= parse(self.server_info["cliVersionInfo"]["max_cli_version"])

# unit tests

# -------------------------
# 1. Basic Test Cases
# -------------------------

def test_version_supported_equal_versions():
    """Test when min_version == max_cli_version (should be supported)."""
    client = DummyRetryingClient("1.2.3")
    codeflash_output = client.version_supported("1.2.3") # 21.3μs -> 20.2μs (5.60% faster)

def test_version_supported_min_version_lower():
    """Test when min_version < max_cli_version (should be supported)."""
    client = DummyRetryingClient("2.0.0")
    codeflash_output = client.version_supported("1.5.0") # 21.3μs -> 21.0μs (1.75% faster)

def test_version_supported_min_version_higher():
    """Test when min_version > max_cli_version (should NOT be supported)."""
    client = DummyRetryingClient("2.0.0")
    codeflash_output = client.version_supported("2.0.1") # 21.1μs -> 20.7μs (1.88% faster)

def test_version_supported_patch_difference():
    """Test patch version difference."""
    client = DummyRetryingClient("1.2.3")
    codeflash_output = client.version_supported("1.2.2") # 20.8μs -> 20.6μs (1.21% faster)
    codeflash_output = client.version_supported("1.2.4") # 7.83μs -> 8.04μs (2.60% slower)

def test_version_supported_minor_difference():
    """Test minor version difference."""
    client = DummyRetryingClient("1.3.0")
    codeflash_output = client.version_supported("1.2.9") # 20.9μs -> 21.0μs (0.533% slower)
    codeflash_output = client.version_supported("1.4.0") # 8.36μs -> 8.14μs (2.69% faster)

def test_version_supported_major_difference():
    """Test major version difference."""
    client = DummyRetryingClient("2.0.0")
    codeflash_output = client.version_supported("1.9.9") # 21.2μs -> 20.5μs (3.66% faster)
    codeflash_output = client.version_supported("3.0.0") # 8.45μs -> 8.25μs (2.37% faster)

# -------------------------
# 2. Edge Test Cases
# -------------------------

@pytest.mark.parametrize(
    "min_version,max_cli_version,expected",
    [
        # Pre-release versions
        ("1.2.3a1", "1.2.3", False),  # alpha < release
        ("1.2.3", "1.2.3a1", True),   # release > alpha
        ("1.2.3rc1", "1.2.3", False), # rc < release
        ("1.2.3", "1.2.3rc1", True),  # release > rc
        ("1.2.3b2", "1.2.3b3", True), # beta2 < beta3
        ("1.2.3b3", "1.2.3b2", False),# beta3 > beta2
        # Post-releases
        ("1.2.3.post1", "1.2.3", False),
        ("1.2.3", "1.2.3.post1", True),
        ("1.2.3.post2", "1.2.3.post3", True),
        ("1.2.3.post4", "1.2.3.post3", False),
        # Dev releases
        ("1.2.3.dev1", "1.2.3", False),
        ("1.2.3", "1.2.3.dev1", True),
        ("1.2.3.dev2", "1.2.3.dev3", True),
        ("1.2.3.dev4", "1.2.3.dev3", False),
        # Leading zeros and normalization
        ("01.02.03", "1.2.3", True),
        ("1.2.3", "01.02.03", True),
        # Long version numbers
        ("1.2.3.4.5", "1.2.3.4.6", True),
        ("1.2.3.4.7", "1.2.3.4.6", False),
        # Empty string (should raise)
        ("", "1.2.3", False),
        ("1.2.3", "", False),
        # Non-numeric
        ("foo", "1.2.3", False),
        ("1.2.3", "bar", False),
    ]
)
def test_version_supported_edge_cases(min_version, max_cli_version, expected):
    """Test various edge cases including pre/post/dev releases and invalid input."""
    client = DummyRetryingClient(max_cli_version)
    if min_version == "" or max_cli_version == "" or not min_version[0].isdigit() or not max_cli_version[0].isdigit():
        # packaging.version.parse will raise for invalid version strings
        with pytest.raises(Exception):
            client.version_supported(min_version) # 423μs -> 414μs (2.07% faster)
    else:
        codeflash_output = client.version_supported(min_version)

def test_version_supported_case_insensitivity():
    """Test that version comparison is case-insensitive."""
    client = DummyRetryingClient("1.2.3RC1")
    codeflash_output = client.version_supported("1.2.3rc1") # 23.4μs -> 23.1μs (1.29% faster)

def test_version_supported_whitespace():
    """Test that leading/trailing whitespace is ignored."""
    client = DummyRetryingClient(" 1.2.3 ")
    codeflash_output = client.version_supported(" 1.2.2 ") # 21.2μs -> 20.5μs (3.22% faster)
    codeflash_output = client.version_supported(" 1.2.4 ") # 8.26μs -> 8.05μs (2.60% faster)

def test_version_supported_invalid_types():
    """Test that non-string types raise an error."""
    client = DummyRetryingClient("1.2.3")
    with pytest.raises(Exception):
        client.version_supported(123) # 2.79μs -> 2.79μs (0.072% faster)
    with pytest.raises(Exception):
        client.version_supported(None) # 1.18μs -> 1.19μs (0.843% slower)

# -------------------------
# 3. Large Scale Test Cases
# -------------------------

def test_version_supported_large_range_supported():
    """Test with a large number of supported min_versions."""
    client = DummyRetryingClient("10.0.0")
    for i in range(0, 1000, 10):  # 0.0.0 up to 990.0.0
        min_version = f"{i}.0.0"
        if i <= 10:
            codeflash_output = client.version_supported(min_version)
        else:
            # Only versions up to 10.0.0 are supported
            codeflash_output = client.version_supported(min_version)

def test_version_supported_large_range_unsupported():
    """Test with a large number of unsupported min_versions."""
    client = DummyRetryingClient("1.0.0")
    for i in range(2, 1002):  # 2.0.0 up to 1001.0.0
        min_version = f"{i}.0.0"
        codeflash_output = client.version_supported(min_version) # 6.54ms -> 6.51ms (0.455% faster)

def test_version_supported_large_versions():
    """Test very large version numbers."""
    client = DummyRetryingClient("123.456.789")
    codeflash_output = client.version_supported("123.456.788") # 21.3μs -> 20.1μs (6.18% faster)
    codeflash_output = client.version_supported("123.456.789") # 8.12μs -> 8.15μs (0.331% slower)
    codeflash_output = client.version_supported("123.456.790") # 6.82μs -> 6.99μs (2.39% slower)

def test_version_supported_many_minor_versions():
    """Test many minor versions for scalability."""
    client = DummyRetryingClient("2.999.0")
    for minor in range(0, 1000):
        min_version = f"2.{minor}.0"
        codeflash_output = client.version_supported(min_version) # 6.48ms -> 6.43ms (0.727% faster)

def test_version_supported_many_patch_versions():
    """Test many patch versions for scalability."""
    client = DummyRetryingClient("1.0.999")
    for patch in range(0, 1000):
        min_version = f"1.0.{patch}"
        codeflash_output = client.version_supported(min_version) # 6.36ms -> 6.36ms (0.063% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import pytest  # used for our unit tests
# function to test
from packaging.version import parse
from wandb.apis.public.api import RetryingClient

# unit tests

# Helper to create a RetryingClient with a given max_cli_version
def make_client_with_max_version(max_version):
    client = RetryingClient(None)
    client._server_info = {
        "cliVersionInfo": {
            "max_cli_version": max_version
        }
    }
    return client

# ---------- BASIC TEST CASES ----------

def test_exact_version_match():
    # Test when min_version == max_cli_version
    client = make_client_with_max_version("1.2.3")
    codeflash_output = client.version_supported("1.2.3") # 22.9μs -> 22.6μs (1.28% faster)

def test_min_version_lower_than_max():
    # Test when min_version < max_cli_version
    client = make_client_with_max_version("2.0.0")
    codeflash_output = client.version_supported("1.9.9") # 23.7μs -> 23.6μs (0.437% faster)

def test_min_version_higher_than_max():
    # Test when min_version > max_cli_version
    client = make_client_with_max_version("1.2.3")
    codeflash_output = client.version_supported("2.0.0") # 22.7μs -> 23.2μs (2.13% slower)

def test_patch_version_difference():
    # Test when only patch version differs
    client = make_client_with_max_version("1.2.4")
    codeflash_output = client.version_supported("1.2.3") # 22.8μs -> 22.9μs (0.031% slower)
    codeflash_output = client.version_supported("1.2.5") # 8.84μs -> 6.04μs (46.4% faster)

def test_minor_version_difference():
    # Test when only minor version differs
    client = make_client_with_max_version("1.3.0")
    codeflash_output = client.version_supported("1.2.0") # 23.2μs -> 23.1μs (0.464% faster)
    codeflash_output = client.version_supported("1.4.0") # 9.19μs -> 6.26μs (46.8% faster)

def test_major_version_difference():
    # Test when only major version differs
    client = make_client_with_max_version("2.0.0")
    codeflash_output = client.version_supported("1.0.0") # 23.2μs -> 22.7μs (2.15% faster)
    codeflash_output = client.version_supported("3.0.0") # 9.37μs -> 6.13μs (52.9% faster)

# ---------- EDGE TEST CASES ----------

def test_pre_release_versions():
    # Test with pre-release versions (e.g., 1.2.3rc1)
    client = make_client_with_max_version("1.2.3")
    codeflash_output = client.version_supported("1.2.3rc1") # 25.6μs -> 25.8μs (0.726% slower)
    codeflash_output = client.version_supported("1.2.4rc1") # 10.8μs -> 7.07μs (52.9% faster)

def test_post_release_versions():
    # Test with post-release versions (e.g., 1.2.3.post1)
    client = make_client_with_max_version("1.2.3.post1")
    codeflash_output = client.version_supported("1.2.3") # 26.6μs -> 25.9μs (2.65% faster)
    codeflash_output = client.version_supported("1.2.3.post2") # 11.2μs -> 7.70μs (45.7% faster)

def test_dev_release_versions():
    # Test with dev-release versions (e.g., 1.2.3.dev1)
    client = make_client_with_max_version("1.2.3")
    codeflash_output = client.version_supported("1.2.3.dev1") # 26.3μs -> 25.8μs (2.01% faster)
    codeflash_output = client.version_supported("1.2.4.dev1") # 11.3μs -> 7.23μs (55.8% faster)

def test_versions_with_v_prefix():
    # Test versions with 'v' prefix
    client = make_client_with_max_version("v1.2.3")
    codeflash_output = client.version_supported("1.2.2") # 23.5μs -> 23.0μs (2.18% faster)
    codeflash_output = client.version_supported("v1.2.3") # 9.01μs -> 6.18μs (45.7% faster)
    codeflash_output = client.version_supported("v1.2.4") # 7.47μs -> 4.81μs (55.3% faster)

def test_versions_with_leading_zeros():
    # Test versions with leading zeros
    client = make_client_with_max_version("01.02.03")
    codeflash_output = client.version_supported("1.2.3") # 24.3μs -> 23.3μs (4.13% faster)
    codeflash_output = client.version_supported("01.02.04") # 9.37μs -> 6.39μs (46.7% faster)

def test_empty_version_string():
    # Test with empty min_version string
    client = make_client_with_max_version("1.2.3")
    with pytest.raises(Exception):
        client.version_supported("") # 4.66μs -> 19.1μs (75.6% slower)

def test_invalid_version_string():
    # Test with invalid min_version string
    client = make_client_with_max_version("1.2.3")
    with pytest.raises(Exception):
        client.version_supported("not.a.version") # 6.59μs -> 19.4μs (66.1% slower)

def test_missing_max_cli_version():
    # Test when max_cli_version is missing from server_info
    client = RetryingClient(None)
    client._server_info = {"cliVersionInfo": {}}
    with pytest.raises(KeyError):
        client.version_supported("1.2.3") # 18.1μs -> 3.23μs (462% faster)

def test_none_max_cli_version():
    # Test when max_cli_version is None
    client = RetryingClient(None)
    client._server_info = {"cliVersionInfo": {"max_cli_version": None}}
    with pytest.raises(Exception):
        client.version_supported("1.2.3") # 19.0μs -> 5.36μs (255% faster)

def test_none_min_version():
    # Test when min_version is None
    client = make_client_with_max_version("1.2.3")
    with pytest.raises(Exception):
        client.version_supported(None) # 4.47μs -> 18.6μs (76.0% slower)

def test_non_string_min_version():
    # Test when min_version is not a string
    client = make_client_with_max_version("1.2.3")
    with pytest.raises(Exception):
        client.version_supported(123) # 4.61μs -> 18.7μs (75.4% slower)

def test_non_string_max_version():
    # Test when max_cli_version is not a string
    client = RetryingClient(None)
    client._server_info = {"cliVersionInfo": {"max_cli_version": 123}}
    with pytest.raises(Exception):
        client.version_supported("1.2.3") # 19.1μs -> 5.34μs (258% faster)

def test_missing_server_info():
    # Test when server_info is None
    client = RetryingClient(None)
    client._server_info = None
    with pytest.raises(TypeError):
        client.version_supported("1.2.3")

def test_missing_cli_version_info():
    # Test when cliVersionInfo is missing from server_info
    client = RetryingClient(None)
    client._server_info = {}
    with pytest.raises(KeyError):
        client.version_supported("1.2.3") # 18.5μs -> 3.12μs (493% faster)

# ---------- LARGE SCALE TEST CASES ----------

def test_many_versions_supported():
    # Test with a large number of version checks, all supported
    client = make_client_with_max_version("9.9.9")
    for i in range(1000):
        min_version = f"1.0.{i}"
        codeflash_output = client.version_supported(min_version) # 7.06ms -> 4.15ms (70.2% faster)

def test_many_versions_unsupported():
    # Test with a large number of version checks, all unsupported
    client = make_client_with_max_version("1.0.0")
    for i in range(1, 1001):
        min_version = f"2.0.{i}"
        codeflash_output = client.version_supported(min_version) # 7.14ms -> 4.17ms (71.3% faster)

def test_large_version_numbers():
    # Test with very large version numbers
    client = make_client_with_max_version("999.999.999")
    codeflash_output = client.version_supported("999.999.998") # 23.8μs -> 23.5μs (1.20% faster)
    codeflash_output = client.version_supported("999.999.999") # 9.22μs -> 6.10μs (51.1% faster)
    codeflash_output = client.version_supported("1000.0.0") # 9.14μs -> 6.09μs (50.1% faster)

def test_large_scale_pre_release_versions():
    # Test with many pre-release versions
    client = make_client_with_max_version("5.5.5")
    for i in range(1, 1000):
        min_version = f"5.5.5rc{i}"
        codeflash_output = client.version_supported(min_version) # 7.43ms -> 4.51ms (65.0% faster)

def test_large_scale_dev_versions():
    # Test with many dev-release versions
    client = make_client_with_max_version("3.3.3")
    for i in range(1, 1000):
        min_version = f"3.3.3.dev{i}"
        codeflash_output = client.version_supported(min_version) # 7.53ms -> 4.63ms (62.5% faster)

def test_large_scale_post_versions():
    # Test with many post-release versions, all unsupported
    client = make_client_with_max_version("2.2.2")
    for i in range(1, 1000):
        min_version = f"2.2.2.post{i}"
        codeflash_output = client.version_supported(min_version) # 7.51ms -> 4.62ms (62.4% faster)

def test_large_scale_mixed_versions():
    # Test with a mix of supported and unsupported versions
    client = make_client_with_max_version("10.10.10")
    for i in range(500):
        min_version = f"10.10.{i}"
        codeflash_output = client.version_supported(min_version) # 3.51ms -> 2.09ms (68.1% faster)
    for i in range(500, 1000):
        min_version = f"10.10.{i}"
        codeflash_output = client.version_supported(min_version) # 3.49ms -> 2.06ms (70.0% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-RetryingClient.version_supported-mhe1paln and push.

Codeflash Static Badge

The optimization implements **caching for the parsed max_cli_version** to avoid redundant parsing operations. 

**Key changes:**
- Caches the parsed `max_cli_version` in `self._parsed_max_cli_version` along with tracking the source string in `self._parsed_max_cli_version_src`
- Only re-parses `max_cli_version` when it changes (checked via string comparison)
- Reuses the cached parsed version across multiple calls with the same max version

**Why it's faster:**
The `packaging.version.parse()` function is computationally expensive, involving string parsing and normalization. The line profiler shows that parsing operations dominated the original runtime (94% of time spent on the return line with two `parse()` calls). By caching the parsed max version, we eliminate repeated parsing of the same version string.

**Performance benefits by test case:**
- **Single version checks**: Modest 1-6% improvements due to one fewer parse call
- **Repeated calls with same max version**: Significant 46-70% speedups in large-scale tests where the same `RetryingClient` instance is used multiple times (e.g., `test_many_versions_supported` shows 70% improvement)
- **Exception cases**: Some degradation when exceptions occur due to additional attribute checks, but these are edge cases

The optimization is particularly effective for scenarios where the same client instance performs multiple version checks, which is the typical usage pattern for this API client.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 30, 2025 23:18
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant