Short, copy-pasteable patterns. Every recipe uses only the public API and the Python standard library.
A common monitoring use case: keep a rolling window of samples and render it as a single line beside other status text.
from collections import deque
from codechu_spark import sparkline
window = deque(maxlen=60)
def on_sample(cpu_percent: float) -> None:
window.append(cpu_percent)
line = sparkline(list(window), width=30)
print(f"cpu {cpu_percent:5.1f}% {line}")Example output for a synthetic 60-sample series, downsampled to width 30:
cpu 62.4% ▇▂▃▄▄▆▄▃█▇▄▇▂▄█▅▄▂▄▆▁▄▅▃▅▅▁▆▂▅
The deque(maxlen=...) does the windowing; width= does the
downsample-to-fit so the output column stays stable.
bar_chart already does the layout. Sort, slice, hand it the
tuples.
from codechu_spark import bar_chart
sizes = {
"logs": 4200,
"build": 980,
"cache": 510,
"node_modules": 320,
"venv": 180,
"tmp": 40,
}
top5 = sorted(sizes.items(), key=lambda kv: kv[1], reverse=True)[:5]
print(bar_chart(top5, width=25))Output:
logs █████████████████████████ 4200
build ██████ 980
cache ███ 510
node_modules ██ 320
venv █ 180
Labels left-pad to the longest entry, bars scale to the max value.
A heatmap row is denser than a sparkline — useful when you have a fixed-length bucketed series (hours, days, percentiles) and want intensity, not shape.
from codechu_spark import heatmap
# requests per hour, midnight → 11pm
hours = [0,0,1,2,3,5,8,12,15,18,22,30,
40,45,50,48,42,35,28,20,15,10,6,3]
print(heatmap(hours))Output (24 chars, one per hour):
░░░░▒▒▓███▓▓▒▒░░
Quiet pre-dawn maps to spaces, the noon-to-afternoon peak to full blocks. Combine with hour ticks above for a one-row "when does traffic happen?" readout.
Both sparkline and heatmap accept chars= — any string ordered
low → high. Useful when the default Unicode blocks don't render
well in your terminal font, or when you want an ASCII-safe build.
from codechu_spark import sparkline
# Classic ASCII ramp, low to high
ascii_ramp = ".:-=+*#%@"
print(sparkline([0.1, 0.3, 0.7, 0.9, 1.0], chars=ascii_ramp))
# → .-*%@
# Two-tone "is it busy or not" ramp
print(sparkline([0, 1, 0, 1, 1, 0], chars=" █"))
# → █ ██Rules:
charsmust be non-empty (elseValueError).- One Python character per glyph is assumed; if you pass an emoji whose terminal width is 2, your output will visually be twice as wide as the glyph count.
sparkline(values, width=N) downsamples by averaging consecutive
buckets. To fit any terminal:
import shutil
from codechu_spark import sparkline
def fit_sparkline(values: list[float], reserve: int = 12) -> str:
"""Render `values` to the current terminal width.
`reserve` columns are left for a leading label or trailing units.
"""
cols = shutil.get_terminal_size((80, 24)).columns
width = max(8, cols - reserve)
return sparkline(values, width=width)
big = [i % 23 + (i // 17) for i in range(10_000)]
print(f"throughput {fit_sparkline(big)}")Notes:
width >= len(values)is treated as no-op — no upsampling. If you pass an absurdly largewidthfor a short series, you'll get exactlylen(values)glyphs back.- Averaging smooths outliers. If you want peaks preserved, downsample
yourself (e.g. take
maxper bucket) and pass the result withwidth=None.
sparkline takes optional thresholds=[low, high] plus a 3-tuple
of ANSI color codes. The caller supplies the raw escapes — the
package ships no palette and makes no assumption about whether your
output stream supports color.
import sys
from codechu_spark import sparkline
GREEN = "\x1b[32m"
YELLOW = "\x1b[33m"
RED = "\x1b[31m"
cpu_samples = [12, 18, 24, 31, 45, 58, 67, 74, 82, 91, 88, 73, 61, 48, 35]
# Only colorize when writing to a TTY (explicit-config principle).
if sys.stdout.isatty():
line = sparkline(
cpu_samples,
thresholds=[30, 70],
colors=(GREEN, YELLOW, RED),
)
else:
line = sparkline(cpu_samples)
print(f"cpu {line}")Values under 30 render green, 30–70 yellow, and ≥ 70 red. The visible glyph count is unchanged — only ANSI wrappers are added.
multi_sparkline lines up multiple series. By default
(share_scale=True) all rows share one min/max, so a flat row stays
visibly flat next to a spiky one. Switch to
share_scale=False when you care about each series' own shape.
from codechu_spark import multi_sparkline
cpu = [12, 18, 24, 31, 45, 58, 67, 74, 82, 91]
mem = [40, 41, 42, 42, 43, 43, 44, 44, 45, 45]
print(multi_sparkline(
[cpu, mem],
labels=["cpu", "mem"],
share_scale=True,
))Output (heights comparable — mem correctly looks nearly flat next
to cpu's climb):
cpu ▁▂▂▃▄▅▆▆▇█
mem ▃▄▄▄▄▄▄▄▄▄
Flip share_scale=False and mem would also climb to █ — useful
when you want shape, not magnitude.
sparkline_with_axis prefixes the min and suffixes the max so a
single line is self-describing — handy in status dashboards where
you can't afford a second row for tick marks.
from codechu_spark import sparkline_with_axis
latencies_ms = [12, 14, 11, 18, 22, 35, 47, 31, 20, 15, 13]
print(f"p50 latency {sparkline_with_axis(latencies_ms, width=20)}")Output:
p50 latency 11 →▁▂▁▂▃▆█▅▃▂▁← 47
The numeric labels reflect the original extremes (before any downsample averaging), so a reader can map any glyph height back to real units.