Clean Room
Beta Access — Clean Room is currently in beta and not yet generally available. To request early access, email [email protected].
Python only — Clean Room currently supports Python. R support is coming soon.
Overview
Clean Room turns a Python function into a self-contained, reproducible, interactive application. You decorate a function with @reproducible, and GoFigr draws a clean boundary around it: the function can only access the variables you pass in, the packages you declare, and nothing else. When the function produces a figure, everything—code, data, parameters, environment—is captured and published automatically.
The workflow:
Explore — work however you normally work in Jupyter
Distill — pull the core logic into a
@reproduciblefunctionRun — call the function; GoFigr captures everything automatically
Share — send a link; stakeholders interact with the figure in the browser
Quick Start
Load the GoFigr extension and configure it:
%load_ext gofigr
# configure() # only needed if overriding defaultsThe %load_ext gofigr magic injects reproducible, SliderParam, DropdownParam, and other names into your notebook namespace.
Simplest Example
That's it. When the function runs, GoFigr captures:
Source code — the function body, extracted from the notebook cell
Parameters — types, defaults, and values for every argument
Data — DataFrames passed as arguments, serialized alongside the revision
Environment — package names and versions, Python version
Output — the figures produced by the run
Interactive Mode
Add interactive=True to render parameter widgets directly in Jupyter. Changing a widget re-runs the function immediately.
Requires the anywidget package and its JupyterLab frontend extension:
After installing, fully restart JupyterLab (not just the kernel) and hard-reload the browser so the frontend extension registers. If widgets still don't render, run the built-in health check:
This verifies the Python package, traitlets, the Jupyter kernel, and prints guidance for fixing the frontend extension.
Full Example
In Jupyter, this renders sliders, dropdowns, a checkbox, and a text input above the figure. Adjusting any control re-executes the function and updates the plot.
Parameter Widgets
GoFigr maps Python types to interactive controls. You can rely on auto-inference or use explicit Param classes for more control.
Auto-Inference
int or float
Slider
bins: int = 20
bool
Checkbox
show_grid: bool = True
str
Text input
title: str = "My Chart"
Literal[...] type hint
Dropdown
mode: Literal["a", "b"] = "a"
pd.DataFrame
Static (read-only)
Passed at call time
SliderParam
Numeric slider with explicit bounds.
If you omit bounds, they are resolved automatically:
int
float
min
0
0
max
max(value * 2, 100)
max(value * 2, 1.0)
step
1
0.1
DropdownParam
Categorical dropdown with explicit choices.
You can also use a Literal type hint to create a dropdown automatically without DropdownParam:
CheckboxParam
Boolean toggle. Auto-inferred for bool defaults—you rarely need this explicitly.
TextParam
Free-form text input. Auto-inferred for str defaults.
StaticParam
For DataFrames and other complex objects. Read-only in interactive mode—no widget is rendered. The value is available in the Clean Room studio for inspection.
Custom Packages
By default, the clean room environment includes:
pd
pandas
np
numpy
plt
matplotlib.pyplot
sns
seaborn
Adding Packages
Use the packages argument to add more. By default, your packages are merged with the defaults:
Replacing Default Packages
Set merge_packages=False to use only the packages you specify:
Global Package Configuration
To change defaults for all @reproducible functions in a session:
Publishing
Automatic Capture (Jupyter)
With configure(auto_publish=True) (the default), figures are published automatically when a @reproducible function runs. No extra code needed.
Explicit Publishing
For more control, use the publisher argument and call publish() inside the function:
The publish() function is injected into the clean room globals when a publisher is provided.
What Gets Stored
Each published revision includes:
Source code — the function body
Manifest — JSON with parameter types, widget config, imports, and package versions
DataFrame parameters — serialized as Parquet
Revision flag — marks the revision as a Clean Room revision
Nested Calls
Each @reproducible call gets its own context. If you nest @reproducible functions, the innermost context wins for publish(). The context is reset after the function returns or raises an exception.
Edge Cases and Caveats
Clean room isolation — The function cannot access module-level variables from your notebook. Only declared packages, builtins, and function arguments are available. This is by design: it ensures the function is self-contained and reproducible.
DataFrames are copied — DataFrame arguments are round-tripped through Parquet serialization. The function receives a deserialized copy, not the original object. This ensures the clean room version matches what gets stored.
100 MB limit — Total DataFrame size (estimated via memory_usage(deep=True)) must be under 100 MB. If exceeded, a warning is issued and clean room is skipped—the function still runs normally but without isolation or capture.
Unsupported parameter types — Custom objects, numpy arrays, and lambdas cannot be serialized. If detected, a warning is issued and the function falls back to direct execution (no clean room).
interactive=True outside Jupyter — A warning is issued and the function runs non-interactively.
plt.show() auto-called — If matplotlib figures exist after execution, plt.show() is called automatically. You don't need to call it yourself.
Source code extraction — The function must be defined in importable source (a notebook cell or .py file). Dynamically created functions (e.g., via exec) are not supported.
Return values — In non-interactive mode, the function's return value is returned normally. In interactive mode, the return value is None (the output is rendered in the widget).
Usage in Scripts
Clean Room works outside Jupyter with a few differences:
No interactive mode —
interactive=Trueis ignored (with a warning)No automatic capture — you must use the
publisherargument explicitlyExplicit imports — import from
gofigr.reproducibleinstead of relying on the%load_extmagic
Last updated