Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 9% (0.09x) speedup for __getattr__ in starlette/status.py

⏱️ Runtime : 4.61 milliseconds 4.25 milliseconds (best of 78 runs)

📝 Explanation and details

The optimization achieves an 8% speedup by eliminating redundant dictionary creation on every function call.

Key changes:

  • Moved dictionary to module level: The deprecation_changes dictionary is now defined as _deprecation_changes at module scope instead of being recreated inside __getattr__ on every call. The line profiler shows this eliminated ~4.8ms of overhead from dictionary creation (lines with dict literal assignments in the original).

  • Streamlined conditional logic: Combined the __deprecated__.get(name) call with the conditional check using the walrus operator (if (deprecated := __deprecated__.get(name)) is not None:), reducing from two separate operations to one.

Why it's faster:

  • Dictionary creation is expensive in Python - the original code allocated and populated a 4-key dictionary on every attribute access, totaling 25% of the function's runtime based on line profiler data
  • The walrus operator eliminates a redundant variable assignment while maintaining readability
  • Module-level constants are accessed faster than local variables created in function scope

Test case performance:
The optimization performs well across all test scenarios, showing consistent 2-20% improvements for individual attribute lookups. It's particularly effective for:

  • Repeated calls to deprecated attributes (8-9% faster)
  • Invalid attribute lookups that hit the AttributeError path (12-21% faster)
  • High-frequency usage patterns where the dictionary creation overhead compounds

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 4301 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 1 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
from __future__ import annotations

import warnings

# imports
import pytest  # used for our unit tests
from starlette.status import __getattr__

__deprecated__ = {
    "HTTP_413_REQUEST_ENTITY_TOO_LARGE": 413,
    "HTTP_414_REQUEST_URI_TOO_LONG": 414,
    "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE": 416,
    "HTTP_422_UNPROCESSABLE_ENTITY": 422,
}
from starlette.status import __getattr__

# unit tests

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

def test_deprecated_value_returned_and_warning():
    """Test that deprecated names return correct value and emit a warning."""
    for name, expected in __deprecated__.items():
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            codeflash_output = __getattr__(name); result = codeflash_output

def test_non_deprecated_raises_attributeerror():
    """Test that non-deprecated names raise AttributeError."""
    invalid_names = [
        "HTTP_404_NOT_FOUND",
        "HTTP_200_OK",
        "HTTP_418_IM_A_TEAPOT",
        "HTTP_413_CONTENT_TOO_LARGE",  # valid new name, but not deprecated
        "HTTP_414_URI_TOO_LONG",
        "HTTP_416_RANGE_NOT_SATISFIABLE",
        "HTTP_422_UNPROCESSABLE_CONTENT",
        "random_string",
        "",
        "http_413_request_entity_too_large",  # wrong case
    ]
    for name in invalid_names:
        with pytest.raises(AttributeError) as excinfo:
            __getattr__(name)

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

@pytest.mark.parametrize("name", [
    None,                   # NoneType input
    413,                    # integer input
    414.0,                  # float input
    ["HTTP_413_REQUEST_ENTITY_TOO_LARGE"],  # list input
    {"name": "HTTP_413_REQUEST_ENTITY_TOO_LARGE"},  # dict input
    True,                   # boolean input
    b"HTTP_413_REQUEST_ENTITY_TOO_LARGE",   # bytes input
])
def test_non_string_input_raises_typeerror_or_attributeerror(name):
    """Test that non-string input raises TypeError or AttributeError."""
    # __getattr__ expects a string; passing non-string may raise TypeError or AttributeError
    with pytest.raises(Exception) as excinfo:
        __getattr__(name) # 11.6μs -> 10.2μs (13.6% faster)

def test_empty_string_raises_attributeerror():
    """Test that empty string input raises AttributeError."""
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("") # 1.18μs -> 1.05μs (11.8% faster)

def test_whitespace_string_raises_attributeerror():
    """Test that whitespace string input raises AttributeError."""
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("   ") # 1.13μs -> 1.01μs (12.1% faster)

def test_similar_but_not_exact_names():
    """Test that names similar to deprecated ones do not match."""
    similar_names = [
        "HTTP_413_REQUEST_ENTITY_TOO_LARGE_",  # extra underscore
        "HTTP_413_REQUEST_ENTITY_TOO_LARG",    # missing last char
        "HTTP_413_REQUEST_ENTITY_TOO_LARGE1",  # extra number
        "HTTP_413_REQUEST_ENTITY_TOO_LARGE "   # trailing space
    ]
    for name in similar_names:
        with pytest.raises(AttributeError):
            __getattr__(name)

def test_warning_message_contains_suggestion():
    """Test that warning message contains the suggested new name."""
    for name in __deprecated__:
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            __getattr__(name)
            # Check the suggested name is present in the warning
            deprecation_changes = {
                "HTTP_413_REQUEST_ENTITY_TOO_LARGE": "HTTP_413_CONTENT_TOO_LARGE",
                "HTTP_414_REQUEST_URI_TOO_LONG": "HTTP_414_URI_TOO_LONG",
                "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE": "HTTP_416_RANGE_NOT_SATISFIABLE",
                "HTTP_422_UNPROCESSABLE_ENTITY": "HTTP_422_UNPROCESSABLE_CONTENT",
            }
            suggestion = deprecation_changes[name]

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

def test_large_number_of_invalid_names():
    """Test that many invalid names all raise AttributeError."""
    # Generate 500 unique invalid names
    invalid_names = [f"HTTP_FAKE_STATUS_{i}" for i in range(500)]
    for name in invalid_names:
        with pytest.raises(AttributeError):
            __getattr__(name)

def test_large_number_of_valid_names():
    """Test that repeated calls to deprecated names always return correct value and warning."""
    # 250 repetitions for each deprecated name
    for name, expected in __deprecated__.items():
        for _ in range(250):
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter("always")
                codeflash_output = __getattr__(name); result = codeflash_output

def test_performance_under_many_calls():
    """Test that calling __getattr__ many times for deprecated and invalid names is fast and correct."""
    import time
    names = []
    # 250 deprecated names
    for name in __deprecated__:
        names.extend([name] * 250)
    # 250 invalid names
    names.extend([f"HTTP_FAKE_STATUS_{i}" for i in range(250)])
    start = time.time()
    for name in names:
        if name in __deprecated__:
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter("always")
                codeflash_output = __getattr__(name); result = codeflash_output
        else:
            with pytest.raises(AttributeError):
                __getattr__(name)
    duration = time.time() - start

def test_no_mutation_of_deprecated_dict():
    """Test that __getattr__ does not mutate the __deprecated__ dictionary."""
    import copy
    before = copy.deepcopy(__deprecated__)
    for name in __deprecated__:
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            __getattr__(name)
    after = __deprecated__
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from __future__ import annotations

import warnings

# imports
import pytest  # used for our unit tests
from starlette.status import __getattr__

__deprecated__ = {
    "HTTP_413_REQUEST_ENTITY_TOO_LARGE": 413,
    "HTTP_414_REQUEST_URI_TOO_LONG": 414,
    "HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE": 416,
    "HTTP_422_UNPROCESSABLE_ENTITY": 422,
}
from starlette.status import __getattr__

# unit tests

# --- Basic Test Cases ---

def test_basic_deprecated_413():
    # Test that deprecated attribute returns correct value and emits a warning
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        codeflash_output = __getattr__("HTTP_413_REQUEST_ENTITY_TOO_LARGE"); result = codeflash_output # 7.02μs -> 6.85μs (2.59% faster)

def test_basic_deprecated_414():
    # Test another deprecated attribute
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        codeflash_output = __getattr__("HTTP_414_REQUEST_URI_TOO_LONG"); result = codeflash_output # 5.10μs -> 4.71μs (8.32% faster)

def test_basic_deprecated_416():
    # Test another deprecated attribute
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        codeflash_output = __getattr__("HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE"); result = codeflash_output # 4.69μs -> 4.40μs (6.59% faster)

def test_basic_deprecated_422():
    # Test another deprecated attribute
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        codeflash_output = __getattr__("HTTP_422_UNPROCESSABLE_ENTITY"); result = codeflash_output # 4.69μs -> 4.31μs (8.82% faster)

# --- Edge Test Cases ---

def test_nonexistent_attribute_raises():
    # Test that a non-existent attribute raises AttributeError
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("HTTP_999_UNKNOWN_STATUS") # 1.22μs -> 1.04μs (17.7% faster)

def test_empty_string_attribute_raises():
    # Test that an empty string raises AttributeError
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("") # 1.17μs -> 988ns (18.4% faster)



def test_case_sensitivity():
    # Test that attribute lookup is case-sensitive
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("http_413_request_entity_too_large") # 1.42μs -> 1.20μs (17.6% faster)

def test_whitespace_in_attribute():
    # Test that attribute with whitespace is not found
    with pytest.raises(AttributeError) as excinfo:
        __getattr__(" HTTP_413_REQUEST_ENTITY_TOO_LARGE ") # 1.29μs -> 1.08μs (20.2% faster)

def test_partial_match_attribute():
    # Test that partial matches do not succeed
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("HTTP_413_REQUEST") # 1.22μs -> 1.01μs (20.8% faster)

# --- Large Scale Test Cases ---

def test_large_number_of_invalid_attributes():
    # Test many invalid attribute lookups to ensure no memory leaks or slowdowns
    for i in range(1000):
        attr = f"HTTP_FAKE_STATUS_{i}"
        with pytest.raises(AttributeError):
            __getattr__(attr)

def test_large_number_of_valid_deprecated_attributes():
    # Dynamically add many deprecated attributes and test them
    # We'll simulate this by patching __deprecated__ and deprecation_changes
    # Note: This is not mocking, just direct mutation for the test
    global __deprecated__
    original_deprecated = __deprecated__.copy()
    try:
        # Add 500 new deprecated attributes
        for i in range(500):
            __deprecated__[f"HTTP_FAKE_DEPRECATED_{i}"] = 600 + i
        # Patch deprecation_changes for these
        deprecation_changes = {f"HTTP_FAKE_DEPRECATED_{i}": f"HTTP_FAKE_NEW_{i}" for i in range(500)}
        def patched_getattr(name: str) -> int:
            deprecated = __deprecated__.get(name)
            if deprecated:
                warnings.warn(
                    f"'{name}' is deprecated. Use '{deprecation_changes.get(name, 'UNKNOWN')}' instead.",
                    category=DeprecationWarning,
                    stacklevel=3,
                )
                return deprecated
            raise AttributeError(f"module 'starlette.status' has no attribute '{name}'")
        # Test all new deprecated attributes
        for i in range(500):
            attr = f"HTTP_FAKE_DEPRECATED_{i}"
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter("always")
                result = patched_getattr(attr)
    finally:
        __deprecated__ = original_deprecated

def test_performance_many_calls(monkeypatch):
    # Test performance and correctness with repeated calls
    import time
    start = time.time()
    for i in range(500):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            codeflash_output = __getattr__("HTTP_413_REQUEST_ENTITY_TOO_LARGE"); result = codeflash_output
    elapsed = time.time() - start

# --- Additional Edge Cases ---

@pytest.mark.parametrize("bad_input", [[], {}, set(), object()])
def test_non_string_types_raise(bad_input):
    # Test that passing non-string types raises TypeError
    with pytest.raises(TypeError):
        __getattr__(bad_input) # 4.03μs -> 3.64μs (10.8% faster)

def test_attribute_with_unicode_characters():
    # Test that unicode characters in attribute name are not found
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("HTTP_413_请求实体过大") # 1.65μs -> 1.50μs (10.2% faster)

def test_attribute_with_newline():
    # Test that attribute name with newline is not found
    with pytest.raises(AttributeError) as excinfo:
        __getattr__("HTTP_413_REQUEST_ENTITY_TOO_LARGE\n") # 1.21μs -> 1.05μs (15.2% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from starlette.status import __getattr__
import pytest

def test___getattr__():
    __getattr__('HTTP_422_UNPROCESSABLE_ENTITY')

def test___getattr___2():
    with pytest.raises(AttributeError, match="module\\ 'starlette\\.status'\\ has\\ no\\ attribute\\ ''"):
        __getattr__('')
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic_xzaz2m9_/tmpt158t0ud/test_concolic_coverage.py::test___getattr___2 1.44μs 1.24μs 16.5%✅

To edit these changes git checkout codeflash/optimize-__getattr__-mhbhyfe4 and push.

Codeflash

The optimization achieves an 8% speedup by eliminating redundant dictionary creation on every function call. 

**Key changes:**
- **Moved dictionary to module level**: The `deprecation_changes` dictionary is now defined as `_deprecation_changes` at module scope instead of being recreated inside `__getattr__` on every call. The line profiler shows this eliminated ~4.8ms of overhead from dictionary creation (lines with dict literal assignments in the original).

- **Streamlined conditional logic**: Combined the `__deprecated__.get(name)` call with the conditional check using the walrus operator (`if (deprecated := __deprecated__.get(name)) is not None:`), reducing from two separate operations to one.

**Why it's faster:**
- Dictionary creation is expensive in Python - the original code allocated and populated a 4-key dictionary on every attribute access, totaling 25% of the function's runtime based on line profiler data
- The walrus operator eliminates a redundant variable assignment while maintaining readability
- Module-level constants are accessed faster than local variables created in function scope

**Test case performance:**
The optimization performs well across all test scenarios, showing consistent 2-20% improvements for individual attribute lookups. It's particularly effective for:
- Repeated calls to deprecated attributes (8-9% faster)
- Invalid attribute lookups that hit the AttributeError path (12-21% faster) 
- High-frequency usage patterns where the dictionary creation overhead compounds
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 29, 2025 04:30
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 29, 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