0
# Core Application Framework
1
2
Essential classes and patterns for building Lona web applications. The core framework provides the fundamental components needed to create interactive web applications entirely in Python, including application setup, view handling, request processing, and routing.
3
4
## Capabilities
5
6
### Application Class
7
8
The main application class that serves as the entry point for Lona web applications, managing routes, middleware, templates, static files, and server configuration.
9
10
```python { .api }
11
class App:
12
def __init__(self, script_path: str):
13
"""
14
Initialize a Lona application.
15
16
Args:
17
script_path (str): Path to the main script file (__file__)
18
"""
19
20
def route(self, raw_pattern: str | int, name: str = '', interactive: bool = True,
21
http_pass_through: bool = False, frontend_view=None):
22
"""
23
Decorator for registering view routes.
24
25
Args:
26
raw_pattern (str | int): URL pattern to match or MATCH_ALL constant
27
name (str): Optional route name for reverse URL generation
28
interactive (bool): Whether the route supports interactivity
29
http_pass_through (bool): Pass HTTP requests directly to view
30
frontend_view: Optional frontend view function
31
32
Returns:
33
Decorator function
34
"""
35
36
def middleware(self):
37
"""
38
Decorator for registering middleware functions.
39
40
Returns:
41
Decorator function
42
"""
43
44
def frontend_view(self):
45
"""
46
Decorator for registering frontend views.
47
48
Returns:
49
Decorator function
50
"""
51
52
def error_403_view(self):
53
"""
54
Decorator for registering 403 error views.
55
56
Returns:
57
Decorator function
58
"""
59
60
def error_404_view(self):
61
"""
62
Decorator for registering 404 error views.
63
64
Returns:
65
Decorator function
66
"""
67
68
def error_500_view(self):
69
"""
70
Decorator for registering 500 error views.
71
72
Returns:
73
Decorator function
74
"""
75
76
def add_template(self, name: str, string: str = '', path: str = ''):
77
"""
78
Add a template to the application.
79
80
Args:
81
name (str): Template name
82
string (str): Template content as string
83
path (str): Path to template file
84
"""
85
86
def add_static_file(self, name: str, string: str = '', path: str = ''):
87
"""
88
Add a static file to the application.
89
90
Args:
91
name (str): Static file name
92
string (str): File content as string
93
path (str): Path to static file
94
"""
95
96
def add_route(self, route: 'Route'):
97
"""
98
Programmatically add a route to the application.
99
100
Args:
101
route (Route): Route object to add
102
"""
103
104
def run(self, host: str = 'localhost', port: int = 8080,
105
loop_class=None, **kwargs):
106
"""
107
Run the application development server.
108
109
Args:
110
host (str): Server host address
111
port (int): Server port number
112
loop_class: Optional event loop class
113
**kwargs: Additional server arguments
114
"""
115
116
def setup_server(self, host: str = 'localhost', port: int = 8080):
117
"""
118
Setup the server without running it.
119
120
Args:
121
host (str): Server host address
122
port (int): Server port number
123
124
Returns:
125
Server instance
126
"""
127
```
128
129
#### Usage Example
130
131
```python
132
from lona import App, View
133
from lona.html import HTML, H1
134
135
app = App(__file__)
136
137
@app.route('/')
138
class IndexView(View):
139
def handle_request(self, request):
140
return HTML(H1('Hello World'))
141
142
# Add middleware
143
@app.middleware()
144
def my_middleware(request):
145
request.custom_data = 'processed'
146
return request
147
148
# Add template
149
app.add_template('base', '''
150
<html>
151
<head><title>{{ title }}</title></head>
152
<body>{{ content }}</body>
153
</html>
154
''')
155
156
if __name__ == '__main__':
157
app.run(host='0.0.0.0', port=8080)
158
```
159
160
### View Class
161
162
Base class for creating views that handle HTTP requests and user interactions. Views can be stateless (for simple HTTP responses) or stateful (for interactive applications with real-time user interaction).
163
164
```python { .api }
165
class View:
166
def handle_request(self, request: 'Request'):
167
"""
168
Main request handler method - override this in subclasses.
169
170
Args:
171
request (Request): The HTTP request object
172
173
Returns:
174
Response object, HTML content, or None for interactive views
175
"""
176
177
def show(self, html=None, template: str = None, title: str = None):
178
"""
179
Display HTML content to the user and enter interactive mode.
180
181
Args:
182
html: HTML content to display
183
template (str): Template name to render
184
title (str): Page title
185
"""
186
187
def set_title(self, title: str):
188
"""
189
Set the page title.
190
191
Args:
192
title (str): New page title
193
"""
194
195
def await_input_event(self, *nodes, html=None, timeout=None) -> 'InputEvent':
196
"""
197
Wait for any input event on specified nodes.
198
199
Args:
200
*nodes: HTML nodes to monitor for events
201
html: Optional HTML to show before waiting
202
timeout: Optional timeout in seconds
203
204
Returns:
205
InputEvent object containing event details
206
"""
207
208
def await_click(self, *nodes, html=None, timeout=None) -> 'InputEvent':
209
"""
210
Wait for click events on specified nodes.
211
212
Args:
213
*nodes: HTML nodes to monitor for clicks
214
html: Optional HTML to show before waiting
215
timeout: Optional timeout in seconds
216
217
Returns:
218
InputEvent object containing click details
219
"""
220
221
def await_change(self, *nodes, html=None, timeout=None) -> 'InputEvent':
222
"""
223
Wait for change events on specified nodes.
224
225
Args:
226
*nodes: HTML nodes to monitor for changes
227
html: Optional HTML to show before waiting
228
timeout: Optional timeout in seconds
229
230
Returns:
231
InputEvent object containing change details
232
"""
233
234
def await_focus(self, *nodes, html=None, timeout=None) -> 'InputEvent':
235
"""
236
Wait for focus events on specified nodes.
237
238
Args:
239
*nodes: HTML nodes to monitor for focus
240
html: Optional HTML to show before waiting
241
timeout: Optional timeout in seconds
242
243
Returns:
244
InputEvent object containing focus details
245
"""
246
247
def await_blur(self, *nodes, html=None, timeout=None) -> 'InputEvent':
248
"""
249
Wait for blur events on specified nodes.
250
251
Args:
252
*nodes: HTML nodes to monitor for blur
253
html: Optional HTML to show before waiting
254
timeout: Optional timeout in seconds
255
256
Returns:
257
InputEvent object containing blur details
258
"""
259
260
def send_str(self, string: str, broadcast: bool = False):
261
"""
262
Send a string message to the frontend.
263
264
Args:
265
string (str): Message to send
266
broadcast (bool): Whether to broadcast to all connected clients
267
"""
268
269
def fire_view_event(self, name: str, data=None):
270
"""
271
Fire a custom view event.
272
273
Args:
274
name (str): Event name
275
data: Event data payload
276
"""
277
278
def subscribe(self, topic: str, handler):
279
"""
280
Subscribe to a channel topic.
281
282
Args:
283
topic (str): Channel topic name
284
handler: Function to handle messages
285
"""
286
287
def unsubscribe(self, topic: str = None):
288
"""
289
Unsubscribe from channel topics.
290
291
Args:
292
topic (str): Specific topic to unsubscribe from, or None for all
293
"""
294
295
def sleep(self, delay: float):
296
"""
297
Async sleep for the specified delay.
298
299
Args:
300
delay (float): Sleep duration in seconds
301
"""
302
303
def is_daemon_view(self) -> bool:
304
"""
305
Check if this view runs as a daemon.
306
307
Returns:
308
bool: True if view is a daemon
309
"""
310
311
def is_interactive(self) -> bool:
312
"""
313
Check if this view supports interactivity.
314
315
Returns:
316
bool: True if view is interactive
317
"""
318
319
# Properties
320
request: 'Request' # Current request object
321
server: object # Server instance
322
logger: object # Logger instance
323
```
324
325
#### Usage Example
326
327
```python
328
from lona import App, View
329
from lona.html import HTML, H1, Button, P
330
from lona.responses import JsonResponse
331
332
app = App(__file__)
333
334
# Simple HTTP view
335
@app.route('/api/status')
336
class StatusView(View):
337
def handle_request(self, request):
338
return JsonResponse({'status': 'ok'})
339
340
# Interactive view
341
@app.route('/counter')
342
class CounterView(View):
343
def handle_request(self, request):
344
count = 0
345
button = Button('Increment')
346
counter_text = P(f'Count: {count}')
347
348
html = HTML(
349
H1('Interactive Counter'),
350
counter_text,
351
button
352
)
353
354
self.show(html)
355
356
while True:
357
self.await_click(button)
358
count += 1
359
counter_text.set_text(f'Count: {count}')
360
```
361
362
### Request Class
363
364
Represents an HTTP request with additional Lona-specific functionality for handling interactive applications, user sessions, and routing information.
365
366
```python { .api }
367
class Request:
368
# Request data
369
GET: dict # GET parameters
370
POST: dict # POST parameters
371
method: str # HTTP method (GET, POST, etc.)
372
url: str # Request URL
373
374
# User and session
375
user: object # User object (if authentication middleware used)
376
377
# Routing information
378
route: 'Route' # Matched route object
379
match_info: dict # URL pattern match information
380
381
# Lona-specific
382
interactive: bool # Whether request supports interactivity
383
connection: object # Connection object for interactive features
384
view: 'View' # Associated view instance (set during processing)
385
386
# Headers and metadata
387
headers: dict # Request headers
388
content_type: str # Content type header
389
content_length: int # Content length
390
391
# Session data (if session middleware enabled)
392
session: dict # Session data dictionary
393
```
394
395
#### Usage Example
396
397
```python
398
@app.route('/user/<int:user_id>')
399
class UserProfileView(View):
400
def handle_request(self, request):
401
# Access URL parameters
402
user_id = request.match_info['user_id']
403
404
# Access GET/POST data
405
search_query = request.GET.get('q', '')
406
407
# Check request method
408
if request.method == 'POST':
409
# Handle form submission
410
name = request.POST.get('name', '')
411
412
# Access user (if authenticated)
413
if request.user:
414
username = request.user.username
415
416
# Check if interactive
417
if request.interactive:
418
# Can use interactive features
419
self.show(HTML(H1(f'User {user_id}')))
420
else:
421
# Return static response
422
return HtmlResponse(f'<h1>User {user_id}</h1>')
423
```
424
425
### Route Class
426
427
Defines URL routing patterns and associates them with view classes or functions, supporting both interactive and non-interactive routes with optional HTTP pass-through.
428
429
```python { .api }
430
class Route:
431
def __init__(self, raw_pattern: str, view, name: str = '',
432
interactive: bool = True, http_pass_through: bool = False,
433
frontend_view=None):
434
"""
435
Create a new route definition.
436
437
Args:
438
raw_pattern (str): URL pattern (supports placeholders like <int:id>)
439
view: View class or function to handle requests
440
name (str): Optional route name for reverse URL generation
441
interactive (bool): Whether route supports interactive features
442
http_pass_through (bool): Pass HTTP requests directly to view
443
frontend_view: Optional frontend view function
444
"""
445
446
# Properties
447
raw_pattern: str # Original URL pattern string
448
view: object # View class or function
449
name: str # Route name for reverse lookups
450
interactive: bool # Interactive support flag
451
http_pass_through: bool # HTTP pass-through flag
452
frontend_view: object # Frontend view function
453
```
454
455
#### Usage Example
456
457
```python
458
from lona import App, Route, View
459
from lona.html import HTML, H1
460
461
app = App(__file__)
462
463
# Using decorator (recommended)
464
@app.route('/users/<int:user_id>', name='user_profile', interactive=True)
465
class UserView(View):
466
def handle_request(self, request):
467
user_id = request.match_info['user_id']
468
return HTML(H1(f'User {user_id}'))
469
470
# Programmatic route creation
471
class ApiView(View):
472
def handle_request(self, request):
473
return JsonResponse({'api': 'v1'})
474
475
api_route = Route('/api/v1', ApiView, name='api', interactive=False)
476
app.add_route(api_route)
477
478
# Route matching constant
479
from lona import MATCH_ALL
480
481
@app.route(MATCH_ALL, name='catch_all') # Matches any URL
482
class CatchAllView(View):
483
def handle_request(self, request):
484
return HTML(H1('Page not found'))
485
```
486
487
## Types
488
489
```python { .api }
490
from typing import Union, Optional, Dict, List, Callable, Any, Type
491
492
# Core framework types
493
ViewClass = Type['View']
494
ViewFunction = Callable[['Request'], Any]
495
ViewHandler = Union[ViewClass, ViewFunction]
496
MiddlewareHandler = Callable[['Request'], 'Request']
497
RoutePattern = Union[str, int] # int for MATCH_ALL constant
498
Settings = Union[object, Dict[str, Any]]
499
```