panel-live-server¶
panel_live_server
¶
Panel Live Server - Execute and visualize Python code snippets.
__all__ = ['__version__']
module-attribute
¶
__version__ = importlib.metadata.version('panel-live-server')
module-attribute
¶
app
¶
Panel server for code visualization.
This module implements a Panel web server that executes Python code and displays the results through various endpoints.
cli
¶
CLI for Panel Live Server.
app = typer.Typer(name='pls', help='Panel Live Server - Execute and visualize Python code snippets.', add_completion=False)
module-attribute
¶
list_app = typer.Typer(help='List resources (packages, etc.).')
module-attribute
¶
logger = logging.getLogger(__name__)
module-attribute
¶
list_packages(filter=typer.Argument('', help='Optional substring to filter package names (case-insensitive).', show_default=False))
¶
List all Python packages installed in the current environment.
Optionally filter by a substring, e.g. pls list packages panel to show
only packages whose name contains "panel".
main()
¶
Entry point for the pls command.
main_callback(ctx, version=False)
¶
Panel Live Server - Execute and visualize Python code snippets.
mcp(transport=typer.Option('stdio', '--transport', '-t', help='MCP transport: stdio, http, or sse.', envvar='PANEL_LIVE_SERVER_TRANSPORT'), host=typer.Option('127.0.0.1', '--host', help='Host for HTTP/SSE transport.', envvar='PANEL_LIVE_SERVER_MCP_HOST'), port=typer.Option(8001, '--port', '-p', help='Port for HTTP/SSE transport.', envvar='PANEL_LIVE_SERVER_MCP_PORT'), verbose=typer.Option(False, '--verbose', '-v', help='Enable verbose logging.'))
¶
Start as an MCP server for AI assistants.
The MCP server exposes the show tool for executing and displaying
Python visualizations. A Panel visualization server starts automatically
on port 5077 (PANEL_LIVE_SERVER_PORT) — visit that address in a browser
to watch visualizations appear in real time.
Note: the --port flag here controls the MCP HTTP/SSE listener, NOT the Panel visualization server port. For stdio transport, --port is unused.
serve(port=typer.Option(5077, '--port', '-p', help='Port number to run the Panel server on.', envvar='PANEL_LIVE_SERVER_PORT', show_default=True), host=typer.Option('localhost', '--host', '-H', help='Host address to bind to.', envvar='PANEL_LIVE_SERVER_HOST', show_default=True), db_path=typer.Option(None, '--db-path', help='Path to the SQLite database file.', envvar='PANEL_LIVE_SERVER_DB_PATH'), show=typer.Option(False, '--show', help='Open the server in a browser after starting.'), verbose=typer.Option(False, '--verbose', '-v', help='Enable verbose logging.'))
¶
Start the Panel Live Server directly.
The server provides a web interface for executing Python code snippets
and visualizing the results. Visit http://
Note: if you are also running pls mcp, both commands use the same Panel
server port (PANEL_LIVE_SERVER_PORT). Run only one at a time unless you
configure different ports.
status(port=typer.Option(5077, '--port', '-p', help='Port to check.', envvar='PANEL_LIVE_SERVER_PORT', show_default=True), host=typer.Option('localhost', '--host', '-H', help='Host to check.', envvar='PANEL_LIVE_SERVER_HOST', show_default=True))
¶
Check whether the Panel server is running.
Queries the health endpoint and reports the server status.
version_callback(value)
¶
Print version and exit.
client
¶
HTTP client for Display Server REST API.
This module provides a client for interacting with the Panel Display Server via its REST API. The client can be used with either a locally-managed subprocess or a remote server instance.
logger = logging.getLogger(__name__)
module-attribute
¶
DisplayClient
¶
HTTP client for Display Server REST API.
This client handles all HTTP communication with the Panel Display Server, including health checks and snippet creation. It uses a persistent session for connection pooling.
base_url = base_url.rstrip('/')
instance-attribute
¶
session = requests.Session()
instance-attribute
¶
timeout = timeout
instance-attribute
¶
close()
¶
Close the HTTP session and cleanup resources.
create_snippet(code, name='', description='', method='jupyter')
¶
Create a visualization snippet on the Display Server.
Sends Python code to the server for execution and rendering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to execute |
required |
name
|
str
|
Name for the visualization |
''
|
description
|
str
|
Description of the visualization |
''
|
method
|
str
|
Execution method ("jupyter" or "panel") |
'jupyter'
|
Returns:
| Type | Description |
|---|---|
dict
|
Server response containing either: - Success: {"url": str, "id": str, ...} - Error: {"error": str, "message": str, "traceback": str} |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If HTTP request fails |
is_healthy()
¶
Check if Display Server is healthy.
Returns:
| Type | Description |
|---|---|
bool
|
True if server responds to health check, False otherwise |
config
¶
Configuration for Panel Live Server.
logger = logging.getLogger('panel_live_server')
module-attribute
¶
Config
¶
Bases: BaseModel
Panel Live Server configuration.
db_path = Field(default_factory=(lambda: _default_user_dir() / 'snippets' / 'snippets.db'), description='Path to SQLite database for snippets')
class-attribute
instance-attribute
¶
host = Field(default='localhost', description='Host address for the Panel server')
class-attribute
instance-attribute
¶
jupyter_server_proxy_url = Field(default='', description='Jupyter server proxy URL')
class-attribute
instance-attribute
¶
max_restarts = Field(default=3, description='Maximum number of restart attempts')
class-attribute
instance-attribute
¶
port = Field(default=5077, description='Port for the Panel server')
class-attribute
instance-attribute
¶
get_config()
¶
Get or create the config instance.
reset_config()
¶
Reset config (for testing).
database
¶
Database models and operations for the display server.
This module handles SQLite database operations for storing and retrieving visualization requests.
logger = logging.getLogger(__name__)
module-attribute
¶
Snippet
¶
Bases: BaseModel
Model for a code snippet stored in the database.
Represents a code snippet submitted to the Display System for visualization.
app = Field(..., description='Python code to execute')
class-attribute
instance-attribute
¶
created_at = Field(default_factory=(lambda: datetime.now(timezone.utc)))
class-attribute
instance-attribute
¶
description = Field(default='', description='Short description of the app')
class-attribute
instance-attribute
¶
error_message = Field(default=None, description="Error details if status='error'")
class-attribute
instance-attribute
¶
execution_time = Field(default=None, description='Execution time in seconds')
class-attribute
instance-attribute
¶
extensions = Field(default_factory=list, description='Inferred Panel extensions')
class-attribute
instance-attribute
¶
id = Field(default_factory=(lambda: str(uuid.uuid4())))
class-attribute
instance-attribute
¶
method = Field(..., description='Execution method')
class-attribute
instance-attribute
¶
name = Field(default='', description='User-provided name')
class-attribute
instance-attribute
¶
readme = Field(default='', description='Longer documentation describing the app')
class-attribute
instance-attribute
¶
requirements = Field(default_factory=list, description='Inferred required packages')
class-attribute
instance-attribute
¶
slug = Field(default='', description='URL-friendly slug for persistent links')
class-attribute
instance-attribute
¶
status = Field(default='pending')
class-attribute
instance-attribute
¶
tags = Field(default_factory=list, description='List of tags')
class-attribute
instance-attribute
¶
updated_at = Field(default_factory=(lambda: datetime.now(timezone.utc)))
class-attribute
instance-attribute
¶
user = Field(default='guest', description='User who created the snippet')
class-attribute
instance-attribute
¶
validate_slug(v)
classmethod
¶
Validate that slug is either empty or a valid URL slug.
SnippetDatabase
¶
SQLite database manager for code snippets.
Manages storage and retrieval of Snippet records (code snippets) submitted to the Display System.
db_path = db_path
instance-attribute
¶
create_snippet(snippet)
¶
create_visualization(app, name='', description='', readme='', method='jupyter')
¶
Create a visualization request.
This is the core business logic for creating visualizations, shared by both the HTTP API endpoint and the UI form.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
str
|
Python code to execute |
required |
name
|
str
|
Display name for the visualization |
''
|
description
|
str
|
Short description of the visualization |
''
|
readme
|
str
|
Longer documentation describing the app |
''
|
method
|
str
|
Execution method: "jupyter", "panel", or "pyodide" |
'jupyter'
|
Returns:
| Type | Description |
|---|---|
Snippet
|
The snippet created for the visualization request. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If app is empty or contains unsupported operations |
SyntaxError
|
If app has syntax errors |
Exception
|
If database operation or other errors occur |
delete_snippet(snippet_id)
¶
Delete a snippet record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snippet_id
|
str
|
Snippet ID |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if deleted, False if not found |
get_snippet(snippet_id)
¶
Get a snippet record by ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snippet_id
|
str
|
Snippet ID |
required |
Returns:
| Type | Description |
|---|---|
Optional[Snippet]
|
Snippet record if found, None otherwise |
get_snippet_by_slug(slug)
¶
Get the most recent snippet record by slug.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
slug
|
str
|
Snippet slug |
required |
Returns:
| Type | Description |
|---|---|
Optional[Snippet]
|
Most recent snippet record with this slug if found, None otherwise |
list_snippets(limit=100, offset=0, start=None, end=None, status=None, method=None)
¶
List snippet records with filters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
int
|
Maximum number of snippets to return |
100
|
offset
|
int
|
Number of snippets to skip |
0
|
start
|
Optional[datetime]
|
Filter snippets created after this time |
None
|
end
|
Optional[datetime]
|
Filter snippets created before this time |
None
|
status
|
Optional[str]
|
Filter by status |
None
|
method
|
Optional[str]
|
Filter by method |
None
|
Returns:
| Type | Description |
|---|---|
list[Snippet]
|
List of snippet records |
search_snippets(query, limit=100)
¶
Search snippet records using full-text search.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Search query |
required |
limit
|
int
|
Maximum number of results |
100
|
Returns:
| Type | Description |
|---|---|
list[Snippet]
|
Matching snippet records |
update_snippet(snippet_id, status=None, error_message=None, execution_time=None, requirements=None, extensions=None)
¶
Update a snippet record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snippet_id
|
str
|
Snippet ID |
required |
status
|
Optional[str]
|
New status |
None
|
error_message
|
Optional[str]
|
Error message |
None
|
execution_time
|
Optional[float]
|
Execution time |
None
|
requirements
|
Optional[list[str]]
|
Required packages |
None
|
extensions
|
Optional[list[str]]
|
Required extensions |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if updated, False if not found |
get_db(db_path=None)
¶
Get or create the SnippetDatabase instance.
This function implements lazy initialization with a global cache. The database instance is created once and reused across the application.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
Optional[Path]
|
Path to database file. If None, uses default from environment/config. Only used on first call; subsequent calls ignore this parameter. |
None
|
Returns:
| Type | Description |
|---|---|
SnippetDatabase
|
Shared database instance |
reset_db()
¶
Reset the database instance.
This is primarily for testing purposes to ensure a clean state.
endpoints
¶
REST API endpoints for the Display System.
This module implements Tornado RequestHandler classes that provide HTTP endpoints for creating visualizations and checking server health.
manager
¶
Panel server subprocess management.
This module manages the Panel server as a subprocess, including startup, health checks, and shutdown.
logger = logging.getLogger(__name__)
module-attribute
¶
PanelServerManager
¶
Manages the Panel server subprocess.
db_path = db_path
instance-attribute
¶
host = host
instance-attribute
¶
max_restarts = max_restarts
instance-attribute
¶
port = port
instance-attribute
¶
process = None
instance-attribute
¶
restart_count = 0
instance-attribute
¶
get_base_url()
¶
Get the base URL for the Panel server.
Returns:
| Type | Description |
|---|---|
str
|
Base URL |
is_healthy()
¶
Check if Panel server is healthy.
Returns:
| Type | Description |
|---|---|
bool
|
True if server is healthy, False otherwise |
restart()
¶
Restart the Panel server.
Returns:
| Type | Description |
|---|---|
bool
|
True if restarted successfully, False otherwise |
start()
¶
Start the Panel server subprocess.
Returns:
| Type | Description |
|---|---|
bool
|
True if started successfully, False otherwise |
stop(timeout=5)
¶
Stop the Panel server subprocess.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
int
|
Maximum time to wait for graceful shutdown |
5
|
pages
¶
Panel page functions for Panel Live Server.
add_page
¶
Add page for creating new visualizations.
This module implements the /add page endpoint that provides a form for manually creating visualizations via the UI.
ABOUT = '\n## Add Visualization\n\nThis page allows you to create new visualizations by writing Python code.\n\n### How to Use\n\n1. **Write Code**: Enter your Python visualization code in the editor\n2. **Configure**: Set a name, description, and execution method in the sidebar\n3. **Submit**: Click the Submit button to create the visualization\n\n### Execution Methods\n\n- **jupyter**: The last expression in the code is displayed (like a Jupyter cell)\n- **panel**: Objects marked with `.servable()` are displayed as a Panel app\n\n### Learn More\n\nFor more information about this project, visit:\n[Panel Live Server](https://github.com/panel-extensions/panel-live-server).\n'
module-attribute
¶
DEFAULT_SNIPPET = "import pandas as pd\nimport hvplot.pandas\n\ndf = pd.DataFrame({\n 'Product': ['A', 'B', 'C', 'D'],\n 'Sales': [120, 95, 180, 150]\n})\n\ndf.hvplot.bar(x='Product', y='Sales', title='Sales by Product')"
module-attribute
¶
logger = logging.getLogger(__name__)
module-attribute
¶
add_page()
¶
Create the /add page for manually creating visualizations.
Provides a UI form for entering code, name, description, and execution method.
admin_page
¶
Admin page for managing snippets.
This module implements the /admin page endpoint that allows viewing and deleting snippets from the database.
ABOUT = '\n## Snippet Manager\n\nThis page provides an administrative interface for managing all visualizations\nstored in the database.\n\n### Features\n\n- **View All Snippets**: See all visualizations with their name, description, method, status, and creation date\n- **View Code**: Expand any row to see the full Python code for that visualization\n- **Delete Snippets**: Remove visualizations you no longer need\n- **Direct Links**: Click the link icon to view any visualization\n\n### Learn More\n\nFor more information about this project, visit:\n[Panel Live Server](https://github.com/panel-extensions/panel-live-server).\n'
module-attribute
¶
admin_page()
¶
Create the /admin page.
Provides an administrative interface for managing all snippets in the database.
feed_page
¶
Feed page showing a scrollable list of visualizations.
This module implements the /feed page endpoint that displays recent visualizations in a feed-style layout with live updates.
ABOUT = '\n## Visualization Feed\n\nThis page displays a live feed of recent visualizations created through the Panel Live Server display tool.\n\n### Features\n\n- **Live Updates**: The feed automatically refreshes every second to show new visualizations\n- **View / Code Tabs**: Each visualization shows both an interactive preview and the source code\n- **Actions**: Open visualizations in full screen, copy code to clipboard, or delete entries\n- **Limit Control**: Use the sidebar to control how many visualizations are displayed\n\n### How It Works\n\nWhen an AI assistant uses the `show` tool to display a visualization, it appears here in the feed.\nEach entry includes the visualization name, creation time, description, and an iframe preview.\n\n### Learn More\n\nFor more information about this project, including setup instructions and advanced configuration options,\nvisit: [Panel Live Server](https://github.com/panel-extensions/panel-live-server).\n'
module-attribute
¶
feed_page()
¶
Create the /feed page.
Displays a feed of recent visualizations with automatic updates.
view_page
¶
View page for displaying individual visualizations.
This module implements the /view page endpoint that executes and displays a single visualization by ID.
logger = logging.getLogger(__name__)
module-attribute
¶
create_view(snippet_id)
¶
Create a view for a single visualization snippet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snippet_id
|
str
|
ID of the snippet to display |
required |
Returns:
| Type | Description |
|---|---|
Viewable
|
Panel component displaying the visualization |
view_page()
¶
Create the /view page.
Renders a single visualization by ID or slug from the query string parameter. Supports ?id=... or ?slug=... query parameters. If both are provided, id takes precedence.
server
¶
Panel Live Server - MCP Server.
A standalone MCP server that provides the show tool
for executing Python code and rendering visualizations via a Panel web server.
SHOW_RESOURCE_URI = 'ui://panel-live-server/show.html'
module-attribute
¶
SHOW_TEMPLATE_PATH = Path(__file__).parent / 'templates' / 'show.html'
module-attribute
¶
logger = logging.getLogger(__name__)
module-attribute
¶
mcp = FastMCP('Panel Live Server', instructions='Panel Live Server executes Python code snippets and renders the resulting visualizations as live, interactive web pages.\n\nWORKFLOWS — choose one based on complexity:\n\nQUICK (simple plots): Call `show(code, name, quick=True)`. Runs full validation inline and renders in one step. Use for straightforward plots with well-known libraries.\n\nSTANDARD (complex apps / unfamiliar code):\n1. DISCOVER: Call `list_packages()` (once per session) to see what Python packages are installed. The environment is fixed and cannot be modified. By default it returns the ~30 core visualization, data, and panel packages.\n2. VALIDATE: Call `validate(code, method)` before `show`. It runs static checks AND executes the code to catch runtime errors. Fix any issues and re-validate until it returns `{"valid": true}`. Results are cached — `show` reuses them with zero overhead.\n3. SHOW: Call `show(code, name, description, method, zoom)` to render. `show` will raise an error if `validate()` was not called first.\n\nLIBRARY SELECTION (prefer in this order when suitable):\n- hvPlot: quick interactive plots from DataFrames (.plot API)\n- HoloViews: advanced composable, interactive visualizations\n- Panel: dashboards, data apps, complex layouts (use method=\'panel\')\n- Matplotlib: publication-quality static plots\n- Plotly: interactive charts with 3D, hover\n- ECharts (pn.pane.ECharts): modern business-quality charts with data transitions and animations\n- Bokeh: low-level interactive web plots\n- deck.gl (pn.pane.DeckGL): large-scale geospatial and 3D data visualization\nAlways verify the library is installed via `list_packages` first.\n\nOUTPUT\nAfter calling `show`, ALWAYS present the returned URL to the user as a clickable Markdown link: [Show Visualization](url)\n\nERRORS\n`show` raises `SecurityError` for blocked imports or dangerous patterns — these require a substantive code rewrite, not a retry. `show` raises `ValidationError` for syntax errors, missing packages, or missing Panel extension declarations — fix the reported issue and try again.', lifespan=app_lifespan)
module-attribute
¶
app_lifespan(app)
async
¶
MCP server lifespan - eagerly start the Panel server.
list_packages(category='core', query='', include_versions=False, ctx=None)
async
¶
List Python packages installed in the server environment.
IMPORTANT — call this tool ONCE at the start of every session before writing any visualization code, so you know exactly what libraries are available. The environment is fixed — packages cannot be installed or changed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
category
|
str
|
Comma-separated list of categories to filter by.
Valid categories: |
'core'
|
query
|
str
|
Case-insensitive substring filter on package name.
Example: |
''
|
include_versions
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
list[str] | list[dict[str, str]]
|
Sorted list of package names (default) or |
Examples:
>>> list_packages()
["bokeh", "hvplot", "numpy", "panel", ...]
>>> list_packages(category="visualization")
["bokeh", "matplotlib", ...]
>>> list_packages(category="") # all installed packages
["aiofile", "anyio", ...]
>>> list_packages(query="panel", include_versions=True)
[{"name": "panel", "version": "1.6.0"}, ...]
show(code, name='', description='', method='jupyter', zoom=100, quick=False, ctx=None)
async
¶
Display Python code as a live, interactive visualization.
Two usage modes:
- Quick (
quick=True): one-shot — runs full validation (static + runtime) inline. No priorvalidate()call needed. Ideal for simple plots with well-known libraries. - Standard (
quick=False, default): expectsvalidate(code, method)to have been called first.showreuses the cached static validation with zero overhead.
In both modes, validation failures raise SecurityError or
ValidationError — the MCP App is only returned on success.
Executes Python code and renders the result in a Panel web interface. Always call this tool when the user asks to show, display, plot, or visualize anything.
IMPORTANT — always provide a short name (e.g. "Temperature chart") so the
visualization can be identified in the feed. The description is optional but helpful.
IMPORTANT — after calling this tool, always present the returned url to the user
as a clickable Markdown link: Show Visualization
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to execute. For "jupyter" method: the LAST expression is displayed. It must be at column 0 (fully dedented — no leading whitespace or indentation). For "panel" method: call .servable() on the objects you want displayed. |
required |
name
|
str
|
Short descriptive name shown in the visualization feed (e.g. "Sales chart 2024"). Always provide this — unnamed visualizations are hard to track. |
''
|
description
|
str
|
One-sentence description of what the visualization shows. |
''
|
method
|
(jupyter, panel)
|
Execution mode:
- "jupyter": displays the last expression's result. Use for standard plots,
dataframes, and objects that do NOT import panel directly.
- "panel": displays objects marked |
"jupyter"
|
zoom
|
(100, 75, 50, 25)
|
Initial zoom level for the visualization preview. Choose based on how much content the visualization contains: - 100: simple plots, single charts, dataframes, small widgets — fits naturally. - 75: multi-panel layouts, apps with a sidebar, moderate dashboards. - 50: full-page template apps (FastListTemplate, MaterialTemplate, etc.) with header + sidebar + main area. - 25: very large or wide apps designed for big screens; use when 50 still feels cramped in the preview pane. |
100
|
quick
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
str
|
JSON payload for MCP App rendering, including the visualization URL. |
show_view()
¶
Return the HTML resource used by the show MCP App.
validate(code, method='jupyter', ctx=None)
async
¶
Validate Python visualization code — ALWAYS call before show().
Runs static checks AND executes the code to catch both compile-time and runtime errors before the visualization is created. This prevents failed renders and wasted round-trips.
Checks performed (in order):
1. Syntax — ast.parse
2. Security — ruff security rules + blocked-import list
3. Package availability — all imports must be installed in this environment
4. Panel extensions — declared via pn.extension() (panel method only;
jupyter method auto-injects extensions so no declaration is needed)
5. Runtime execution — runs the code in an isolated namespace to catch
ValueError, TypeError, AttributeError, import failures, etc.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to validate. |
required |
method
|
(jupyter, panel)
|
Execution method — same as the |
"jupyter"
|
Returns:
| Type | Description |
|---|---|
dict
|
|
ui
¶
Shared UI components for the Display Server.
banner()
¶
Create a banner indicating alpha/experimental software.
utils
¶
Utilities for inferring required packages and Panel extensions from code.
logger = logging.getLogger(__name__)
module-attribute
¶
ExtensionError
¶
Bases: Exception
Custom exception for missing Panel extensions.
execute_in_module(code, module_name, *, cleanup=True)
¶
Execute Python code in a proper module namespace.
Creates a types.ModuleType following Bokeh's pattern, registers it in sys.modules, executes code, and optionally cleans up. This ensures Panel decorators (@pn.cache, @pn.depends) and function references work properly by using module.dict as a single namespace for both globals and locals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to execute |
required |
module_name
|
str
|
Unique name for the module (should be a valid Python identifier) |
required |
cleanup
|
bool
|
Whether to remove module from sys.modules after execution. Set to False if you need to keep the module registered (e.g., for later eval calls), but remember to clean up manually. |
True
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The module's namespace (module.dict) after execution |
Raises:
| Type | Description |
|---|---|
Exception
|
Any exception raised during code execution |
Notes
This pattern is critical for Panel decorators and code that uses function introspection or cross-references. It follows Bokeh's CodeRunner pattern.
Examples:
extract_last_expression(code)
¶
Extract the last expression from code for jupyter method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code |
required |
Returns:
| Type | Description |
|---|---|
tuple[str, str]
|
(statements_code, last_expression_code) |
find_extensions(code, namespace=None)
¶
Infer Panel extensions required for code execution.
Maps common packages/types to their Panel extensions: - pandas DataFrame/Series -> "tabulator" - plotly figures -> "plotly" - bokeh models -> (none, default) - matplotlib figures -> (none, uses pngpane) - altair charts -> "vega" - deck.gl -> "deckgl"
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to analyze |
required |
namespace
|
dict[str, Any] | None
|
Namespace from code execution (optional) |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of required Panel extension names |
find_requirements(code)
¶
Find package requirements from code.
Uses Panel's built-in find_requirements function to detect package dependencies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to analyze |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of required package names |
get_relative_view_url(id)
¶
Generate a relative URL for viewing a visualization by ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Visualization ID |
required |
Returns:
| Type | Description |
|---|---|
str
|
Relative URL to view the visualization |
validate_code(code)
¶
Execute code in a thread with a timeout to catch runtime errors.
Runs code in a separate thread so the caller is not blocked indefinitely by long-running or hanging code. The thread is a daemon — on timeout it continues running until the process exits (threads cannot be forcibly killed).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to validate as a string. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Empty string if the code runs without error, otherwise the traceback or a timeout message. |
validate_extension_availability(code)
¶
Validate that required Panel Javascript extensions are loaded in the code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python code to analyze |
required |
Raises:
| Type | Description |
|---|---|
ExtensionError
|
If a required extension is not loaded. |
Example¶
This code will raise an ExtensionError because 'tabulator' extension is not available:¶
code = ''' import pandas as pd import panel as pn pn.extension()
df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) pn.widgets.Tabulator(df).servable() '''
validate_extension_availability(code)
This code will pass as 'tabulator' extension is included:¶
code = ''' import pandas as pd import panel as pn pn.extension('tabulator') df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) pn.widgets.Tabulator(df).servable() '''
validate_extension_availability(code)
Note¶
pn.extension("tabulator", "plotly")
and
pn.extension("tabulator") pn.extension("plotly")
will both work correctly.
validation
¶
Code validation pipeline for panel-live-server.
Provides four static validation layers that run before code is stored:
- Layer 1
ast_check— syntax viaast.parse() - Layer 2
ruff_check— security rules viaruff(raisesSecurityError) - Layer 3
check_packages— all imports are installed - Formatting
ruff_format— autoformat viaruff format
Runtime execution (Layer 5) lives in utils.validate_code.
BLOCKED_IMPORTS = frozenset({'pickle', 'marshal', 'shelve', 'subprocess', 'multiprocessing', 'threading', 'socket', 'ctypes', 'importlib', 'ftplib', 'smtplib', 'telnetlib', 'webbrowser', 'xmlrpc'})
module-attribute
¶
IMPORT_TO_PACKAGE = {'PIL': 'Pillow', 'sklearn': 'scikit-learn', 'cv2': 'opencv-python', 'skimage': 'scikit-image', 'bs4': 'beautifulsoup4', 'yaml': 'PyYAML', 'dateutil': 'python-dateutil', 'dotenv': 'python-dotenv', 'gi': 'PyGObject', 'wx': 'wxPython', 'Crypto': 'pycryptodome', 'OpenSSL': 'pyOpenSSL', 'usb': 'pyusb', 'serial': 'pyserial', 'magic': 'python-magic', 'attr': 'attrs'}
module-attribute
¶
logger = logging.getLogger(__name__)
module-attribute
¶
SecurityError
¶
Bases: ToolError
Raised by show() when code contains a security violation.
Given a special class (separate from ValidationError) to signal seriousness — security violations are never auto-fixable and should not be retried without a substantive code rewrite. Particularly relevant in enterprise contexts where security policy enforcement is audited.
ValidationError
¶
Bases: ToolError
Raised by show() when code fails a non-security validation check.
Covers syntax errors, missing packages, and missing Panel extension declarations.
The message always begins with the layer name in brackets, e.g.
[syntax] invalid syntax so the LLM can identify the failing check at a glance.
ast_check(code)
¶
Return an error string if code has a syntax error, else None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python source to check. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Human-readable error with line/col info, or |
Examples:
check_packages(code)
¶
Check that all packages imported by code are installed.
Parses imports via AST and calls importlib.util.find_spec() for each
top-level module name. Stdlib modules are skipped. Returns an error string
for the first missing package, or None if everything is available.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python source to analyse. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Error string with install hint, or |
Examples:
ruff_check(code)
¶
Run import blocklist and ruff security checks on code.
First performs a fast AST-based blocked-import scan (does not depend on ruff being installed), then runs ruff for deeper static analysis.
Raises SecurityError if any violations are found.
Returns None silently if the code is clean or ruff is not installed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python source to check. |
required |
Raises:
| Type | Description |
|---|---|
SecurityError
|
If a blocked import is found or ruff reports any violations. |
Examples:
ruff_format(code)
¶
Autoformat code via ruff format and return the result.
Returns code unchanged if ruff is not installed or formatting fails.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Python source to format. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Formatted source, or original source on failure. |
Examples: