0
# Application Wrappers
1
2
Wrapper classes that adapt ASGI and WSGI applications to Hypercorn's internal protocol interface. These wrappers handle protocol translation, request/response lifecycle management, and provide the bridge between applications and Hypercorn's event-driven architecture.
3
4
## Capabilities
5
6
### ASGI Application Wrapper
7
8
Wrapper for ASGI applications that provides the interface between ASGI applications and Hypercorn's internal protocol handling.
9
10
```python { .api }
11
class ASGIWrapper:
12
"""
13
Wrapper for ASGI applications.
14
15
Adapts ASGI applications to Hypercorn's internal protocol interface,
16
handling the ASGI application lifecycle and providing the necessary
17
sync_spawn and call_soon capabilities for ASGI compatibility.
18
"""
19
20
def __init__(self, app: ASGIFramework):
21
"""
22
Initialize ASGI wrapper.
23
24
Args:
25
app: ASGI application callable that follows the ASGI spec
26
"""
27
28
async def __call__(
29
self,
30
scope: Scope,
31
receive: ASGIReceiveCallable,
32
send: ASGISendCallable,
33
sync_spawn: Callable,
34
call_soon: Callable
35
):
36
"""
37
Execute the ASGI application.
38
39
Args:
40
scope: ASGI scope dictionary containing request information
41
receive: ASGI receive callable for getting request data
42
send: ASGI send callable for sending response data
43
sync_spawn: Function to spawn synchronous tasks
44
call_soon: Function to schedule tasks for immediate execution
45
46
The wrapper handles the ASGI application lifecycle, managing
47
the connection between Hypercorn's event-driven architecture
48
and the ASGI application interface.
49
"""
50
```
51
52
### WSGI Application Wrapper
53
54
Wrapper for WSGI applications that adapts them to work within Hypercorn's async environment, handling the synchronous WSGI interface within an async context.
55
56
```python { .api }
57
class WSGIWrapper:
58
"""
59
Wrapper for WSGI applications.
60
61
Adapts WSGI applications to work within Hypercorn's async environment
62
by handling the synchronous WSGI interface and translating between
63
WSGI's request/response model and ASGI's event-driven interface.
64
"""
65
66
def __init__(self, app: WSGIFramework, max_body_size: int):
67
"""
68
Initialize WSGI wrapper.
69
70
Args:
71
app: WSGI application callable following WSGI spec
72
max_body_size: Maximum request body size in bytes
73
"""
74
75
async def __call__(
76
self,
77
scope: Scope,
78
receive: ASGIReceiveCallable,
79
send: ASGISendCallable,
80
sync_spawn: Callable,
81
call_soon: Callable
82
):
83
"""
84
Execute the WSGI application.
85
86
Args:
87
scope: ASGI scope dictionary (converted to WSGI environ)
88
receive: ASGI receive callable for getting request data
89
send: ASGI send callable for sending response data
90
sync_spawn: Function to spawn synchronous tasks (used for WSGI execution)
91
call_soon: Function to schedule tasks for immediate execution
92
93
The wrapper translates between ASGI's async event model and
94
WSGI's synchronous request/response model, handling:
95
- Environ dictionary creation from ASGI scope
96
- Request body streaming and buffering
97
- Response handling and chunked encoding
98
- Synchronous execution within async context
99
"""
100
```
101
102
### Application Loading Exceptions
103
104
Exceptions raised during application loading and wrapping processes.
105
106
```python { .api }
107
class InvalidPathError(Exception):
108
"""
109
Raised when an invalid application path is provided.
110
111
This exception occurs when the application path cannot be
112
resolved to a valid ASGI or WSGI application, such as:
113
- Module import failures
114
- Missing application attributes
115
- Invalid application callable signatures
116
"""
117
```
118
119
## Framework Type Definitions
120
121
Type definitions for the application frameworks supported by the wrappers.
122
123
```python { .api }
124
# ASGI application interface
125
ASGIFramework = Callable[[Scope, ASGIReceiveCallable, ASGISendCallable], Awaitable[None]]
126
127
# WSGI application interface
128
WSGIFramework = Callable[[dict, Callable], Iterable[bytes]]
129
130
# ASGI scope types
131
Scope = HTTPScope | WebsocketScope | LifespanScope
132
133
# ASGI callable types
134
ASGIReceiveCallable = Callable[[], Awaitable[ASGIReceiveEvent]]
135
ASGISendCallable = Callable[[ASGISendEvent], Awaitable[None]]
136
```
137
138
## Usage Examples
139
140
### Direct ASGI Wrapper Usage
141
142
```python
143
from hypercorn.app_wrappers import ASGIWrapper
144
145
async def asgi_app(scope, receive, send):
146
"""Simple ASGI application"""
147
if scope['type'] == 'http':
148
await send({
149
'type': 'http.response.start',
150
'status': 200,
151
'headers': [[b'content-type', b'text/plain']],
152
})
153
await send({
154
'type': 'http.response.body',
155
'body': b'Hello from ASGI',
156
})
157
158
# Wrap the ASGI application
159
wrapper = ASGIWrapper(asgi_app)
160
161
# The wrapper is now ready for use by Hypercorn's internal protocol handlers
162
```
163
164
### Direct WSGI Wrapper Usage
165
166
```python
167
from hypercorn.app_wrappers import WSGIWrapper
168
169
def wsgi_app(environ, start_response):
170
"""Simple WSGI application"""
171
status = '200 OK'
172
headers = [('Content-Type', 'text/plain')]
173
start_response(status, headers)
174
return [b'Hello from WSGI']
175
176
# Wrap the WSGI application with body size limit
177
max_body_size = 16 * 1024 * 1024 # 16MB
178
wrapper = WSGIWrapper(wsgi_app, max_body_size)
179
180
# The wrapper is now ready for use by Hypercorn's internal protocol handlers
181
```
182
183
### Framework Detection and Wrapping
184
185
The wrappers are typically used internally by Hypercorn, but can be used directly for custom server implementations:
186
187
```python
188
from hypercorn.app_wrappers import ASGIWrapper, WSGIWrapper
189
from hypercorn.utils import is_asgi
190
191
def wrap_application(app, max_body_size=16*1024*1024):
192
"""Wrap application based on its type"""
193
if is_asgi(app):
194
return ASGIWrapper(app)
195
else:
196
return WSGIWrapper(app, max_body_size)
197
198
# Usage
199
wrapped_app = wrap_application(my_app)
200
```
201
202
### Custom Application Wrapper
203
204
For advanced use cases, you can create custom wrappers following the same interface:
205
206
```python
207
class CustomWrapper:
208
def __init__(self, app):
209
self.app = app
210
211
async def __call__(self, scope, receive, send, sync_spawn, call_soon):
212
# Custom application adaptation logic
213
if scope['type'] == 'http':
214
# Handle HTTP requests
215
await self.handle_http(scope, receive, send)
216
elif scope['type'] == 'websocket':
217
# Handle WebSocket connections
218
await self.handle_websocket(scope, receive, send)
219
220
async def handle_http(self, scope, receive, send):
221
# Custom HTTP handling
222
pass
223
224
async def handle_websocket(self, scope, receive, send):
225
# Custom WebSocket handling
226
pass
227
```
228
229
### Error Handling
230
231
```python
232
from hypercorn.app_wrappers import InvalidPathError, WSGIWrapper
233
234
try:
235
wrapper = WSGIWrapper(invalid_app, 1024*1024)
236
except InvalidPathError as e:
237
print(f"Failed to wrap application: {e}")
238
# Handle invalid application path
239
```
240
241
The application wrappers provide the essential bridge between standard Python web frameworks (ASGI/WSGI) and Hypercorn's high-performance async server architecture, enabling seamless integration of existing applications with modern protocol support.