Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 2,084% (20.84x) speedup for create_pyi in gradio/component_meta.py

⏱️ Runtime : 190 milliseconds 8.70 milliseconds (best of 72 runs)

📝 Explanation and details

The optimization achieves a 20x speedup by implementing template caching to eliminate repeated Jinja2 template compilation overhead.

Key optimization:

  • Template caching: The original code compiled Template(INTERFACE_TEMPLATE) on every function call (94.7% of runtime). The optimized version caches the compiled template as a function attribute create_pyi._template, compiling it only once on the first call.

Performance impact:

  • Template compilation drops from 94.7% of runtime (845ms across 195 calls) to just 8.4% on first call only
  • Subsequent calls skip compilation entirely, accessing the cached template in ~0.45μs vs ~4ms for recompilation
  • Overall runtime reduces from 190ms to 8.7ms

When this optimization excels:
Based on the test results, the optimization provides consistent 50-80x speedups for typical use cases with small to medium numbers of events (most tests show 5000-8000% improvements). For large-scale cases with 1000+ events, the speedup is more modest (80-90%) since template rendering becomes the dominant cost, but still significant.

This pattern is ideal for code generation workflows where the same template is used repeatedly with different data, making template compilation caching a highly effective optimization.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 34 Passed
⏪ Replay Tests 81 Passed
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
from typing import Any

# imports
import pytest  # used for our unit tests
from gradio.component_meta import create_pyi
# function to test (copied from above)
from jinja2 import Template

INTERFACE_TEMPLATE = '''
{{ contents }}
    from typing import Callable, Literal, Sequence, Any, TYPE_CHECKING
    from gradio.blocks import Block
    if TYPE_CHECKING:
        from gradio.components import Timer
        from gradio.components.base import Component

    {% for event in events %}
    def {{ event.event_name }}(self,
        fn: Callable[..., Any] | None = None,
        inputs: Block | Sequence[Block] | set[Block] | None = None,
        outputs: Block | Sequence[Block] | None = None,
        api_name: str | None | Literal[False] = None,
        scroll_to_output: bool = False,
        show_progress: Literal["full", "minimal", "hidden"] = "full",
        show_progress_on: Component | Sequence[Component] | None = None,
        queue: bool | None = None,
        batch: bool = False,
        max_batch_size: int = 4,
        preprocess: bool = True,
        postprocess: bool = True,
        cancels: dict[str, Any] | list[dict[str, Any]] | None = None,
        every: Timer | float | None = None,
        trigger_mode: Literal["once", "multiple", "always_last"] | None = None,
        js: str | Literal[True] | None = None,
        concurrency_limit: int | None | Literal["default"] = "default",
        concurrency_id: str | None = None,
        show_api: bool = True,
        key: int | str | tuple[int | str, ...] | None = None,
        api_description: str | None | Literal[False] = None,
        validator: Callable[..., Any] | None = None,
    {% for arg in event.event_specific_args %}
        {{ arg.name }}: {{ arg.type }},
    {% endfor %}
        ) -> Dependency:
        """
        Parameters:
            fn: the function to call when this event is triggered. Often a machine learning model's prediction function. Each parameter of the function corresponds to one input component, and the function should return a single value or a tuple of values, with each element in the tuple corresponding to one output component.
            inputs: list of gradio.components to use as inputs. If the function takes no inputs, this should be an empty list.
            outputs: list of gradio.components to use as outputs. If the function returns no outputs, this should be an empty list.
            api_name: defines how the endpoint appears in the API docs. Can be a string, None, or False. If False, the endpoint will not be exposed in the api docs. If set to None, will use the functions name as the endpoint route. If set to a string, the endpoint will be exposed in the api docs with the given name.
            scroll_to_output: if True, will scroll to output component on completion
            show_progress: how to show the progress animation while event is running: "full" shows a spinner which covers the output component area as well as a runtime display in the upper right corner, "minimal" only shows the runtime display, "hidden" shows no progress animation at all
            show_progress_on: Component or list of components to show the progress animation on. If None, will show the progress animation on all of the output components.
            queue: if True, will place the request on the queue, if the queue has been enabled. If False, will not put this event on the queue, even if the queue has been enabled. If None, will use the queue setting of the gradio app.
            batch: if True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. The lists should be of equal length (and be up to length `max_batch_size`). The function is then *required* to return a tuple of lists (even if there is only 1 output component), with each list in the tuple corresponding to one output component.
            max_batch_size: maximum number of inputs to batch together if this is called from the queue (only relevant if batch=True)
            preprocess: if False, will not run preprocessing of component data before running 'fn' (e.g. leaving it as a base64 string if this method is called with the `Image` component).
            postprocess: if False, will not run postprocessing of component data before returning 'fn' output to the browser.
            cancels: a list of other events to cancel when this listener is triggered. For example, setting cancels=[click_event] will cancel the click_event, where click_event is the return value of another components .click method. Functions that have not yet run (or generators that are iterating) will be cancelled, but functions that are currently running will be allowed to finish.
            every: continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
            trigger_mode: if "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` and `.key_up()` events) would allow a second submission after the pending event is complete.
            js: optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components.
            concurrency_limit: if set, this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `Blocks.queue()`, which itself is 1 by default).
            concurrency_id: if set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
            show_api: whether to show this event in the "view API" page of the Gradio app, or in the ".view_api()" method of the Gradio clients. Unlike setting api_name to False, setting show_api to False will still allow downstream apps as well as the Clients to use this event. If fn is None, show_api will automatically be set to False.
            key: A unique key for this event listener to be used in @gr.render(). If set, this value identifies an event as identical across re-renders when the key is identical.
            api_description: Description of the API endpoint. Can be a string, None, or False. If set to a string, the endpoint will be exposed in the API docs with the given description. If None, the function's docstring will be used as the API endpoint description. If False, then no description will be displayed in the API docs.
            validator: Optional validation function to run before the main function. If provided, this function will be executed first with queue=False, and only if it completes successfully will the main function be called. The validator receives the same inputs as the main function.
        {% for arg in event.event_specific_args %}
            {{ arg.name }}: {{ arg.doc }},
        {% endfor %}
        """
        ...
    {% endfor %}
'''

# Minimal EventListener class for testing
class EventListener:
    def __init__(self, event_name: str, event_specific_args: list[dict[str, Any]]):
        self.event_name = event_name
        self.event_specific_args = event_specific_args
from gradio.component_meta import create_pyi

# --------- UNIT TESTS ---------

# 1. Basic Test Cases

def test_single_event_string_no_args():
    # Test with a single event name as string, no specific args
    class_code = "class MyBlock:\n    pass"
    events = ["click"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.66ms -> 29.2μs (5582% faster)

def test_single_eventlistener_with_args():
    # Test with EventListener object with event-specific args
    class_code = "class MyBlock:\n    pass"
    events = [
        EventListener(
            event_name="change",
            event_specific_args=[
                {"name": "value", "type": "int", "doc": "The new value."},
                {"name": "old_value", "type": "int", "doc": "The previous value."}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.64ms -> 29.6μs (5439% faster)

def test_multiple_events_mixed_types():
    # Test with a mix of strings and EventListener objects
    class_code = "class MyBlock:\n    pass"
    events = [
        "submit",
        EventListener(
            event_name="update",
            event_specific_args=[
                {"name": "status", "type": "str", "doc": "Status message."}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.67ms -> 32.9μs (4967% faster)

def test_empty_events_list():
    # Test with empty events list
    class_code = "class EmptyBlock:\n    pass"
    events = []
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.64ms -> 19.6μs (8298% faster)

def test_class_code_is_inserted():
    # Test that class_code is inserted at the top of the template
    class_code = "class InsertedBlock:\n    pass"
    events = ["foo"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 28.4μs (5702% faster)

# 2. Edge Test Cases

def test_event_name_with_special_characters():
    # Event name with underscores and numbers
    class_code = "class SpecialBlock:\n    pass"
    events = ["on_123_event"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.67ms -> 27.7μs (5918% faster)

def test_eventlistener_with_empty_event_specific_args():
    # EventListener with empty event_specific_args
    class_code = "class Block:\n    pass"
    events = [EventListener(event_name="empty_args", event_specific_args=[])]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.67ms -> 29.8μs (5497% faster)

def test_eventlistener_with_many_event_specific_args():
    # EventListener with many event-specific args
    class_code = "class ManyArgsBlock:\n    pass"
    many_args = [
        {"name": f"arg{i}", "type": "str", "doc": f"Arg {i}."}
        for i in range(10)
    ]
    events = [EventListener(event_name="many_args", event_specific_args=many_args)]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.66ms -> 29.8μs (5460% faster)
    for i in range(10):
        pass

def test_eventlistener_with_reserved_python_keywords():
    # EventListener with event-specific arg named as a Python keyword
    class_code = "class KeywordBlock:\n    pass"
    events = [
        EventListener(
            event_name="keyword_event",
            event_specific_args=[
                {"name": "class", "type": "str", "doc": "Python keyword arg."}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.66ms -> 29.6μs (5500% faster)

def test_eventlistener_with_non_ascii_event_name_and_arg():
    # EventListener with non-ASCII event name and arg
    class_code = "class UnicodeBlock:\n    pass"
    events = [
        EventListener(
            event_name="événement",
            event_specific_args=[
                {"name": "valeur", "type": "str", "doc": "Valeur en français."}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.67ms -> 28.9μs (5677% faster)

def test_eventlistener_with_empty_string_event_name():
    # EventListener with empty string event name
    class_code = "class EmptyEventNameBlock:\n    pass"
    events = [
        EventListener(event_name="", event_specific_args=[])
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 28.9μs (5597% faster)

def test_eventlistener_with_none_event_specific_args():
    # EventListener with event_specific_args=None
    class_code = "class NoneArgsBlock:\n    pass"
    events = [
        EventListener(event_name="none_args", event_specific_args=None)
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 29.7μs (5473% faster)

def test_eventlistener_with_arg_missing_type_or_doc():
    # EventListener with arg missing type or doc
    class_code = "class MissingTypeDocBlock:\n    pass"
    events = [
        EventListener(
            event_name="incomplete_arg",
            event_specific_args=[
                {"name": "foo", "type": "", "doc": ""},
                {"name": "bar", "type": "int", "doc": ""},
                {"name": "baz", "type": "", "doc": "Baz doc."}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.66ms -> 28.2μs (5763% faster)

# 3. Large Scale Test Cases

def test_many_events_max_size():
    # Test with a large number of events (<=1000)
    class_code = "class LargeBlock:\n    pass"
    n_events = 1000
    events = [f"event_{i}" for i in range(n_events)]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 3.56ms -> 1.88ms (89.5% faster)
    # Check that all function definitions are present
    for i in range(n_events):
        pass

def test_many_event_specific_args_max_size():
    # Test with an event with a large number of event-specific args (<=1000)
    class_code = "class LargeArgsBlock:\n    pass"
    n_args = 1000
    args = [{"name": f"arg{i}", "type": "str", "doc": f"Arg {i}."} for i in range(n_args)]
    events = [EventListener(event_name="big_event", event_specific_args=args)]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 31.6μs (5115% faster)
    # Check that all args are present in signature and docstring
    for i in range(n_args):
        pass

def test_large_class_code():
    # Test with a large class code string
    class_code = "\n".join([f"class BigBlock{i}:\n    pass" for i in range(100)])
    events = ["large_event"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.66ms -> 27.3μs (5954% faster)
    # Check that all class definitions are present
    for i in range(100):
        pass

def test_performance_with_large_inputs():
    # Performance test: ensure function completes quickly with large inputs
    import time
    class_code = "class PerfBlock:\n    pass"
    n_events = 500
    n_args = 500
    events = [
        EventListener(
            event_name=f"event_{i}",
            event_specific_args=[
                {"name": f"arg{j}", "type": "str", "doc": f"Arg {j}."}
                for j in range(n_args)
            ]
        )
        for i in range(n_events)
    ]
    start = time.time()
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 3.02ms -> 1.12ms (169% faster)
    duration = time.time() - start
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from typing import Any

# imports
import pytest  # used for our unit tests
from gradio.component_meta import create_pyi
# Function to test (from gradio/component_meta.py)
from jinja2 import Template

INTERFACE_TEMPLATE = '''
{{ contents }}
    from typing import Callable, Literal, Sequence, Any, TYPE_CHECKING
    from gradio.blocks import Block
    if TYPE_CHECKING:
        from gradio.components import Timer
        from gradio.components.base import Component

    {% for event in events %}
    def {{ event.event_name }}(self,
        fn: Callable[..., Any] | None = None,
        inputs: Block | Sequence[Block] | set[Block] | None = None,
        outputs: Block | Sequence[Block] | None = None,
        api_name: str | None | Literal[False] = None,
        scroll_to_output: bool = False,
        show_progress: Literal["full", "minimal", "hidden"] = "full",
        show_progress_on: Component | Sequence[Component] | None = None,
        queue: bool | None = None,
        batch: bool = False,
        max_batch_size: int = 4,
        preprocess: bool = True,
        postprocess: bool = True,
        cancels: dict[str, Any] | list[dict[str, Any]] | None = None,
        every: Timer | float | None = None,
        trigger_mode: Literal["once", "multiple", "always_last"] | None = None,
        js: str | Literal[True] | None = None,
        concurrency_limit: int | None | Literal["default"] = "default",
        concurrency_id: str | None = None,
        show_api: bool = True,
        key: int | str | tuple[int | str, ...] | None = None,
        api_description: str | None | Literal[False] = None,
        validator: Callable[..., Any] | None = None,
    {% for arg in event.event_specific_args %}
        {{ arg.name }}: {{ arg.type }},
    {% endfor %}
        ) -> Dependency:
        """
        Parameters:
            fn: the function to call when this event is triggered. Often a machine learning model's prediction function. Each parameter of the function corresponds to one input component, and the function should return a single value or a tuple of values, with each element in the tuple corresponding to one output component.
            inputs: list of gradio.components to use as inputs. If the function takes no inputs, this should be an empty list.
            outputs: list of gradio.components to use as outputs. If the function returns no outputs, this should be an empty list.
            api_name: defines how the endpoint appears in the API docs. Can be a string, None, or False. If False, the endpoint will not be exposed in the api docs. If set to None, will use the functions name as the endpoint route. If set to a string, the endpoint will be exposed in the api docs with the given name.
            scroll_to_output: if True, will scroll to output component on completion
            show_progress: how to show the progress animation while event is running: "full" shows a spinner which covers the output component area as well as a runtime display in the upper right corner, "minimal" only shows the runtime display, "hidden" shows no progress animation at all
            show_progress_on: Component or list of components to show the progress animation on. If None, will show the progress animation on all of the output components.
            queue: if True, will place the request on the queue, if the queue has been enabled. If False, will not put this event on the queue, even if the queue has been enabled. If None, will use the queue setting of the gradio app.
            batch: if True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. The lists should be of equal length (and be up to length `max_batch_size`). The function is then *required* to return a tuple of lists (even if there is only 1 output component), with each list in the tuple corresponding to one output component.
            max_batch_size: maximum number of inputs to batch together if this is called from the queue (only relevant if batch=True)
            preprocess: if False, will not run preprocessing of component data before running 'fn' (e.g. leaving it as a base64 string if this method is called with the `Image` component).
            postprocess: if False, will not run postprocessing of component data before returning 'fn' output to the browser.
            cancels: a list of other events to cancel when this listener is triggered. For example, setting cancels=[click_event] will cancel the click_event, where click_event is the return value of another components .click method. Functions that have not yet run (or generators that are iterating) will be cancelled, but functions that are currently running will be allowed to finish.
            every: continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
            trigger_mode: if "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` and `.key_up()` events) would allow a second submission after the pending event is complete.
            js: optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components.
            concurrency_limit: if set, this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `Blocks.queue()`, which itself is 1 by default).
            concurrency_id: if set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
            show_api: whether to show this event in the "view API" page of the Gradio app, or in the ".view_api()" method of the Gradio clients. Unlike setting api_name to False, setting show_api to False will still allow downstream apps as well as the Clients to use this event. If fn is None, show_api will automatically be set to False.
            key: A unique key for this event listener to be used in @gr.render(). If set, this value identifies an event as identical across re-renders when the key is identical.
            api_description: Description of the API endpoint. Can be a string, None, or False. If set to a string, the endpoint will be exposed in the API docs with the given description. If None, the function's docstring will be used as the API endpoint description. If False, then no description will be displayed in the API docs.
            validator: Optional validation function to run before the main function. If provided, this function will be executed first with queue=False, and only if it completes successfully will the main function be called. The validator receives the same inputs as the main function.
        {% for arg in event.event_specific_args %}
            {{ arg.name }}: {{ arg.doc }},
        {% endfor %}
        """
        ...
    {% endfor %}
'''

class EventListener:
    def __init__(self, event_name: str, event_specific_args: list[dict[str, Any]]):
        self.event_name = event_name
        self.event_specific_args = event_specific_args
from gradio.component_meta import create_pyi

# ---- UNIT TESTS ----

# 1. Basic Test Cases

def test_basic_single_event_no_args():
    """
    Test with a single event name (string), no event-specific args.
    """
    class_code = "class Foo:\n    pass"
    events = ["click"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.63ms -> 29.1μs (5483% faster)

def test_basic_multiple_events_no_args():
    """
    Test with multiple event names (strings), no event-specific args.
    """
    class_code = "class Bar:\n    pass"
    events = ["submit", "change"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.64ms -> 30.6μs (5247% faster)

def test_basic_eventlistener_with_args():
    """
    Test with EventListener object, with event-specific args.
    """
    class_code = "class Baz:\n    pass"
    events = [
        EventListener(
            event_name="custom_event",
            event_specific_args=[
                {"name": "foo", "type": "int", "doc": "foo argument"},
                {"name": "bar", "type": "str", "doc": "bar argument"},
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.64ms -> 29.1μs (5553% faster)

def test_basic_mixed_event_types():
    """
    Test with a mix of strings and EventListener objects.
    """
    class_code = "class Mix:\n    pass"
    events = [
        "click",
        EventListener(
            event_name="special",
            event_specific_args=[
                {"name": "x", "type": "float", "doc": "x coordinate"}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.64ms -> 32.0μs (5027% faster)

# 2. Edge Test Cases

def test_edge_empty_events_list():
    """
    Test with empty events list.
    """
    class_code = "class Empty:\n    pass"
    events = []
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.63ms -> 19.5μs (8280% faster)

def test_edge_empty_class_code():
    """
    Test with empty class_code string.
    """
    class_code = ""
    events = ["change"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 27.3μs (5946% faster)

def test_edge_eventlistener_empty_args():
    """
    Test EventListener with empty event_specific_args.
    """
    class_code = "class Edge:\n    pass"
    events = [EventListener(event_name="edge_event", event_specific_args=[])]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.64ms -> 29.1μs (5533% faster)

def test_edge_event_name_special_characters():
    """
    Test event names with special characters (should be rendered as-is).
    """
    class_code = "class Special:\n    pass"
    events = ["on_click$", "on-change!"]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 29.8μs (5428% faster)

def test_edge_eventlistener_arg_types_and_docs():
    """
    Test EventListener with unusual types and docs.
    """
    class_code = "class Types:\n    pass"
    events = [
        EventListener(
            event_name="typed_event",
            event_specific_args=[
                {"name": "data", "type": "list[int]", "doc": "A list of integers"},
                {"name": "flag", "type": "bool | None", "doc": "Optional boolean flag"},
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.67ms -> 28.7μs (5715% faster)

def test_edge_eventlistener_duplicate_arg_names():
    """
    Test EventListener with duplicate argument names.
    Should render both, but Python signature would be invalid.
    """
    class_code = "class Dup:\n    pass"
    events = [
        EventListener(
            event_name="dup_event",
            event_specific_args=[
                {"name": "foo", "type": "int", "doc": "first foo"},
                {"name": "foo", "type": "str", "doc": "second foo"},
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.67ms -> 28.8μs (5692% faster)

def test_edge_eventlistener_no_event_name():
    """
    Test EventListener with empty event_name.
    """
    class_code = "class NoName:\n    pass"
    events = [
        EventListener(event_name="", event_specific_args=[])
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.66ms -> 28.7μs (5675% faster)

def test_edge_eventlistener_long_arg_name():
    """
    Test EventListener with a very long argument name.
    """
    class_code = "class LongArg:\n    pass"
    long_name = "a" * 100
    events = [
        EventListener(
            event_name="long_arg_event",
            event_specific_args=[
                {"name": long_name, "type": "str", "doc": "long arg name"}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.63ms -> 28.6μs (5605% faster)

# 3. Large Scale Test Cases

def test_large_many_events():
    """
    Test with a large number of events (up to 1000).
    """
    class_code = "class Large:\n    pass"
    events = [f"event_{i}" for i in range(1000)]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 3.55ms -> 1.86ms (91.1% faster)
    # Check that all event function definitions exist
    for i in range(1000):
        pass
    # Should not be excessively slow or crash

def test_large_many_event_specific_args():
    """
    Test EventListener with a large number of event-specific args (up to 1000).
    """
    class_code = "class LargeArgs:\n    pass"
    args = [{"name": f"arg{i}", "type": "int", "doc": f"arg{i} doc"} for i in range(1000)]
    events = [
        EventListener(event_name="big_event", event_specific_args=args)
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.63ms -> 32.1μs (4986% faster)
    # All args should be present in the function signature and docstring
    for i in range(1000):
        pass

def test_large_mixed_events_and_eventlisteners():
    """
    Test with a mix of many strings and EventListeners.
    """
    class_code = "class MixedLarge:\n    pass"
    events = [f"ev{i}" for i in range(500)] + [
        EventListener(
            event_name=f"custom_ev{i}",
            event_specific_args=[
                {"name": f"foo{i}", "type": "str", "doc": f"foo{i} doc"}
            ]
        )
        for i in range(500)
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 3.74ms -> 2.06ms (81.0% faster)
    # Check that all function definitions exist
    for i in range(500):
        pass

def test_large_eventlistener_long_event_name():
    """
    Test EventListener with a very long event name.
    """
    class_code = "class LongEventName:\n    pass"
    long_event_name = "event_" + "x" * 500
    events = [
        EventListener(event_name=long_event_name, event_specific_args=[])
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.65ms -> 29.6μs (5453% faster)

def test_large_eventlistener_long_docstring():
    """
    Test EventListener with a very long docstring in event_specific_args.
    """
    class_code = "class LongDoc:\n    pass"
    long_doc = "doc " + "y" * 500
    events = [
        EventListener(
            event_name="long_doc_event",
            event_specific_args=[
                {"name": "foo", "type": "str", "doc": long_doc}
            ]
        )
    ]
    codeflash_output = create_pyi(class_code, events); result = codeflash_output # 1.63ms -> 28.5μs (5639% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
⏪ Replay Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
test_pytest_testtest_components_py_testcomponentstest_audio_py_testcomponentstest_file_py_testcomponentst__replay_test_0.py::test_gradio_component_meta_create_pyi 126ms 911μs 13793%✅

To edit these changes git checkout codeflash/optimize-create_pyi-mhasettm and push.

Codeflash

The optimization achieves a **20x speedup** by implementing **template caching** to eliminate repeated Jinja2 template compilation overhead.

**Key optimization:**
- **Template caching**: The original code compiled `Template(INTERFACE_TEMPLATE)` on every function call (94.7% of runtime). The optimized version caches the compiled template as a function attribute `create_pyi._template`, compiling it only once on the first call.

**Performance impact:**
- Template compilation drops from 94.7% of runtime (845ms across 195 calls) to just 8.4% on first call only
- Subsequent calls skip compilation entirely, accessing the cached template in ~0.45μs vs ~4ms for recompilation
- Overall runtime reduces from 190ms to 8.7ms

**When this optimization excels:**
Based on the test results, the optimization provides consistent 50-80x speedups for typical use cases with small to medium numbers of events (most tests show 5000-8000% improvements). For large-scale cases with 1000+ events, the speedup is more modest (80-90%) since template rendering becomes the dominant cost, but still significant.

This pattern is ideal for code generation workflows where the same template is used repeatedly with different data, making template compilation caching a highly effective optimization.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 28, 2025 16:35
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 28, 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