Skip to content

hbagheri/PythonChartHandler

Repository files navigation

ChartHandler (Python)

Pure-Python charting with pluggable backends and base64 / data-URI output — designed so charts drop straight into HTML emails and into any web framework (Django, Flask, and ASGI apps under uvicorn).

from charthandler import Chart

# A PNG <img> tag you can paste into an HTML email — no external request.
html = Chart.pie({"Chrome": 63, "Firefox": 19, "Safari": 18}).title("Browser share").to_email_img()

Why this design

  • The SVG backend is pure Python with zero dependencies — installs instantly anywhere (slim Docker, Alpine, serverless) and works identically under Django, Flask, FastAPI/uvicorn, etc. (a chart is just str/bytes, so it's framework-agnostic).
  • Raster (PNG/JPEG/GIF/WebP) is an optional extra via Pillow — needed because SVG doesn't render in some email clients (Outlook desktop). Install only if you need it.

Features

  • Chart types: pie, donut, bar, stacked bar, line, area, scatter, and combo (mixed bars + line/area with an independently-scaled secondary axis).
  • Outputs: raw bytes, file, base64, data: URI, ready-to-embed <img> tag.
  • Fully type-hinted (py.typed), tested, mypy-strict + ruff clean.

Install

pip install pythoncharthandler            # SVG only — zero dependencies
pip install "pythoncharthandler[raster]"  # + PNG/JPEG/GIF/WebP via Pillow

Quickstart

from charthandler import Chart, Axis, Series, LegendPosition

# Pie -> SVG string (no dependencies)
Chart.pie({"Chrome": 63, "Firefox": 19, "Safari": 18}).title("Share").to_svg()

# Bar -> PNG bytes (needs the [raster] extra)
Chart.bar([12, 19, 7, 22, 15]).categories(["Jan", "Feb", "Mar", "Apr", "May"]).to_png()

# Multi-series line, saved (format inferred from the extension)
Chart.line([Series.from_values("2024", [10, 14, 9, 18])]) \
    .add_series(Series.from_values("2025", [13, 11, 17, 21])) \
    .categories(["Q1", "Q2", "Q3", "Q4"]).save("signups.svg")

# Stacked bar
Chart.stacked_bar([
    Series.from_values("Direct", [12, 19, 15]),
    Series.from_values("Organic", [20, 24, 28]),
]).categories(["Jan", "Feb", "Mar"]).to_png()

# Scatter from (x, y) pairs
Chart.scatter([(1, 5), (2, 9), (4, 3)]).add_points("B", [(1, 2), (3, 6)]).to_svg()

# Combo: grouped bars + a line on an independently-scaled right axis
Chart.combo() \
    .add_bar("Revenue", [120, 190, 70, 220]) \
    .add_line("Conversion %", [3.2, 4.1, 2.8, 5.0], axis=Axis.RIGHT) \
    .title("Revenue vs conversion").categories(["Q1", "Q2", "Q3", "Q4"]) \
    .to_email_img()

Output methods

Method Returns Notes
to_svg() str SVG markup
to_png() / to_jpeg() bytes needs the [raster] extra
to_data_uri(fmt=Format.PNG) str data:<mime>;base64,…
to_html_img(fmt=Format.PNG, **attrs) str <img src="data:…" …>
to_email_img(**attrs) str PNG <img>use this for email
save(path) None format inferred from the extension
render(fmt) RenderedChart for full control

Using it in web frameworks

FastAPI / uvicorn:

from fastapi import Response
from charthandler import Chart

@app.get("/chart.png")
def chart() -> Response:
    png = Chart.bar({"Mon": 8, "Tue": 12, "Wed": 5}).title("This week").to_png()
    return Response(content=png, media_type="image/png")

Flask:

@app.get("/chart.png")
def chart():
    return Chart.bar([8, 12, 5]).to_png(), 200, {"Content-Type": "image/png"}

Django:

from django.http import HttpResponse

def chart(request):
    return HttpResponse(Chart.pie({"A": 60, "B": 40}).to_png(), content_type="image/png")

For inline charts in a template/email, pass chart.to_data_uri() and use <img src="{{ chart_uri }}">.

Charts in HTML email

Email clients can't fetch external images offline, and several (Outlook desktop, some Gmail setups) won't render inline SVG. The reliable approach is a base64 PNG embedded directly in the markup (to_email_img() does exactly this — it needs the [raster] extra):

img = Chart.bar({"Mon": 8, "Tue": 12, "Wed": 5}).title("This week").to_email_img(alt="Activity")
html = f"<h1>Your report</h1>{img}"
# -> <img src="data:image/png;base64,iVBORw0KGgo..." alt="Activity" />

Run the demo (Docker)

A FastAPI gallery is bundled. With Docker it serves on http://localhost:2200/:

docker compose up -d --build
# http://localhost:2200/                 — gallery (every type, inline PNG)
# http://localhost:2200/charts/combo.png — single chart as PNG
# http://localhost:2200/charts/pie.svg   — single chart as SVG

Without Docker:

pip install "pythoncharthandler[raster,demo]"
uvicorn examples.fastapi_app:app --reload   # http://127.0.0.1:8000/

Development

python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest        # tests
mypy          # strict type-check
ruff check .  # lint

A FastAPI demo lives in examples/fastapi_app.py (run with uvicorn examples.fastapi_app:app --reload).

License

MIT — see LICENSE.

About

Pure-Python charting library: SVG core + optional Pillow raster, base64/data-URI output for HTML email; FastAPI/uvicorn demo.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors