Skip to main content

Wrapper around xlsxwriter to improve usability

Project description

Excelipy

codecov

Installation

You can install the package using pip:

pip install excelipy

Or add it as a dependency in your pyproject.toml:

uv add excelipy

Appeal

This was created with less than 100 lines of code, in a declarative, clean, and easy to understand style:

demo_example.png

Usage

Detailed Model Overview

Excelipy is built on top of Pydantic models, making it easy to define your Excel structure programmatically.

Excel

The top-level container for your workbook.

  • path: Path to the output file (string, Path, or BytesIO).
  • sheets: A list of Sheet objects.
  • nan_inf_to_errors: If True (default), converts NaN and Inf values to Excel errors.

Sheet

Represents a single worksheet.

  • name: The name of the sheet.
  • components: A list of components (Tables, Text, etc.) to be rendered sequentially.
  • grid_lines: Boolean to show or hide grid lines.
  • style: A Style object to apply to the entire sheet (e.g., padding).

Table

The main component for rendering data.

  • data: A pandas.DataFrame.
  • header_style: A dictionary mapping column names to Style objects.
  • body_style: A default Style for the table body.
  • column_style: Styles for specific columns. Can be a Style object or a function that returns a Style based on cell value.
  • row_style: Styles for specific rows.
  • header_filters: Boolean to enable/disable Excel header filters.
  • merge_equal_headers: Boolean. If True, adjacent columns with the same name will have their headers merged.

Style

Defines how cells look. Supports most common Excel formatting:

  • Font: bold, font_color, font_family, font_size, underline.
  • Alignment: align (left, center, right, etc.), valign (top, vcenter, bottom), text_wrap.
  • Border: border (sets all sides) or individual border_top, border_bottom, etc. border_color.
  • Background: background (hex color).
  • Format: numeric_format (e.g., "0.00%", "#,##0.00").

Other Components

  • Text: A simple text cell or merged range. Supports width, height, and style.
  • Link: A clickable hyperlink (This can be nested inside a Table component).
  • Image: Embeds an image from a path.
  • Fill: Empty space component to push other components down or across.

AI Integration

Excelipy exposes ep.AI_GUIDE — a prompt string describing its full API — and Pydantic-compatible schemas so any LLM with structured output can generate excelipy components directly.

Pass ep.AI_GUIDE as the system prompt, use any component's .model_json_schema() to constrain the output, then validate the result with .model_validate(). Any Excelipy model can be targeted — ep.Table, ep.Text, ep.Sheet, etc. The more complex the schema, the stronger the model needs to be.

import excelipy as ep
from pathlib import Path
from langchain_core.messages import SystemMessage, HumanMessage
from langchain_ollama import ChatOllama

model = ChatOllama(model="qwen2.5:7b")

# Any component can be targeted
target = ep.Table  # or ep.Text, ep.Sheet, ...

result = target.model_validate(
    model
    .with_structured_output(schema=target.model_json_schema())
    .invoke([
        SystemMessage(ep.AI_GUIDE),
        HumanMessage("Create a mocked social platform interaction table and style it"),
    ])
)

ep.save(ep.Excel(
    path=Path("ai_output.xlsx"),
    sheets=[ep.Sheet(name="Sheet1", components=[result])],
))

This works with any framework that supports structured output (LangChain, the OpenAI SDK, the Anthropic SDK, etc.) — just swap the model and client.

Examples

Displaying a table

The most basic usage is to display a pandas.DataFrame as a table.

import excelipy as ep

df = ...  # your pandas DataFrame

ep.save(
    excel=ep.Excel(
        path="output.xlsx",
        sheets=[ep.Sheet(name="Sheet1", components=[ep.Table(data=df)])],
    )
)

By default, ep.Table renders a pandas.DataFrame with standard formatting, including header filters and automatic column width adjustment.

displaying_a_table.png

Basic column formatting

You can apply styles to headers and specific columns. Here, we use header_style to bold and center the headers, and column_style to format the "Value" column with two decimal places.

ep.save(
    excel=ep.Excel(
        path="output.xlsx",
        sheets=[
            ep.Sheet(
                name="Sheet1",
                components=[
                    ep.Table(
                        data=df,
                        header_style={
                            col: ep.Style(
                                bold=True, align="center", valign="vcenter"
                            )
                            for col in df.columns
                        },
                        column_style={"Value": ep.Style(numeric_format=",.2f")},
                    )
                ],
            )
        ],
    )
)

The output shows bold, centered headers and a formatted numeric column.

basic_column_formatting.png

Adding a title

You can add text components above or around your tables. In this example, an ep.Text component is added before the ep.Table. We set its width to match the number of columns in the table and apply a background color and centered alignment to create a unified title bar.

num_cols = len(df.columns)
ep.save(
    excel=ep.Excel(
        path="output.xlsx",
        sheets=[
            ep.Sheet(
                name="Sheet1",
                components=[
                    ep.Text(
                        text="Sales by Product",
                        width=num_cols,
                        style=ep.Style(
                            bold=True,
                            background="#ecedef",
                            align="center",
                            valign="vcenter",
                        ),
                    ),
                    ep.Table(
                        data=df,
                        header_style={
                            col: ep.Style(
                                bold=True, align="center", valign="vcenter"
                            )
                            for col in df.columns
                        },
                        column_style={"Value": ep.Style(numeric_format=",.2f")},
                    ),
                ],
            )
        ],
    )
)

The resulting Excel sheet features a stylized title spanning across the top of the table.

adding_a_title.png

Category coloring

Styles can be applied dynamically based on the cell content. By passing a function to column_style, we can change the background and font color of the "Store" column depending on its value, making different categories easy to distinguish.

def get_store_color(store: str) -> ep.Style:
    return ep.Style(
        background=store_colors[store],
        font_color=choose_font_color(store_colors[store]),
        bold=True,
    )


ep.save(
    excel=ep.Excel(
        path="output.xlsx",
        sheets=[
            ep.Sheet(
                name="Sheet1",
                components=[
                    ep.Table(
                        data=df,
                        column_style={
                            "Value": ep.Style(numeric_format=",.2f"),
                            "Store": get_store_color,
                        },
                    ),
                ],
            )
        ],
    )
)

Each store now has its own unique color coding in the "Store" column.

category_coloring.png

Merging columns

By giving multiple columns the same name and setting merge_equal_headers=True (which is the default), they will be merged in the header. In this example, all columns are renamed to the same title, creating a single merged header over the entire table. We also use idx_column_style to apply styles by column index instead of name.

unified = "Sales by Product by Store"
df = df.rename(columns={col: unified for col in df.columns})

ep.save(
    excel=ep.Excel(
        path="output.xlsx",
        sheets=[
            ep.Sheet(
                name="Sheet1",
                components=[
                    ep.Table(
                        data=df,
                        header_style={
                            col: ep.Style(
                                bold=True, align="center", valign="vcenter"
                            )
                            for col in df.columns
                        },
                        body_style=ep.Style(align="center", valign="vcenter"),
                        idx_column_style={
                            0: get_store_color,
                            2: ep.Style(numeric_format=",.2f"),
                        },
                        header_filters=False,
                    )
                ],
            )
        ],
    )
)

The header columns are merged into one, and the body cells are centered.

merging_columns.png

Conditional formatting

You can also apply row-wise conditional formatting. Using the @ep.row_wise decorator on a styling function allows you to access the entire row's data. Here, we highlight "Value" in red if it falls below the average for that product. We also hide grid_lines and add padding to the sheet for a cleaner look.

@ep.row_wise
def get_value_style(row) -> ep.Style:
    store, product, value = row
    prod_avg = avg_by_product[product]
    if value < prod_avg:
        return ep.Style(font_color="#ff0014", numeric_format=",.2f", bold=True)
    return ep.Style(numeric_format=",.2f", bold=True)


ep.save(
    excel=ep.Excel(
        path="output.xlsx",
        sheets=[
            ep.Sheet(
                name="Sheet1",
                components=[
                    ep.Table(
                        data=df,
                        idx_column_style={
                            0: get_store_color,
                            2: get_value_style,
                        },
                        header_filters=False,
                    ),
                    ep.Fill(width=num_cols),
                    ep.Text(
                        text="Products that sold below average are highlighted in red",
                        style=ep.Style(
                            bold=True,
                            valign="vcenter",
                            align="center",
                            border=3,
                            border_color="#ff0014",
                        ),
                        width=num_cols,
                        height=3,
                    ),
                ],
                grid_lines=False,
                style=ep.Style(padding=2),
            )
        ],
    )
)

The final output features conditional red text for below-average sales, a descriptive footer box, and no visible grid lines.

conditional_formatting.png

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

excelipy-1.1.3.tar.gz (20.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

excelipy-1.1.3-py3-none-any.whl (19.5 kB view details)

Uploaded Python 3

File details

Details for the file excelipy-1.1.3.tar.gz.

File metadata

  • Download URL: excelipy-1.1.3.tar.gz
  • Upload date:
  • Size: 20.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for excelipy-1.1.3.tar.gz
Algorithm Hash digest
SHA256 7c6ff67e58943359f6dc01833f2786a235f84eb033f6593129480522d2e2f349
MD5 b6289f387506f9956ef5bd7c7226d0f5
BLAKE2b-256 8f70d11b5cd4fb59db58839f12dacd436f438b910340f54a372bd6ce60227ff6

See more details on using hashes here.

File details

Details for the file excelipy-1.1.3-py3-none-any.whl.

File metadata

  • Download URL: excelipy-1.1.3-py3-none-any.whl
  • Upload date:
  • Size: 19.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for excelipy-1.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 0b7bf5d428fd7ddc98953b7c202ccfcc78765b724c946a31a5d067f6d78e29e3
MD5 ee038cb2a6627de218bdc29bb182e277
BLAKE2b-256 681c08894a82c0ada776a904dc50de28e764ffa045955429b3b317a916cde048

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page