CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-shiny

A web development framework for Python that enables building fast, beautiful, and interactive web applications using reactive programming principles.

Pending
Overview
Eval results
Files

types.mddocs/

Types and Exceptions

Type definitions, exception classes, and utility types used throughout the Shiny framework. This module provides the foundational types that enable type safety and proper error handling in Shiny applications.

Capabilities

Exception Classes

Custom exception types for handling different error conditions in Shiny applications.

class SafeException(Exception):
    """
    Exception that is safe to display to users in production.
    
    Unlike regular exceptions, SafeException messages are shown to users
    even when error sanitization is enabled, making them suitable for
    user-facing error messages.
    """
    def __init__(self, message: str) -> None:
        """Create a safe exception with a user-friendly message."""

class SilentException(Exception):
    """
    Exception that fails silently without displaying error messages.
    
    Used internally by functions like req() to cancel computation
    without showing error messages to users.
    """
    def __init__(self, message: str = "") -> None:
        """Create a silent exception."""

class SilentCancelOutputException(SilentException):
    """
    Silent exception that cancels output without clearing existing content.
    
    Similar to SilentException but preserves any existing output content
    instead of clearing it.
    """
    def __init__(self, message: str = "") -> None:
        """Create a silent cancel output exception."""

Usage Examples

from shiny.types import SafeException, SilentException

def server(input: Inputs, output: Outputs, session: Session):
    
    @output
    @render.text
    def data_summary():
        try:
            data = load_data(input.data_source())
            return f"Loaded {len(data)} records"
        
        except FileNotFoundError:
            # Safe to show to users
            raise SafeException("The selected data file could not be found. Please check your selection.")
        
        except PermissionError:
            # Safe user message for permission issues
            raise SafeException("You don't have permission to access this data file.")
        
        except Exception as e:
            # Generic error - don't expose internal details
            raise SafeException("An error occurred while loading the data. Please try again.")
    
    @output
    @render.plot
    def analysis_plot():
        # Use req() which raises SilentException internally
        req(input.x_variable(), input.y_variable())
        
        data = current_data()
        if len(data) == 0:
            # Silently cancel without showing error
            raise SilentException("No data available")
        
        return create_plot(data, input.x_variable(), input.y_variable())
    
    @output
    @render.table
    def filtered_data():
        base_data = get_base_data()
        
        # Apply filters
        filters = input.active_filters()
        if not filters:
            # Cancel output but don't clear existing table
            raise SilentCancelOutputException("No filters active")
        
        return apply_filters(base_data, filters)

File Handling Types

Type definitions for handling file uploads and file information.

class FileInfo(TypedDict):
    """
    Information about an uploaded file.
    
    Contains metadata about files uploaded through file input controls,
    providing access to file properties and the server-side file path.
    """
    name: str
        """Original filename as provided by the user."""
    
    size: int
        """File size in bytes."""
    
    type: str
        """MIME type of the uploaded file."""
    
    datapath: str
        """Server-side path where the uploaded file is stored."""

Usage Examples

from shiny.types import FileInfo
import pandas as pd
import os

def server(input: Inputs, output: Outputs, session: Session):
    
    @output
    @render.text
    def file_info():
        file: FileInfo | None = input.uploaded_file()
        
        if file is None:
            return "No file uploaded"
        
        # Access file metadata
        size_mb = file["size"] / (1024 * 1024)
        
        return f"""
        File Information:
        - Name: {file['name']}
        - Size: {size_mb:.2f} MB
        - Type: {file['type']}
        - Server Path: {file['datapath']}
        """
    
    @output
    @render.data_frame
    def uploaded_data():
        file: FileInfo | None = input.data_file()
        
        if file is None:
            return pd.DataFrame()  # Empty DataFrame
        
        # Validate file type
        if not file["type"].startswith("text/csv"):
            raise SafeException("Please upload a CSV file")
        
        # Validate file size (10MB limit)
        if file["size"] > 10 * 1024 * 1024:
            raise SafeException("File size must be less than 10MB")
        
        try:
            # Read file using server path
            data = pd.read_csv(file["datapath"])
            return data
        
        except pd.errors.EmptyDataError:
            raise SafeException("The uploaded file is empty")
        
        except pd.errors.ParserError:
            raise SafeException("Unable to parse the CSV file. Please check the format.")
    
    @reactive.effect
    @reactive.event(input.process_file)
    def process_uploaded_file():
        file: FileInfo | None = input.processing_file()
        
        if file is None:
            return
        
        # Create processed filename
        base_name = os.path.splitext(file["name"])[0]
        processed_name = f"{base_name}_processed.csv"
        
        # Process the file
        try:
            original_data = pd.read_csv(file["datapath"])
            processed_data = perform_data_processing(original_data)
            
            # Save processed file
            output_path = f"/tmp/{processed_name}"
            processed_data.to_csv(output_path, index=False)
            
            # Trigger download
            session.download("processed_download", output_path)
            
        except Exception as e:
            raise SafeException(f"Error processing file: {str(e)}")

Image Rendering Types

Type definitions for image rendering and display.

class ImgData(TypedDict):
    """
    Image data structure for render.image() output.
    
    Provides complete control over image rendering including source,
    dimensions, accessibility, and styling options.
    """
    src: str
        """Image source URL or data URI."""
    
    width: NotRequired[str | float]
        """Image width (CSS units or pixels)."""
    
    height: NotRequired[str | float]
        """Image height (CSS units or pixels)."""
    
    alt: NotRequired[str]
        """Alt text for accessibility."""
    
    style: NotRequired[str]
        """CSS styles to apply to the image."""
    
    coordmap: NotRequired[Any]
        """Coordinate mapping for interactive plots."""

Usage Examples

from shiny.types import ImgData
import base64
import io
from PIL import Image, ImageDraw

def server(input: Inputs, output: Outputs, session: Session):
    
    @output
    @render.image
    def dynamic_chart() -> ImgData:
        # Generate image based on inputs
        width, height = 400, 300
        img = Image.new('RGB', (width, height), color='white')
        draw = ImageDraw.Draw(img)
        
        # Draw content based on user inputs
        title = input.chart_title() or "Default Chart"
        color = input.chart_color() or "blue"
        
        # Draw some sample content
        draw.rectangle([50, 50, width-50, height-50], outline=color, width=3)
        draw.text((width//2 - 50, 20), title, fill='black')
        
        # Convert to base64 data URI
        buffer = io.BytesIO()
        img.save(buffer, format='PNG')
        buffer.seek(0)
        img_data = base64.b64encode(buffer.getvalue()).decode()
        
        return {
            "src": f"data:image/png;base64,{img_data}",
            "width": "100%",
            "height": "300px",
            "alt": f"Dynamic chart: {title}",
            "style": "border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
        }
    
    @output
    @render.image
    def responsive_image() -> ImgData:
        # Create responsive image based on device characteristics
        pixel_ratio = session.client_data.pixelratio
        
        # Generate high-DPI image if needed
        base_width = 300
        base_height = 200
        
        actual_width = int(base_width * pixel_ratio)
        actual_height = int(base_height * pixel_ratio)
        
        img = create_high_resolution_image(actual_width, actual_height)
        
        # Save to temporary file
        temp_path = f"/tmp/responsive_img_{id(session)}.png"
        img.save(temp_path, format='PNG', dpi=(96 * pixel_ratio, 96 * pixel_ratio))
        
        return {
            "src": temp_path,
            "width": f"{base_width}px",
            "height": f"{base_height}px",
            "alt": "Responsive high-DPI image",
            "style": f"max-width: 100%; height: auto;"
        }
    
    @output
    @render.image  
    def plot_with_interaction() -> ImgData:
        # Generate plot that supports click interactions
        plot_data = get_plot_data()
        
        fig = create_interactive_plot(plot_data)
        
        # Save plot with coordinate mapping
        plot_path = "/tmp/interactive_plot.png"
        fig.savefig(plot_path, dpi=150, bbox_inches='tight')
        
        # Create coordinate mapping for click events
        coord_map = generate_coordinate_mapping(fig)
        
        return {
            "src": plot_path,
            "width": "800px",
            "height": "600px",
            "alt": "Interactive plot - click for details",
            "coordmap": coord_map
        }

Utility Types and Constants

General utility types and constants used throughout the framework.

class MISSING_TYPE:
    """
    Type for the MISSING sentinel value.
    
    Used to distinguish between None (which is a valid value) and
    a parameter that was not provided at all.
    """
    def __repr__(self) -> str:
        return "MISSING"

MISSING: MISSING_TYPE
    """
    Sentinel value indicating a missing/unspecified parameter.
    
    Used throughout Shiny to distinguish between None (a valid value)
    and parameters that were not provided by the user.
    """

Jsonifiable = str | int | float | bool | None | dict[str, Any] | list[Any]
    """
    Type alias for values that can be JSON-serialized.
    
    Represents the types that can be safely converted to JSON for
    client-server communication in Shiny applications.
    """

Usage Examples

from shiny.types import MISSING, MISSING_TYPE, Jsonifiable

def optional_parameter_function(
    required_param: str,
    optional_param: str | MISSING_TYPE = MISSING
) -> str:
    """Function demonstrating MISSING sentinel usage."""
    
    if optional_param is MISSING:
        # Parameter was not provided
        return f"Required: {required_param}, Optional: not provided"
    else:
        # Parameter was provided (could be None)
        return f"Required: {required_param}, Optional: {optional_param}"

# Usage examples
result1 = optional_parameter_function("hello")
# "Required: hello, Optional: not provided"

result2 = optional_parameter_function("hello", "world")
# "Required: hello, Optional: world"

result3 = optional_parameter_function("hello", None)
# "Required: hello, Optional: None" (None is a valid value)

# JSON serialization helper
def send_data_to_client(data: Jsonifiable) -> None:
    """Send JSON-serializable data to client."""
    import json
    
    try:
        json_string = json.dumps(data)
        session.send_custom_message("data_update", {"data": data})
    except TypeError as e:
        raise SafeException(f"Data is not JSON-serializable: {e}")

def server(input: Inputs, output: Outputs, session: Session):
    
    @reactive.effect
    @reactive.event(input.send_data)
    def send_analysis_results():
        results = get_analysis_results()
        
        # Ensure data is JSON-serializable
        json_data: Jsonifiable = {
            "summary": results.summary_dict(),
            "metrics": results.metrics_list(),
            "timestamp": datetime.now().isoformat(),
            "success": True
        }
        
        send_data_to_client(json_data)
    
    # Function using MISSING sentinel
    def create_plot_with_optional_title(
        data: pd.DataFrame,
        title: str | MISSING_TYPE = MISSING
    ):
        fig, ax = plt.subplots()
        ax.plot(data['x'], data['y'])
        
        if title is not MISSING:
            ax.set_title(title)
        else:
            # Auto-generate title
            ax.set_title(f"Plot of {data.columns[1]} vs {data.columns[0]}")
        
        return fig
    
    @output
    @render.plot
    def main_plot():
        data = current_data()
        
        # Title input might be empty string, None, or missing
        user_title = input.plot_title()
        
        if user_title:  # Non-empty string
            return create_plot_with_optional_title(data, user_title)
        else:  # Empty string or None - use auto title
            return create_plot_with_optional_title(data)  # MISSING used

HTML and UI Types

Type aliases for HTML and UI component construction.

# From htmltools (re-exported by shiny.types)
Tag: TypeAlias
    """HTML tag object."""

TagAttrs: TypeAlias  
    """HTML tag attributes dictionary."""

TagAttrValue: TypeAlias
    """Valid values for HTML tag attributes."""

TagChild: TypeAlias
    """Valid child content for HTML tags."""

TagList: TypeAlias
    """List of HTML tags or tag children."""

Usage Examples

from shiny.types import Tag, TagChild, TagAttrs
from shiny import ui

def create_custom_card(
    title: str,
    content: TagChild,
    **attrs: TagAttrs
) -> Tag:
    """Create a custom card component."""
    
    return ui.div(
        ui.div(
            ui.h4(title, class_="card-title"),
            class_="card-header"
        ),
        ui.div(
            content,
            class_="card-body"
        ),
        class_="card",
        **attrs
    )

def create_info_section(
    items: list[tuple[str, TagChild]]
) -> Tag:
    """Create an information section from key-value pairs."""
    
    info_items = []
    for key, value in items:
        info_items.append(
            ui.div(
                ui.strong(f"{key}: "),
                value,
                class_="info-item"
            )
        )
    
    return ui.div(
        *info_items,
        class_="info-section"
    )

# Usage in server function
def server(input: Inputs, output: Outputs, session: Session):
    
    @output
    @render.ui
    def dynamic_card():
        return create_custom_card(
            title="Analysis Results",
            content=ui.div(
                ui.p(f"Dataset has {len(current_data())} rows"),
                ui.p(f"Selected variables: {input.variables()}")
            ),
            id="results-card",
            style="margin: 20px 0;"
        )
    
    @output
    @render.ui
    def system_info():
        return create_info_section([
            ("Session ID", str(id(session))),
            ("Client IP", session.client_data.url_hostname),
            ("User Agent", "Modern Browser"),
            ("Connected At", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        ])

Type Checking and Validation

Utilities for runtime type checking and validation.

def is_jsonifiable(obj: Any) -> bool:
    """
    Check if an object can be JSON-serialized.
    
    Args:
        obj: Object to check.
        
    Returns:
        True if object is JSON-serializable.
    """

def validate_file_info(obj: Any) -> FileInfo:
    """
    Validate and return a FileInfo object.
    
    Args:
        obj: Object to validate as FileInfo.
        
    Returns:
        Validated FileInfo object.
        
    Raises:
        TypeError: If object is not a valid FileInfo.
    """

def validate_img_data(obj: Any) -> ImgData:
    """
    Validate and return an ImgData object.
    
    Args:
        obj: Object to validate as ImgData.
        
    Returns:
        Validated ImgData object.
        
    Raises:
        TypeError: If object is not a valid ImgData.
    """

Usage Examples

from shiny.types import is_jsonifiable, validate_file_info, validate_img_data

def server(input: Inputs, output: Outputs, session: Session):
    
    @reactive.calc
    def safe_json_data():
        raw_data = get_raw_analysis_results()
        
        # Ensure all data is JSON-serializable before sending to client
        cleaned_data = {}
        for key, value in raw_data.items():
            if is_jsonifiable(value):
                cleaned_data[key] = value
            else:
                # Convert non-serializable data to string representation
                cleaned_data[key] = str(value)
        
        return cleaned_data
    
    @output
    @render.text
    def file_validation_result():
        uploaded = input.data_file()
        
        if uploaded is None:
            return "No file uploaded"
        
        try:
            # Validate file info structure
            file_info = validate_file_info(uploaded)
            return f"Valid file: {file_info['name']} ({file_info['size']} bytes)"
        
        except TypeError as e:
            return f"Invalid file info: {e}"
    
    @output
    @render.image
    def validated_image():
        try:
            img_data = generate_image_data()
            
            # Validate image data structure
            validated = validate_img_data(img_data)
            return validated
        
        except TypeError as e:
            # Return error image
            return {
                "src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwMCI+PHRleHQgeD0iMTAiIHk9IjUwIj5FcnJvcjwvdGV4dD48L3N2Zz4=",
                "alt": f"Image generation error: {e}"
            }

Install with Tessl CLI

npx tessl i tessl/pypi-shiny

docs

app.md

express.md

index.md

reactive.md

render.md

session.md

types.md

ui.md

tile.json