Skip to content

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.

config = get_config() module-attribute

logger = logging.getLogger(__name__) module-attribute

main(address='localhost', port=5077, show=True)

Start the Panel server.

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://:/feed to see visualizations as they are created.

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 a new snippet record.

Parameters:

Name Type Description Default
snippet Snippet

Snippet record to create

required

Returns:

Type Description
Snippet

Created snippet record with ID

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.

logger = logging.getLogger(__name__) module-attribute

HealthEndpoint

Bases: RequestHandler

Tornado RequestHandler for /api/health endpoint.

get()

Handle GET requests to check server health.

SnippetEndpoint

Bases: RequestHandler

Tornado RequestHandler for /api/snippet endpoint.

post()

Handle POST requests to store snippets and create visualizations.

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: "visualization", "data", "panel", "core". Default "core" returns the union of visualization + data + panel (~30 packages). Use "" (empty string) to return all installed packages.

'core'
query str

Case-insensitive substring filter on package name. Example: "panel" returns only packages with "panel" in their name.

''
include_versions bool

If True, return {"name": ..., "version": ...} dicts. Default False returns a flat list of package name strings to minimize context window usage.

False

Returns:

Type Description
list[str] | list[dict[str, str]]

Sorted list of package names (default) or {"name": ..., "version": ...} dicts when include_versions=True.

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 prior validate() call needed. Ideal for simple plots with well-known libraries.
  • Standard (quick=False, default): expects validate(code, method) to have been called first. show reuses 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 .servable(). Use when the code imports and uses Panel to build dashboards, apps, or complex layouts.

"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 True, run full validation (static checks + runtime execution) inline before rendering — no prior validate() call needed. Use for simple, well-known visualizations to save a round-trip.

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 method parameter of show.

"jupyter"

Returns:

Type Description
dict

{"valid": True} on success, or {"valid": False, "layer": "...", "message": "..."} describing the first failing check. Layers: "syntax", "security", "packages", "extensions", "runtime".

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:

>>> namespace = execute_in_module(
...     "x = 1\ny = 2\nz = x + y",
...     "my_module"
... )
>>> namespace['z']
3

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 via ast.parse()
  • Layer 2 ruff_check — security rules via ruff (raises SecurityError)
  • Layer 3 check_packages — all imports are installed
  • Formatting ruff_format — autoformat via ruff 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 None if syntax is valid.

Examples:

>>> ast_check("x = 1")
>>> ast_check("if True")
'expected \':\' (line 1, col 8)'

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 None if all packages are available.

Examples:

>>> check_packages("import os\\nimport json") is None
True
>>> check_packages("import numpy") is None
True
>>> "list_packages" in (check_packages("import _totally_fake_pkg_xyz") or "")
True

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_check("x = 1")
>>> ruff_check("import pickle")
SecurityError: ...

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:

>>> ruff_format("x=1+2") == "x = 1 + 2\\n"
True