or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app.mdexpress.mdindex.mdreactive.mdrender.mdsession.mdtypes.mdui.md

types.mddocs/

0

# Types and Exceptions

1

2

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.

3

4

## Capabilities

5

6

### Exception Classes

7

8

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

9

10

```python { .api }

11

class SafeException(Exception):

12

"""

13

Exception that is safe to display to users in production.

14

15

Unlike regular exceptions, SafeException messages are shown to users

16

even when error sanitization is enabled, making them suitable for

17

user-facing error messages.

18

"""

19

def __init__(self, message: str) -> None:

20

"""Create a safe exception with a user-friendly message."""

21

22

class SilentException(Exception):

23

"""

24

Exception that fails silently without displaying error messages.

25

26

Used internally by functions like req() to cancel computation

27

without showing error messages to users.

28

"""

29

def __init__(self, message: str = "") -> None:

30

"""Create a silent exception."""

31

32

class SilentCancelOutputException(SilentException):

33

"""

34

Silent exception that cancels output without clearing existing content.

35

36

Similar to SilentException but preserves any existing output content

37

instead of clearing it.

38

"""

39

def __init__(self, message: str = "") -> None:

40

"""Create a silent cancel output exception."""

41

```

42

43

#### Usage Examples

44

45

```python

46

from shiny.types import SafeException, SilentException

47

48

def server(input: Inputs, output: Outputs, session: Session):

49

50

@output

51

@render.text

52

def data_summary():

53

try:

54

data = load_data(input.data_source())

55

return f"Loaded {len(data)} records"

56

57

except FileNotFoundError:

58

# Safe to show to users

59

raise SafeException("The selected data file could not be found. Please check your selection.")

60

61

except PermissionError:

62

# Safe user message for permission issues

63

raise SafeException("You don't have permission to access this data file.")

64

65

except Exception as e:

66

# Generic error - don't expose internal details

67

raise SafeException("An error occurred while loading the data. Please try again.")

68

69

@output

70

@render.plot

71

def analysis_plot():

72

# Use req() which raises SilentException internally

73

req(input.x_variable(), input.y_variable())

74

75

data = current_data()

76

if len(data) == 0:

77

# Silently cancel without showing error

78

raise SilentException("No data available")

79

80

return create_plot(data, input.x_variable(), input.y_variable())

81

82

@output

83

@render.table

84

def filtered_data():

85

base_data = get_base_data()

86

87

# Apply filters

88

filters = input.active_filters()

89

if not filters:

90

# Cancel output but don't clear existing table

91

raise SilentCancelOutputException("No filters active")

92

93

return apply_filters(base_data, filters)

94

```

95

96

### File Handling Types

97

98

Type definitions for handling file uploads and file information.

99

100

```python { .api }

101

class FileInfo(TypedDict):

102

"""

103

Information about an uploaded file.

104

105

Contains metadata about files uploaded through file input controls,

106

providing access to file properties and the server-side file path.

107

"""

108

name: str

109

"""Original filename as provided by the user."""

110

111

size: int

112

"""File size in bytes."""

113

114

type: str

115

"""MIME type of the uploaded file."""

116

117

datapath: str

118

"""Server-side path where the uploaded file is stored."""

119

```

120

121

#### Usage Examples

122

123

```python

124

from shiny.types import FileInfo

125

import pandas as pd

126

import os

127

128

def server(input: Inputs, output: Outputs, session: Session):

129

130

@output

131

@render.text

132

def file_info():

133

file: FileInfo | None = input.uploaded_file()

134

135

if file is None:

136

return "No file uploaded"

137

138

# Access file metadata

139

size_mb = file["size"] / (1024 * 1024)

140

141

return f"""

142

File Information:

143

- Name: {file['name']}

144

- Size: {size_mb:.2f} MB

145

- Type: {file['type']}

146

- Server Path: {file['datapath']}

147

"""

148

149

@output

150

@render.data_frame

151

def uploaded_data():

152

file: FileInfo | None = input.data_file()

153

154

if file is None:

155

return pd.DataFrame() # Empty DataFrame

156

157

# Validate file type

158

if not file["type"].startswith("text/csv"):

159

raise SafeException("Please upload a CSV file")

160

161

# Validate file size (10MB limit)

162

if file["size"] > 10 * 1024 * 1024:

163

raise SafeException("File size must be less than 10MB")

164

165

try:

166

# Read file using server path

167

data = pd.read_csv(file["datapath"])

168

return data

169

170

except pd.errors.EmptyDataError:

171

raise SafeException("The uploaded file is empty")

172

173

except pd.errors.ParserError:

174

raise SafeException("Unable to parse the CSV file. Please check the format.")

175

176

@reactive.effect

177

@reactive.event(input.process_file)

178

def process_uploaded_file():

179

file: FileInfo | None = input.processing_file()

180

181

if file is None:

182

return

183

184

# Create processed filename

185

base_name = os.path.splitext(file["name"])[0]

186

processed_name = f"{base_name}_processed.csv"

187

188

# Process the file

189

try:

190

original_data = pd.read_csv(file["datapath"])

191

processed_data = perform_data_processing(original_data)

192

193

# Save processed file

194

output_path = f"/tmp/{processed_name}"

195

processed_data.to_csv(output_path, index=False)

196

197

# Trigger download

198

session.download("processed_download", output_path)

199

200

except Exception as e:

201

raise SafeException(f"Error processing file: {str(e)}")

202

```

203

204

### Image Rendering Types

205

206

Type definitions for image rendering and display.

207

208

```python { .api }

209

class ImgData(TypedDict):

210

"""

211

Image data structure for render.image() output.

212

213

Provides complete control over image rendering including source,

214

dimensions, accessibility, and styling options.

215

"""

216

src: str

217

"""Image source URL or data URI."""

218

219

width: NotRequired[str | float]

220

"""Image width (CSS units or pixels)."""

221

222

height: NotRequired[str | float]

223

"""Image height (CSS units or pixels)."""

224

225

alt: NotRequired[str]

226

"""Alt text for accessibility."""

227

228

style: NotRequired[str]

229

"""CSS styles to apply to the image."""

230

231

coordmap: NotRequired[Any]

232

"""Coordinate mapping for interactive plots."""

233

```

234

235

#### Usage Examples

236

237

```python

238

from shiny.types import ImgData

239

import base64

240

import io

241

from PIL import Image, ImageDraw

242

243

def server(input: Inputs, output: Outputs, session: Session):

244

245

@output

246

@render.image

247

def dynamic_chart() -> ImgData:

248

# Generate image based on inputs

249

width, height = 400, 300

250

img = Image.new('RGB', (width, height), color='white')

251

draw = ImageDraw.Draw(img)

252

253

# Draw content based on user inputs

254

title = input.chart_title() or "Default Chart"

255

color = input.chart_color() or "blue"

256

257

# Draw some sample content

258

draw.rectangle([50, 50, width-50, height-50], outline=color, width=3)

259

draw.text((width//2 - 50, 20), title, fill='black')

260

261

# Convert to base64 data URI

262

buffer = io.BytesIO()

263

img.save(buffer, format='PNG')

264

buffer.seek(0)

265

img_data = base64.b64encode(buffer.getvalue()).decode()

266

267

return {

268

"src": f"data:image/png;base64,{img_data}",

269

"width": "100%",

270

"height": "300px",

271

"alt": f"Dynamic chart: {title}",

272

"style": "border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"

273

}

274

275

@output

276

@render.image

277

def responsive_image() -> ImgData:

278

# Create responsive image based on device characteristics

279

pixel_ratio = session.client_data.pixelratio

280

281

# Generate high-DPI image if needed

282

base_width = 300

283

base_height = 200

284

285

actual_width = int(base_width * pixel_ratio)

286

actual_height = int(base_height * pixel_ratio)

287

288

img = create_high_resolution_image(actual_width, actual_height)

289

290

# Save to temporary file

291

temp_path = f"/tmp/responsive_img_{id(session)}.png"

292

img.save(temp_path, format='PNG', dpi=(96 * pixel_ratio, 96 * pixel_ratio))

293

294

return {

295

"src": temp_path,

296

"width": f"{base_width}px",

297

"height": f"{base_height}px",

298

"alt": "Responsive high-DPI image",

299

"style": f"max-width: 100%; height: auto;"

300

}

301

302

@output

303

@render.image

304

def plot_with_interaction() -> ImgData:

305

# Generate plot that supports click interactions

306

plot_data = get_plot_data()

307

308

fig = create_interactive_plot(plot_data)

309

310

# Save plot with coordinate mapping

311

plot_path = "/tmp/interactive_plot.png"

312

fig.savefig(plot_path, dpi=150, bbox_inches='tight')

313

314

# Create coordinate mapping for click events

315

coord_map = generate_coordinate_mapping(fig)

316

317

return {

318

"src": plot_path,

319

"width": "800px",

320

"height": "600px",

321

"alt": "Interactive plot - click for details",

322

"coordmap": coord_map

323

}

324

```

325

326

### Utility Types and Constants

327

328

General utility types and constants used throughout the framework.

329

330

```python { .api }

331

class MISSING_TYPE:

332

"""

333

Type for the MISSING sentinel value.

334

335

Used to distinguish between None (which is a valid value) and

336

a parameter that was not provided at all.

337

"""

338

def __repr__(self) -> str:

339

return "MISSING"

340

341

MISSING: MISSING_TYPE

342

"""

343

Sentinel value indicating a missing/unspecified parameter.

344

345

Used throughout Shiny to distinguish between None (a valid value)

346

and parameters that were not provided by the user.

347

"""

348

349

Jsonifiable = str | int | float | bool | None | dict[str, Any] | list[Any]

350

"""

351

Type alias for values that can be JSON-serialized.

352

353

Represents the types that can be safely converted to JSON for

354

client-server communication in Shiny applications.

355

"""

356

```

357

358

#### Usage Examples

359

360

```python

361

from shiny.types import MISSING, MISSING_TYPE, Jsonifiable

362

363

def optional_parameter_function(

364

required_param: str,

365

optional_param: str | MISSING_TYPE = MISSING

366

) -> str:

367

"""Function demonstrating MISSING sentinel usage."""

368

369

if optional_param is MISSING:

370

# Parameter was not provided

371

return f"Required: {required_param}, Optional: not provided"

372

else:

373

# Parameter was provided (could be None)

374

return f"Required: {required_param}, Optional: {optional_param}"

375

376

# Usage examples

377

result1 = optional_parameter_function("hello")

378

# "Required: hello, Optional: not provided"

379

380

result2 = optional_parameter_function("hello", "world")

381

# "Required: hello, Optional: world"

382

383

result3 = optional_parameter_function("hello", None)

384

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

385

386

# JSON serialization helper

387

def send_data_to_client(data: Jsonifiable) -> None:

388

"""Send JSON-serializable data to client."""

389

import json

390

391

try:

392

json_string = json.dumps(data)

393

session.send_custom_message("data_update", {"data": data})

394

except TypeError as e:

395

raise SafeException(f"Data is not JSON-serializable: {e}")

396

397

def server(input: Inputs, output: Outputs, session: Session):

398

399

@reactive.effect

400

@reactive.event(input.send_data)

401

def send_analysis_results():

402

results = get_analysis_results()

403

404

# Ensure data is JSON-serializable

405

json_data: Jsonifiable = {

406

"summary": results.summary_dict(),

407

"metrics": results.metrics_list(),

408

"timestamp": datetime.now().isoformat(),

409

"success": True

410

}

411

412

send_data_to_client(json_data)

413

414

# Function using MISSING sentinel

415

def create_plot_with_optional_title(

416

data: pd.DataFrame,

417

title: str | MISSING_TYPE = MISSING

418

):

419

fig, ax = plt.subplots()

420

ax.plot(data['x'], data['y'])

421

422

if title is not MISSING:

423

ax.set_title(title)

424

else:

425

# Auto-generate title

426

ax.set_title(f"Plot of {data.columns[1]} vs {data.columns[0]}")

427

428

return fig

429

430

@output

431

@render.plot

432

def main_plot():

433

data = current_data()

434

435

# Title input might be empty string, None, or missing

436

user_title = input.plot_title()

437

438

if user_title: # Non-empty string

439

return create_plot_with_optional_title(data, user_title)

440

else: # Empty string or None - use auto title

441

return create_plot_with_optional_title(data) # MISSING used

442

```

443

444

### HTML and UI Types

445

446

Type aliases for HTML and UI component construction.

447

448

```python { .api }

449

# From htmltools (re-exported by shiny.types)

450

Tag: TypeAlias

451

"""HTML tag object."""

452

453

TagAttrs: TypeAlias

454

"""HTML tag attributes dictionary."""

455

456

TagAttrValue: TypeAlias

457

"""Valid values for HTML tag attributes."""

458

459

TagChild: TypeAlias

460

"""Valid child content for HTML tags."""

461

462

TagList: TypeAlias

463

"""List of HTML tags or tag children."""

464

```

465

466

#### Usage Examples

467

468

```python

469

from shiny.types import Tag, TagChild, TagAttrs

470

from shiny import ui

471

472

def create_custom_card(

473

title: str,

474

content: TagChild,

475

**attrs: TagAttrs

476

) -> Tag:

477

"""Create a custom card component."""

478

479

return ui.div(

480

ui.div(

481

ui.h4(title, class_="card-title"),

482

class_="card-header"

483

),

484

ui.div(

485

content,

486

class_="card-body"

487

),

488

class_="card",

489

**attrs

490

)

491

492

def create_info_section(

493

items: list[tuple[str, TagChild]]

494

) -> Tag:

495

"""Create an information section from key-value pairs."""

496

497

info_items = []

498

for key, value in items:

499

info_items.append(

500

ui.div(

501

ui.strong(f"{key}: "),

502

value,

503

class_="info-item"

504

)

505

)

506

507

return ui.div(

508

*info_items,

509

class_="info-section"

510

)

511

512

# Usage in server function

513

def server(input: Inputs, output: Outputs, session: Session):

514

515

@output

516

@render.ui

517

def dynamic_card():

518

return create_custom_card(

519

title="Analysis Results",

520

content=ui.div(

521

ui.p(f"Dataset has {len(current_data())} rows"),

522

ui.p(f"Selected variables: {input.variables()}")

523

),

524

id="results-card",

525

style="margin: 20px 0;"

526

)

527

528

@output

529

@render.ui

530

def system_info():

531

return create_info_section([

532

("Session ID", str(id(session))),

533

("Client IP", session.client_data.url_hostname),

534

("User Agent", "Modern Browser"),

535

("Connected At", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

536

])

537

```

538

539

### Type Checking and Validation

540

541

Utilities for runtime type checking and validation.

542

543

```python { .api }

544

def is_jsonifiable(obj: Any) -> bool:

545

"""

546

Check if an object can be JSON-serialized.

547

548

Args:

549

obj: Object to check.

550

551

Returns:

552

True if object is JSON-serializable.

553

"""

554

555

def validate_file_info(obj: Any) -> FileInfo:

556

"""

557

Validate and return a FileInfo object.

558

559

Args:

560

obj: Object to validate as FileInfo.

561

562

Returns:

563

Validated FileInfo object.

564

565

Raises:

566

TypeError: If object is not a valid FileInfo.

567

"""

568

569

def validate_img_data(obj: Any) -> ImgData:

570

"""

571

Validate and return an ImgData object.

572

573

Args:

574

obj: Object to validate as ImgData.

575

576

Returns:

577

Validated ImgData object.

578

579

Raises:

580

TypeError: If object is not a valid ImgData.

581

"""

582

```

583

584

#### Usage Examples

585

586

```python

587

from shiny.types import is_jsonifiable, validate_file_info, validate_img_data

588

589

def server(input: Inputs, output: Outputs, session: Session):

590

591

@reactive.calc

592

def safe_json_data():

593

raw_data = get_raw_analysis_results()

594

595

# Ensure all data is JSON-serializable before sending to client

596

cleaned_data = {}

597

for key, value in raw_data.items():

598

if is_jsonifiable(value):

599

cleaned_data[key] = value

600

else:

601

# Convert non-serializable data to string representation

602

cleaned_data[key] = str(value)

603

604

return cleaned_data

605

606

@output

607

@render.text

608

def file_validation_result():

609

uploaded = input.data_file()

610

611

if uploaded is None:

612

return "No file uploaded"

613

614

try:

615

# Validate file info structure

616

file_info = validate_file_info(uploaded)

617

return f"Valid file: {file_info['name']} ({file_info['size']} bytes)"

618

619

except TypeError as e:

620

return f"Invalid file info: {e}"

621

622

@output

623

@render.image

624

def validated_image():

625

try:

626

img_data = generate_image_data()

627

628

# Validate image data structure

629

validated = validate_img_data(img_data)

630

return validated

631

632

except TypeError as e:

633

# Return error image

634

return {

635

"src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwMCI+PHRleHQgeD0iMTAiIHk9IjUwIj5FcnJvcjwvdGV4dD48L3N2Zz4=",

636

"alt": f"Image generation error: {e}"

637

}

638

```