Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 22, 2025

📄 20% (0.20x) speedup for _get_plugin_specs_as_list in src/_pytest/config/__init__.py

⏱️ Runtime : 168 microseconds 140 microseconds (best of 109 runs)

📝 Explanation and details

The optimization achieves a 19% speedup by adding a fast-path check for common sequence types (list and tuple) before falling back to the more expensive isinstance(specs, collections.abc.Sequence) check.

Key changes:

  • Added explicit type(specs) is list or type(specs) is tuple check that handles the most common sequence cases
  • Moved the expensive collections.abc.Sequence check to only execute for less common sequence-like objects

Why this is faster:
The isinstance(specs, collections.abc.Sequence) call requires protocol checking and ABC machinery, which is significantly slower than direct type comparisons. The line profiler shows this check took 49.8% of total time in the original code but only 33% in the optimized version.

Performance benefits by test case:

  • List inputs: 135-147% faster (most dramatic improvement)
  • Tuple inputs: 203-225% faster (biggest gains)
  • Large sequences: 46-102% faster for large lists/tuples
  • String/None inputs: Modest 3-12% improvements
  • Error cases: Slightly slower (2-5%) due to additional type check, but these are exceptional cases

Impact on workloads:
Given the function reference shows this is called from _import_plugin_specs() during plugin loading, the optimization will significantly speed up pytest initialization when plugins are specified as lists or tuples - common patterns in pytest configuration and programmatic usage.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 54 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 4 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import types

from _pytest.config.__init__ import _get_plugin_specs_as_list
from _pytest.config.exceptions import UsageError

# imports
import pytest


# unit tests

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


def test_none_returns_empty_list():
    # None input should return empty list
    codeflash_output = _get_plugin_specs_as_list(None)  # 403ns -> 388ns (3.87% faster)


def test_module_type_returns_empty_list():
    # ModuleType input should return empty list
    mod = types.ModuleType("pytest_plugins")
    codeflash_output = _get_plugin_specs_as_list(mod)  # 632ns -> 542ns (16.6% faster)


def test_empty_string_returns_empty_list():
    # Empty string input should return empty list
    codeflash_output = _get_plugin_specs_as_list("")  # 763ns -> 682ns (11.9% faster)


def test_single_plugin_string():
    # Single plugin name as string
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA"
    )  # 882ns -> 856ns (3.04% faster)


def test_multiple_plugins_comma_separated_string():
    # Multiple plugin names, comma separated
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA,pluginB,pluginC"
    )  # 926ns -> 912ns (1.54% faster)


def test_sequence_list_of_plugins():
    # List of plugin names as sequence
    codeflash_output = _get_plugin_specs_as_list(
        ["pluginA", "pluginB"]
    )  # 1.95μs -> 827ns (135% faster)


def test_sequence_tuple_of_plugins():
    # Tuple of plugin names as sequence
    codeflash_output = _get_plugin_specs_as_list(
        ("pluginA", "pluginB")
    )  # 3.29μs -> 1.01μs (225% faster)


def test_sequence_empty_list():
    # Empty list should return empty list
    codeflash_output = _get_plugin_specs_as_list([])  # 1.73μs -> 791ns (119% faster)


def test_sequence_empty_tuple():
    # Empty tuple should return empty list
    codeflash_output = _get_plugin_specs_as_list(())  # 2.99μs -> 989ns (203% faster)


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


def test_string_with_extra_commas():
    # String with leading, trailing, and consecutive commas
    codeflash_output = _get_plugin_specs_as_list(
        ",pluginA,,pluginB,"
    )  # 1.03μs -> 979ns (5.52% faster)


def test_string_with_spaces_in_plugin_names():
    # Spaces are not stripped, so they remain in output
    codeflash_output = _get_plugin_specs_as_list(
        " pluginA , pluginB "
    )  # 905ns -> 875ns (3.43% faster)


def test_sequence_with_non_string_elements():
    # Sequence with non-string elements should be returned as is
    codeflash_output = _get_plugin_specs_as_list(
        [1, 2, 3]
    )  # 1.90μs -> 826ns (130% faster)


def test_sequence_with_mixed_types():
    # Sequence with mixed types is returned as is
    codeflash_output = _get_plugin_specs_as_list(
        ["pluginA", 42, None]
    )  # 1.73μs -> 832ns (108% faster)


def test_int_input_raises_usageerror():
    # Integer input should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(123)  # 3.21μs -> 3.39μs (5.14% slower)


def test_dict_input_raises_usageerror():
    # Dict input should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list({"pluginA": True})  # 4.32μs -> 4.35μs (0.759% slower)


def test_set_input_raises_usageerror():
    # Set input is not a sequence, should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(
            {"pluginA", "pluginB"}
        )  # 4.46μs -> 4.59μs (2.83% slower)


def test_frozenset_input_raises_usageerror():
    # Frozenset input is not a sequence, should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(
            frozenset(["pluginA", "pluginB"])
        )  # 4.43μs -> 4.64μs (4.59% slower)


def test_string_with_only_commas():
    # String containing only commas should return a list of empty strings
    codeflash_output = _get_plugin_specs_as_list(
        ",,,"
    )  # 1.04μs -> 1.02μs (1.85% faster)


def test_string_with_unicode_plugin_names():
    # Unicode plugin names in string
    codeflash_output = _get_plugin_specs_as_list(
        "плагинA,插件B"
    )  # 1.48μs -> 1.42μs (4.59% faster)


def test_sequence_with_unicode_plugin_names():
    # Unicode plugin names in sequence
    codeflash_output = _get_plugin_specs_as_list(
        ["плагинA", "插件B"]
    )  # 1.99μs -> 806ns (147% faster)


def test_string_with_newline_and_tab_characters():
    # String with newline and tab characters
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA\n,pluginB\t"
    )  # 914ns -> 819ns (11.6% faster)


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


def test_large_comma_separated_string():
    # Large number of plugins in a comma-separated string
    plugins = ",".join(f"plugin{i}" for i in range(1000))
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 12.0μs -> 11.4μs (5.28% faster)


def test_large_sequence_of_plugins():
    # Large sequence of plugin names
    plugins = [f"plugin{i}" for i in range(1000)]
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 3.27μs -> 2.23μs (46.3% faster)


def test_large_tuple_of_plugins():
    # Large tuple of plugin names
    plugins = tuple(f"plugin{i}" for i in range(1000))
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 4.69μs -> 2.40μs (95.5% faster)


def test_large_string_with_commas_and_empty_plugins():
    # Large string with empty plugin names between commas
    plugins = ",".join([""] * 1000)
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 7.99μs -> 8.06μs (0.782% slower)


def test_large_sequence_with_non_string_elements():
    # Large sequence with non-string elements
    plugins = [i for i in range(1000)]
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 3.07μs -> 2.03μs (51.2% faster)


# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
import types

from _pytest.config.__init__ import _get_plugin_specs_as_list
from _pytest.config.exceptions import UsageError

# imports
import pytest  # used for our unit tests


# unit tests

# ----------------------
# Basic Test Cases
# ----------------------


def test_none_returns_empty_list():
    # None input should return empty list
    codeflash_output = _get_plugin_specs_as_list(None)  # 403ns -> 360ns (11.9% faster)


def test_module_type_returns_empty_list():
    # A module type should return empty list
    dummy_module = types.ModuleType("pytest_plugins")
    codeflash_output = _get_plugin_specs_as_list(
        dummy_module
    )  # 581ns -> 530ns (9.62% faster)


def test_empty_string_returns_empty_list():
    # Empty string input should return empty list
    codeflash_output = _get_plugin_specs_as_list("")  # 679ns -> 661ns (2.72% faster)


def test_single_plugin_string():
    # Single plugin name as string should return list with one element
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA"
    )  # 873ns -> 846ns (3.19% faster)


def test_comma_separated_string():
    # Comma separated plugin names should return list of names
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA,pluginB,pluginC"
    )  # 932ns -> 871ns (7.00% faster)


def test_sequence_of_strings():
    # Sequence (list) of plugin names should return same list
    codeflash_output = _get_plugin_specs_as_list(
        ["pluginA", "pluginB"]
    )  # 2.04μs -> 832ns (146% faster)


def test_tuple_of_strings():
    # Tuple of plugin names should return list of names
    codeflash_output = _get_plugin_specs_as_list(
        ("pluginA", "pluginB")
    )  # 3.19μs -> 982ns (225% faster)


def test_list_with_empty_string():
    # List with empty string should return list with empty string
    codeflash_output = _get_plugin_specs_as_list([""])  # 1.76μs -> 810ns (117% faster)


def test_list_with_commas_in_items():
    # List with comma in item should not split the item
    codeflash_output = _get_plugin_specs_as_list(
        ["pluginA,pluginB"]
    )  # 1.65μs -> 803ns (105% faster)


# ----------------------
# Edge Test Cases
# ----------------------


def test_string_with_trailing_comma():
    # String with trailing comma should include empty string at end
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA,"
    )  # 900ns -> 930ns (3.23% slower)


def test_string_with_leading_comma():
    # String with leading comma should include empty string at beginning
    codeflash_output = _get_plugin_specs_as_list(
        ",pluginA"
    )  # 935ns -> 901ns (3.77% faster)


def test_string_with_multiple_consecutive_commas():
    # Multiple consecutive commas should produce empty string elements
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA,,pluginB"
    )  # 950ns -> 933ns (1.82% faster)


def test_string_with_only_commas():
    # String with only commas should produce empty string elements
    codeflash_output = _get_plugin_specs_as_list(",,")  # 976ns -> 882ns (10.7% faster)


def test_sequence_with_non_string_elements():
    # Sequence with non-string elements should include them as-is
    codeflash_output = _get_plugin_specs_as_list(
        [1, 2, 3]
    )  # 1.92μs -> 788ns (144% faster)


def test_sequence_with_mixed_types():
    # Sequence with mixed types should include all as-is
    codeflash_output = _get_plugin_specs_as_list(
        ["pluginA", 42, None]
    )  # 1.76μs -> 815ns (116% faster)


def test_int_input_raises_usage_error():
    # Integer input should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(123)  # 3.27μs -> 3.34μs (2.13% slower)


def test_dict_input_raises_usage_error():
    # Dict input should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list({"pluginA": True})  # 4.28μs -> 4.36μs (1.79% slower)


def test_set_input_raises_usage_error():
    # Set input should raise UsageError (not a Sequence)
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(
            {"pluginA", "pluginB"}
        )  # 4.42μs -> 4.57μs (3.28% slower)


def test_frozenset_input_raises_usage_error():
    # Frozenset input should raise UsageError (not a Sequence)
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(
            frozenset(["pluginA", "pluginB"])
        )  # 4.42μs -> 4.57μs (3.18% slower)


def test_bool_input_raises_usage_error():
    # Boolean input should raise UsageError
    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(True)  # 2.69μs -> 2.72μs (1.03% slower)


def test_object_input_raises_usage_error():
    # Arbitrary object input should raise UsageError
    class Dummy:
        pass

    with pytest.raises(UsageError):
        _get_plugin_specs_as_list(Dummy())  # 13.6μs -> 14.0μs (3.24% slower)


def test_string_with_spaces():
    # String with spaces around commas should not strip spaces
    codeflash_output = _get_plugin_specs_as_list(
        "pluginA, pluginB"
    )  # 990ns -> 936ns (5.77% faster)


def test_unicode_string():
    # Unicode characters in plugin name should be handled
    codeflash_output = _get_plugin_specs_as_list(
        "plügïnA,插件B"
    )  # 1.58μs -> 1.49μs (5.91% faster)


# ----------------------
# Large Scale Test Cases
# ----------------------


def test_large_list_of_plugins():
    # Large list of plugin names should be handled efficiently
    plugins = [f"plugin{i}" for i in range(1000)]
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 3.48μs -> 2.27μs (53.3% faster)


def test_large_comma_separated_string():
    # Large comma-separated string of plugin names should be split correctly
    plugins = [f"plugin{i}" for i in range(1000)]
    plugin_str = ",".join(plugins)
    codeflash_output = _get_plugin_specs_as_list(plugin_str)
    result = codeflash_output  # 12.5μs -> 12.6μs (1.10% slower)


def test_large_tuple_of_plugins():
    # Large tuple of plugin names should be handled efficiently
    plugins = tuple(f"plugin{i}" for i in range(1000))
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 4.92μs -> 2.44μs (102% faster)


def test_large_list_with_non_string_elements():
    # Large list with non-string elements should include all as-is
    plugins = [i for i in range(1000)]
    codeflash_output = _get_plugin_specs_as_list(plugins)
    result = codeflash_output  # 3.04μs -> 2.06μs (47.8% faster)
from _pytest.config.__init__ import _get_plugin_specs_as_list


def test__get_plugin_specs_as_list():
    _get_plugin_specs_as_list(())


def test__get_plugin_specs_as_list_2():
    _get_plugin_specs_as_list(",")


def test__get_plugin_specs_as_list_3():
    _get_plugin_specs_as_list("")


def test__get_plugin_specs_as_list_4():
    _get_plugin_specs_as_list(None)
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic__lsdxkww/tmp9vbgkavn/test_concolic_coverage.py::test__get_plugin_specs_as_list 3.76μs 1.18μs 219%✅
codeflash_concolic__lsdxkww/tmp9vbgkavn/test_concolic_coverage.py::test__get_plugin_specs_as_list_2 1.03μs 984ns 5.18%✅
codeflash_concolic__lsdxkww/tmp9vbgkavn/test_concolic_coverage.py::test__get_plugin_specs_as_list_3 750ns 709ns 5.78%✅
codeflash_concolic__lsdxkww/tmp9vbgkavn/test_concolic_coverage.py::test__get_plugin_specs_as_list_4 408ns 393ns 3.82%✅

To edit these changes git checkout codeflash/optimize-_get_plugin_specs_as_list-mi9oh0wn and push.

Codeflash Static Badge

The optimization achieves a **19% speedup** by adding a fast-path check for common sequence types (`list` and `tuple`) before falling back to the more expensive `isinstance(specs, collections.abc.Sequence)` check.

**Key changes:**
- Added explicit `type(specs) is list or type(specs) is tuple` check that handles the most common sequence cases
- Moved the expensive `collections.abc.Sequence` check to only execute for less common sequence-like objects

**Why this is faster:**
The `isinstance(specs, collections.abc.Sequence)` call requires protocol checking and ABC machinery, which is significantly slower than direct type comparisons. The line profiler shows this check took **49.8% of total time** in the original code but only **33% in the optimized version**.

**Performance benefits by test case:**
- **List inputs**: 135-147% faster (most dramatic improvement)
- **Tuple inputs**: 203-225% faster (biggest gains)
- **Large sequences**: 46-102% faster for large lists/tuples
- **String/None inputs**: Modest 3-12% improvements
- **Error cases**: Slightly slower (2-5%) due to additional type check, but these are exceptional cases

**Impact on workloads:**
Given the function reference shows this is called from `_import_plugin_specs()` during plugin loading, the optimization will significantly speed up pytest initialization when plugins are specified as lists or tuples - common patterns in pytest configuration and programmatic usage.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 22, 2025 02:36
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Nov 22, 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