Open source

Lightweight visibility for Python

Your outputs. In your browser.

Publish plots, DataFrames, objects and logs from scripts, pipelines and experiments. A few lines of Python, a browser to inspect the result.

Run locally or host a receiver.

Actual plotsrv UI displaying 720 synthetic import runs as a scatter plot in pink and plum
Actual plotsrv UI · synthetic pipeline data
py analysis.py With a receiver running
from plotsrv import publish_view

publish_view(fig, label="Batch duration")
Python objects → browser views

WORKS WITH YOUR EXISTING TOOLS

matplotlibpandasPolarsNumPyplotnineand ordinary Python.

01 / Publishing from Python

Keep your code. Inspect its outputs.

Decorate a function with @view, or publish an existing object with publish_view().Screenshots from a local plotsrv run. Open an image to inspect the full UI.

RECEIVERplotsrv serve

Start a local receiver, then run your publishing code in another process.

01 — PLOTS

Publish the plot
you already made.

Add @view to a function that returns a matplotlib figure or plotnine plot. Call it normally: the return value gets a browser view and continues back to your code.

Use publish_view(fig) for a figure you already have.

py analysis.py
from plotsrv import view

@view(label="Import throughput")
def throughput():
    fig = make_throughput_plot()
    return fig
plotsrv displaying a published matplotlib figure
A published matplotlib figure Actual UI ↗
02 — TABLES

Explore a DataFrame
in the browser.

Publish a pandas or Polars DataFrame. Search, sort and filter its rows, choose columns, then choose Plot + data. plotsrv creates the plot right there in the browser, using the table you published.

One published DataFrame. First the table, then duration by run — no extra Python plotting code.

py imports.py
from plotsrv import publish_view

# runs is a pandas or Polars DataFrame.
publish_view(runs, label="Import runs")
The published import-runs DataFrame in plotsrv’s searchable, sortable table view
01 / The DataFrame you published Actual UI ↗
Choose Plot + data in the browser
The real plotsrv Plot + data controls showing a line chart built from an import-runs table
02 / plotsrv creates this plot in your browser Actual UI ↗
03 — OBJECTS & ARRAYS

Look inside an
intermediate result.

Send a dictionary, list or array to a view. Inspect nested values, types and structure without squeezing the result into a terminal printout.

NumPy arrays include shape, dtype and data in the structured object view.

py pipeline.py
from plotsrv import publish_view

publish_view(summary, label="Run summary")
publish_view(array, label="Output array")
plotsrv's structured object inspector with key, summary, type, value and structure columns
A dictionary in the object inspector Actual UI ↗

Use the objects you have

Several renderers. One publishing API.

plotsrv selects a renderer from the object or file you publish. Plots, tables and structured outputs can live in the same receiver.

FIGURES

matplotlib & plotnine

Publish existing plots for viewing and export.

publish_view(fig)
DATAFRAMES

pandas & Polars

Inspect rows and columns, or plot table data in the browser.

publish_view(frame)Table screenshot ↗
STRUCTURED DATA

Dicts, lists & arrays

Explore values, types and nested structure.

publish_view(result)Array screenshot ↗
REPORTS

Markdown & HTML

Keep generated reports alongside the data they describe.

publish_view(Path("report.md"))
IMAGES & FILES

Saved outputs

View images and supported files directly from their paths.

publish_view(Path("scan.jpg"))
LOGS

Text & JSONL streams

Follow append-only files while a process runs.

stream_view(source="run.jsonl")

02 / Following a running process

Keep a log view
beside your code.

Attach stream_view() to a log file. New records reach the receiver as your process appends them. JSONL logs can be explored as tables, with suggested views for levels, warnings and activity.

py worker.pyWith a receiver running
from plotsrv import stream_view

handle = stream_view(
    source="import.jsonl",
    format="jsonl",
)
# Keep the publisher running while following.
# Call handle.stop() when finished.
Open the live demo
Actual plotsrv log view with stream controls, detected Python application logs and a table of sample events
Log stream in plotsrv · screenshot with synthetic events

03 / Configuration you can version

Keep the settings in one file.

Start with the defaults. Add a plotsrv.yml when you want consistent storage, freshness, limits, receiver settings and appearance across runs.

01 / PUBLISH YOUR DATA

A view of the business.

Publish a dictionary of daily order metrics. The default plotsrv UI lets you inspect the values as soon as they arrive.

publish_view(summary,
view_id="business:summary")

Synthetic order data for Northstar, a fictional business. Both views show the same final values.

The default plotsrv object view showing a daily order summary with 1,842 orders and three late orders
Daily order summary · default appearance Actual UI ↗
02 / ADD YOUR CONFIG

Same data. A few settings.

Add your logo and header colour, keep the last five snapshots, and set the cadence you expect new data to arrive.

A small check reads the published late_orders metric and flags a value above zero.

This example expects an update every five seconds and marks it stale after ten. Set these intervals to suit your own process.

yml plotsrv.ymlSettings used for this capture
ui-settings:
  logo: northstar.svg
  header_text: "Daily operations"
  header_fill_colour: "#eaf4f0"

storage-settings:
  enabled: true
  default_keep_last: 5

freshness-settings:
  enabled: true
  expected_every: 5s
  warn_after: 10s
  overdue_after: 1h

checks-settings:
  enabled: true
  rules:
    - id: late-orders
      source: business:summary
      kind: state
      path: [late_orders]
      op: gt
      value: 0
03 / THE CONFIGURED VIEW

Your branding. A little more context.

The same order summary now has Northstar’s logo and header. Updates are stored as snapshots, ready to revisit from the history controls.

When the publisher stops sending data, the view becomes Stale. Its content stays available to inspect.

Three updates were published and stored for this example.

The same order summary with Northstar branding, a green header, stale data status and enabled snapshot controls
Same content · branding, snapshots and freshness Actual UI ↗
04 / INSPECT THE STATUS

See why it needs attention.

Open the status panel to see the recent updates, saved snapshots and check result together.

The check shows late_orders = 3, above the configured threshold of zero. Freshness separately tells you how long it has been since new data arrived.

Captured from plotsrv after real updates, storage and check evaluation.

Actual plotsrv Live data status panel showing stale data, stored snapshots in recent updates and the triggered late-orders check
Freshness, stored updates and a scalar check Actual UI ↗
TERMINAL SETUP

Work through the configuration.

plotsrv config init

A guided CLI walkthrough for roles, sources and settings. Review the changes before saving them to YAML.

BROWSER EDITOR

Adjust the appearance.

plotsrv config ui

Open a temporary editor for branding, appearance and navigation. Preview and review changes, then save them to the same config file.

See the actual editor

Where it fits

Lightweight observability for work in progress.

For developers and data scientists checking a pipeline, inspecting an intermediate result, or following a long-running job.

“Cheap observability”: a small amount of setup for useful visibility into a Python process.

01

Close to the application.

Publish objects you already produce. Keep your processing and plotting logic in ordinary Python.

02

Control what you retain.

Configure limits, freshness and optional storage for the outputs that matter to your workflow.

03

Alongside your other tools.

Use plotsrv for inspecting outputs and following work. Dedicated platforms still handle fleet-wide telemetry and operational alerting.

Add a view to your next script

Start with one output worth inspecting.