Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Dec 9, 2025

📄 27% (0.27x) speedup for CSSToExcelConverter.build_xlstyle in pandas/io/formats/excel.py

⏱️ Runtime : 1.39 milliseconds 1.09 milliseconds (best of 23 runs)

📝 Explanation and details

The optimized code achieves a 27% speedup by eliminating unnecessary dictionary creation and subsequent cleanup operations. Here are the key optimizations:

1. Avoid None-value dictionary entries: Instead of creating dictionaries with None values and then removing them with remove_none(), the optimized code only adds entries when they have meaningful values. This is most impactful in build_font() where the original code created 9 dictionary entries unconditionally, but the optimized version conditionally adds only non-None entries.

2. Reduced remove_none() overhead: The profiler shows remove_none() took 22.7% of total time in the original code but only 10.2% in the optimized version. This dramatic reduction comes from having fewer None values to remove in the first place.

3. Skip empty border generation: In build_border(), the optimized code checks if a border side has any meaningful properties (style_xl or color_xl) before adding it to the output dictionary, avoiding creation of empty border entries.

4. Early returns for empty cases: Methods like build_number_format() and build_alignment() now return empty dictionaries immediately when no meaningful properties are found, avoiding unnecessary dictionary construction.

5. Local variable caching: Minor optimization in build_border() caches method references (color_to_excel, _border_style) as local variables to reduce attribute lookup overhead in the loop.

The test results show consistent improvements across all scenarios, with the largest gains (20-50%) appearing in cases with sparse property sets where many values would be None. This optimization is particularly valuable for Excel formatting operations that process many CSS declarations with varying completeness, as it reduces both memory allocation and cleanup overhead.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 160 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
# imports
import pytest
from pandas.io.formats.excel import CSSToExcelConverter

# function to test
# (CSSToExcelConverter class as provided above, assumed present in the same module)

# ---------------------------
# Unit Tests for build_xlstyle
# ---------------------------


@pytest.fixture
def converter():
    # Fixture to provide a fresh converter for each test
    return CSSToExcelConverter()


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


def test_basic_font_and_color(converter):
    # Basic font and color properties
    props = {
        "font-family": "Arial",
        "font-size": "12pt",
        "font-weight": "bold",
        "font-style": "italic",
        "color": "red",
        "background-color": "blue",
    }
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 20.9μs -> 18.0μs (15.7% faster)


def test_basic_alignment(converter):
    # Horizontal and vertical alignment
    props = {
        "text-align": "center",
        "vertical-align": "top",
        "white-space": "normal",
    }
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 15.3μs -> 12.9μs (18.8% faster)


def test_basic_border(converter):
    # Simple border properties
    props = {
        "border-top-style": "dotted",
        "border-top-width": "1pt",
        "border-top-color": "black",
    }
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 19.0μs -> 15.5μs (22.9% faster)
    # Other borders should be None
    for side in ["right", "bottom", "left"]:
        pass


def test_basic_number_format(converter):
    # Number format property
    props = {
        "number-format": "0.00",
    }
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 14.8μs -> 11.5μs (28.3% faster)


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


def test_hex_color(converter):
    # Hex color should be converted
    props = {"color": "#FF00FF"}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 18.9μs -> 15.7μs (20.5% faster)


def test_font_family_quotes_and_multiple(converter):
    # Quoted font family and fallback
    props = {"font-family": '"Times New Roman", serif, "FakeFont"'}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 22.7μs -> 18.5μs (22.8% faster)


def test_font_decoration_underline_strike(converter):
    # Multiple text-decoration values
    props = {"text-decoration": "underline line-through"}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 15.8μs -> 12.5μs (26.4% faster)


def test_font_decoration_none(converter):
    # No text-decoration
    props = {}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 14.4μs -> 11.3μs (27.5% faster)


def test_shadow_true(converter):
    # text-shadow present and nonzero
    props = {"text-shadow": "1px 1px 2px #000"}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 17.0μs -> 13.8μs (23.3% faster)


def test_shadow_false(converter):
    # text-shadow present but zero
    props = {"text-shadow": "0px 0px 0px #000"}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 16.8μs -> 13.5μs (24.7% faster)


def test_border_none(converter):
    # border-style none
    props = {
        "border-top-style": "none",
        "border-top-width": "1pt",
        "border-top-color": "black",
    }
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 16.4μs -> 13.1μs (24.8% faster)


def test_fill_transparent_none(converter):
    # Fill with transparent or none should not produce fill
    for val in ["transparent", "none"]:
        props = {"background-color": val}
        codeflash_output = converter.build_xlstyle(props)
        style = codeflash_output  # 27.1μs -> 20.6μs (32.0% faster)


def test_number_format_replace_section(converter):
    # Should replace § with ;
    props = {"number-format": "0.00§[Red]"}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 15.0μs -> 12.1μs (23.8% faster)


def test_bold_map_numeric(converter):
    # Numeric font-weight
    for weight in ["700", "800", "900"]:
        props = {"font-weight": weight}
        codeflash_output = converter.build_xlstyle(props)
        style = codeflash_output  # 31.3μs -> 24.2μs (29.2% faster)
    for weight in ["100", "200", "300", "400", "500"]:
        props = {"font-weight": weight}
        codeflash_output = converter.build_xlstyle(props)
        style = codeflash_output  # 35.3μs -> 23.1μs (52.8% faster)


def test_italic_map(converter):
    # Various font-style values
    for val, expected in [("italic", True), ("oblique", True), ("normal", False)]:
        props = {"font-style": val}
        codeflash_output = converter.build_xlstyle(props)
        style = codeflash_output  # 31.0μs -> 23.3μs (32.8% faster)


def test_font_size_units(converter):
    # Accepts pt and px (should convert px to pt)
    props = {"font-size": "16pt"}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 15.4μs -> 12.1μs (27.3% faster)


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


def test_large_number_of_properties(converter):
    # Many unrelated properties should not break function
    props = {f"custom-prop-{i}": f"value-{i}" for i in range(200)}
    # Add a few real ones
    props.update(
        {
            "font-family": "Arial",
            "font-size": "11pt",
            "color": "green",
            "background-color": "#123456",
            "text-align": "right",
            "vertical-align": "bottom",
            "border-left-style": "dashed",
            "border-left-width": "1pt",
            "border-left-color": "red",
        }
    )
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 25.1μs -> 22.7μs (10.7% faster)


def test_large_font_family_list(converter):
    # Many font names, should pick the first as name, first known as family
    font_families = ",".join([f"Font{i}" for i in range(100)] + ["serif"])
    props = {"font-family": font_families}
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 49.1μs -> 46.7μs (5.20% faster)


def test_all_borders(converter):
    # All border sides set
    props = {}
    for side in ["top", "right", "bottom", "left"]:
        props[f"border-{side}-style"] = "dotted"
        props[f"border-{side}-width"] = "1pt"
        props[f"border-{side}-color"] = "blue"
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 20.3μs -> 18.7μs (8.78% faster)
    for side in ["top", "right", "bottom", "left"]:
        pass


def test_large_scale_combined(converter):
    # Combine many properties, including edge cases
    props = {
        "font-family": '"Arial", serif, "FakeFont"',
        "font-size": "14pt",
        "font-weight": "bold",
        "font-style": "italic",
        "text-decoration": "underline",
        "color": "#ABCDEF",
        "background-color": "yellow",
        "text-align": "center",
        "vertical-align": "middle",
        "white-space": "normal",
        "border-top-style": "dashed",
        "border-top-width": "2pt",
        "border-top-color": "green",
        "border-bottom-style": "double",
        "border-bottom-width": "3pt",
        "border-bottom-color": "#000000",
        "number-format": "0.00§[Red]",
        "text-shadow": "2px 2px 5px #888",
    }
    codeflash_output = converter.build_xlstyle(props)
    style = codeflash_output  # 34.5μs -> 32.0μs (7.80% faster)
# imports
import pytest
from pandas.io.formats.excel import CSSToExcelConverter

# function to test (copied from above, already present)

# --- UNIT TESTS FOR CSSToExcelConverter.build_xlstyle ---


@pytest.fixture
def converter():
    # Provide a fresh instance for each test
    return CSSToExcelConverter()


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


def test_basic_font_bold(converter):
    # Basic font-weight bold test
    props = {"font-weight": "bold"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 18.3μs -> 14.9μs (23.0% faster)


def test_basic_font_italic(converter):
    # Basic font-style italic test
    props = {"font-style": "italic"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.8μs -> 12.8μs (23.1% faster)


def test_basic_font_size_pt(converter):
    # Basic font-size in pt
    props = {"font-size": "12pt"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 16.4μs -> 13.0μs (26.0% faster)


def test_basic_font_family(converter):
    # Basic font-family parsing
    props = {"font-family": "serif"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 18.0μs -> 14.4μs (25.2% faster)


def test_basic_color_named(converter):
    # Basic named color
    props = {"color": "red"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.9μs -> 12.6μs (26.0% faster)


def test_basic_background_color_hex(converter):
    # Basic hex background color
    props = {"background-color": "#00FF00"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 17.3μs -> 14.3μs (21.0% faster)


def test_basic_text_align(converter):
    # Basic text-align property
    props = {"text-align": "center"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.8μs -> 11.8μs (24.8% faster)


def test_basic_vertical_align(converter):
    # Basic vertical-align property
    props = {"vertical-align": "top"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.9μs -> 12.1μs (23.1% faster)


def test_basic_wrap_text(converter):
    # Basic wrap text
    props = {"white-space": "normal"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.0μs -> 12.3μs (22.2% faster)


def test_basic_number_format(converter):
    # Basic number format
    props = {"number-format": "0.00"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 17.7μs -> 14.1μs (25.3% faster)


def test_basic_underline_and_strike(converter):
    # Basic text-decoration underline and strike-through
    props = {"text-decoration": "underline line-through"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.9μs -> 13.1μs (20.7% faster)


def test_basic_shadow(converter):
    # Basic text-shadow with a digit triggers shadow
    props = {"text-shadow": "2px 2px 2px #000"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 17.0μs -> 13.4μs (26.7% faster)


def test_basic_shadow_none(converter):
    # No shadow if no digit
    props = {"text-shadow": "none"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 16.8μs -> 13.3μs (26.5% faster)


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


def test_edge_missing_properties(converter):
    # Empty props dict should return only keys with None or missing
    props = {}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.5μs -> 11.5μs (26.6% faster)
    # All values should be either missing or None
    for section in xlstyle:
        for key in xlstyle[section]:
            pass


def test_edge_invalid_font_weight(converter):
    # Invalid font-weight should not crash and should be None
    props = {"font-weight": "superheavy"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 17.6μs -> 14.0μs (25.6% faster)


def test_edge_invalid_font_style(converter):
    # Invalid font-style should not crash and should be None
    props = {"font-style": "slanted"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.7μs -> 12.2μs (28.8% faster)


def test_edge_font_family_quotes_and_spaces(converter):
    # Font-family with quotes and spaces
    props = {"font-family": "'Comic Sans MS', serif"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 20.4μs -> 17.5μs (17.0% faster)


def test_edge_font_family_multiple(converter):
    # Multiple font families, first is not mapped
    props = {"font-family": "FancyFont, serif"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 18.4μs -> 15.2μs (21.6% faster)


def test_edge_background_color_transparent(converter):
    # Transparent background should not produce fill
    props = {"background-color": "transparent"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.6μs -> 11.5μs (27.4% faster)


def test_edge_background_color_none(converter):
    # "none" background should not produce fill
    props = {"background-color": "none"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.8μs -> 11.3μs (31.9% faster)


def test_edge_border_none_hidden(converter):
    # Border style "none" or "hidden" should map to "none"
    for style in ("none", "hidden"):
        props = {"border-top-style": style}
        codeflash_output = converter.build_xlstyle(props)
        xlstyle = codeflash_output  # 24.6μs -> 19.6μs (25.5% faster)


def test_edge_border_missing_width_and_style(converter):
    # No width or style, should be None and border removed
    props = {"border-top-color": "blue"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.7μs -> 12.5μs (25.6% faster)


def test_edge_number_format_with_section_symbol(converter):
    # Section symbol replaced with semicolon
    props = {"number-format": "0§00"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.5μs -> 11.8μs (22.7% faster)


def test_edge_font_family_empty_string(converter):
    # Empty font-family should return None for name and family
    props = {"font-family": ""}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 17.1μs -> 13.8μs (24.1% faster)


def test_edge_text_decoration_none(converter):
    # text-decoration none
    props = {"text-decoration": "none"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 15.6μs -> 12.5μs (24.4% faster)


def test_edge_border_color_only(converter):
    # Only border color, no style or width
    props = {"border-top-color": "red"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 19.1μs -> 15.8μs (20.3% faster)


def test_edge_font_family_with_double_quotes(converter):
    # Font-family with double quotes
    props = {"font-family": '"Times New Roman", serif'}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 20.7μs -> 17.8μs (16.7% faster)


def test_edge_shadow_non_matching(converter):
    # text-shadow with no digit should be False
    props = {"text-shadow": "#000"}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 16.9μs -> 13.7μs (23.9% faster)


def test_edge_shadow_missing(converter):
    # text-shadow missing should be None
    props = {}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 14.8μs -> 11.3μs (31.2% faster)


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


def test_large_many_font_families(converter):
    # Many font families, first is not mapped
    families = ",".join([f"Font{i}" for i in range(50)] + ["serif"])
    props = {"font-family": families}
    codeflash_output = converter.build_xlstyle(props)
    xlstyle = codeflash_output  # 36.9μs -> 32.9μs (12.0% faster)


def test_large_fill_color_variety(converter):
    # Many different background colors
    for i in range(10):
        hex_color = f"#{i:02X}{i:02X}{i:02X}"
        props = {"background-color": hex_color}
        codeflash_output = converter.build_xlstyle(props)
        xlstyle = codeflash_output  # 91.1μs -> 65.3μs (39.4% faster)


def test_large_font_size_variety(converter):
    # Test a variety of font sizes
    for size in range(8, 30, 2):
        props = {"font-size": f"{size}pt"}
        codeflash_output = converter.build_xlstyle(props)
        xlstyle = codeflash_output  # 91.0μs -> 62.3μs (46.0% faster)


def test_large_font_weight_variety(converter):
    # Test all valid font-weight values
    for weight, expected in CSSToExcelConverter.BOLD_MAP.items():
        props = {"font-weight": weight}
        codeflash_output = converter.build_xlstyle(props)
        xlstyle = codeflash_output  # 100μs -> 68.9μs (45.7% faster)


def test_large_number_format_variety(converter):
    # Test a variety of number formats
    formats = ["0", "0.00", "#,##0", "0§00", "General"]
    expected = ["0", "0.00", "#,##0", "0;00", "General"]
    for fmt, exp in zip(formats, expected):
        props = {"number-format": fmt}
        codeflash_output = converter.build_xlstyle(props)
        xlstyle = codeflash_output  # 50.1μs -> 36.0μs (39.1% faster)


def test_large_shadow_variety(converter):
    # Test a variety of text-shadow values
    shadows = [
        "none",
        "1px 1px 1px #000",
        "0px 0px 0px #fff",
        "5px 0px 0px #333",
        "#000",
    ]
    expected = [False, True, False, True, False]
    for val, exp in zip(shadows, expected):
        props = {"text-shadow": val}
        codeflash_output = converter.build_xlstyle(props)
        xlstyle = codeflash_output  # 50.9μs -> 38.2μs (33.2% 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-CSSToExcelConverter.build_xlstyle-miy5upl6 and push.

Codeflash Static Badge

The optimized code achieves a **27% speedup** by eliminating unnecessary dictionary creation and subsequent cleanup operations. Here are the key optimizations:

**1. Avoid None-value dictionary entries**: Instead of creating dictionaries with None values and then removing them with `remove_none()`, the optimized code only adds entries when they have meaningful values. This is most impactful in `build_font()` where the original code created 9 dictionary entries unconditionally, but the optimized version conditionally adds only non-None entries.

**2. Reduced `remove_none()` overhead**: The profiler shows `remove_none()` took 22.7% of total time in the original code but only 10.2% in the optimized version. This dramatic reduction comes from having fewer None values to remove in the first place.

**3. Skip empty border generation**: In `build_border()`, the optimized code checks if a border side has any meaningful properties (`style_xl` or `color_xl`) before adding it to the output dictionary, avoiding creation of empty border entries.

**4. Early returns for empty cases**: Methods like `build_number_format()` and `build_alignment()` now return empty dictionaries immediately when no meaningful properties are found, avoiding unnecessary dictionary construction.

**5. Local variable caching**: Minor optimization in `build_border()` caches method references (`color_to_excel`, `_border_style`) as local variables to reduce attribute lookup overhead in the loop.

The test results show consistent improvements across all scenarios, with the largest gains (20-50%) appearing in cases with sparse property sets where many values would be None. This optimization is particularly valuable for Excel formatting operations that process many CSS declarations with varying completeness, as it reduces both memory allocation and cleanup overhead.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 December 9, 2025 05:49
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Dec 9, 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