turboapi 0.4.15__cp314-cp314t-macosx_11_0_arm64.whl → 0.5.22__cp314-cp314t-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
turboapi/__init__.py CHANGED
@@ -1,24 +1,141 @@
1
1
  """
2
2
  TurboAPI - Revolutionary Python web framework
3
- Requires Python 3.13+ free-threading for maximum performance
3
+ FastAPI-compatible API with SIMD-accelerated Rust backend.
4
+ Requires Python 3.13+ free-threading for maximum performance.
4
5
  """
5
6
 
6
- # Check free-threading compatibility FIRST (before any other imports)
7
- from .models import TurboRequest, TurboResponse
8
- from .routing import APIRouter, Router
7
+ # Core application
9
8
  from .rust_integration import TurboAPI
10
- from .version_check import check_free_threading_support
9
+ from .routing import APIRouter, Router
10
+ from .models import TurboRequest, TurboResponse, Request
11
+
12
+ # Parameter types (FastAPI-compatible)
13
+ from .datastructures import (
14
+ Body,
15
+ Cookie,
16
+ File,
17
+ Form,
18
+ Header,
19
+ Path,
20
+ Query,
21
+ UploadFile,
22
+ )
23
+
24
+ # Response types
25
+ from .responses import (
26
+ FileResponse,
27
+ HTMLResponse,
28
+ JSONResponse,
29
+ PlainTextResponse,
30
+ RedirectResponse,
31
+ Response,
32
+ StreamingResponse,
33
+ )
34
+
35
+ # Security
36
+ from .security import (
37
+ APIKeyCookie,
38
+ APIKeyHeader,
39
+ APIKeyQuery,
40
+ Depends,
41
+ HTTPBasic,
42
+ HTTPBasicCredentials,
43
+ HTTPBearer,
44
+ HTTPException,
45
+ OAuth2AuthorizationCodeBearer,
46
+ OAuth2PasswordBearer,
47
+ Security,
48
+ SecurityScopes,
49
+ )
50
+
51
+ # Exceptions
52
+ from .exceptions import (
53
+ RequestValidationError,
54
+ WebSocketException,
55
+ )
56
+
57
+ # Middleware
58
+ from .middleware import (
59
+ CORSMiddleware,
60
+ GZipMiddleware,
61
+ HTTPSRedirectMiddleware,
62
+ Middleware,
63
+ TrustedHostMiddleware,
64
+ )
65
+
66
+ # Background tasks
67
+ from .background import BackgroundTasks
68
+
69
+ # WebSocket
70
+ from .websockets import WebSocket, WebSocketDisconnect
71
+
72
+ # Encoders
73
+ from .encoders import jsonable_encoder
74
+
75
+ # Status codes module (import as 'status')
76
+ from . import status
77
+
78
+ # Version check
79
+ from .version_check import check_free_threading_support, get_python_threading_info
11
80
 
12
81
  __version__ = "2.0.0"
13
82
  __all__ = [
83
+ # Core
14
84
  "TurboAPI",
15
85
  "APIRouter",
16
86
  "Router",
17
87
  "TurboRequest",
18
88
  "TurboResponse",
89
+ "Request",
90
+ # Parameters
91
+ "Body",
92
+ "Cookie",
93
+ "File",
94
+ "Form",
95
+ "Header",
96
+ "Path",
97
+ "Query",
98
+ "UploadFile",
99
+ # Responses
100
+ "FileResponse",
101
+ "HTMLResponse",
102
+ "JSONResponse",
103
+ "PlainTextResponse",
104
+ "RedirectResponse",
105
+ "Response",
106
+ "StreamingResponse",
107
+ # Security
108
+ "APIKeyCookie",
109
+ "APIKeyHeader",
110
+ "APIKeyQuery",
111
+ "Depends",
112
+ "HTTPBasic",
113
+ "HTTPBasicCredentials",
114
+ "HTTPBearer",
115
+ "HTTPException",
116
+ "OAuth2AuthorizationCodeBearer",
117
+ "OAuth2PasswordBearer",
118
+ "Security",
119
+ "SecurityScopes",
120
+ # Exceptions
121
+ "RequestValidationError",
122
+ "WebSocketException",
123
+ # Middleware
124
+ "CORSMiddleware",
125
+ "GZipMiddleware",
126
+ "HTTPSRedirectMiddleware",
127
+ "Middleware",
128
+ "TrustedHostMiddleware",
129
+ # Background tasks
130
+ "BackgroundTasks",
131
+ # WebSocket
132
+ "WebSocket",
133
+ "WebSocketDisconnect",
134
+ # Encoders
135
+ "jsonable_encoder",
136
+ # Status module
137
+ "status",
138
+ # Utils
19
139
  "check_free_threading_support",
20
140
  "get_python_threading_info",
21
141
  ]
22
-
23
- # Additional exports for free-threading diagnostics
24
- from .version_check import get_python_threading_info
turboapi/background.py ADDED
@@ -0,0 +1,51 @@
1
+ """Background tasks support for TurboAPI.
2
+
3
+ FastAPI-compatible BackgroundTasks class that runs functions after the response is sent.
4
+ """
5
+
6
+ import asyncio
7
+ import inspect
8
+ from typing import Any, Callable
9
+
10
+
11
+ class BackgroundTasks:
12
+ """A collection of background tasks to run after the response is sent.
13
+
14
+ Usage:
15
+ @app.post("/send-notification")
16
+ async def send_notification(background_tasks: BackgroundTasks):
17
+ background_tasks.add_task(send_email, "user@example.com", message="Hello")
18
+ return {"message": "Notification sent in the background"}
19
+ """
20
+
21
+ def __init__(self):
22
+ self._tasks: list[tuple[Callable, tuple, dict]] = []
23
+
24
+ @property
25
+ def tasks(self) -> list[tuple[Callable, tuple, dict]]:
26
+ """Return the list of tasks (FastAPI compatibility)."""
27
+ return self._tasks
28
+
29
+ def add_task(self, func: Callable, *args: Any, **kwargs: Any) -> None:
30
+ """Add a task to be run in the background after the response is sent."""
31
+ self._tasks.append((func, args, kwargs))
32
+
33
+ async def __call__(self) -> None:
34
+ """Execute all background tasks."""
35
+ for func, args, kwargs in self._tasks:
36
+ if inspect.iscoroutinefunction(func):
37
+ await func(*args, **kwargs)
38
+ else:
39
+ func(*args, **kwargs)
40
+
41
+ def run_tasks(self) -> None:
42
+ """Run all tasks synchronously or in an event loop."""
43
+ for func, args, kwargs in self._tasks:
44
+ if inspect.iscoroutinefunction(func):
45
+ try:
46
+ loop = asyncio.get_running_loop()
47
+ loop.create_task(func(*args, **kwargs))
48
+ except RuntimeError:
49
+ asyncio.run(func(*args, **kwargs))
50
+ else:
51
+ func(*args, **kwargs)
@@ -0,0 +1,262 @@
1
+ """Data structures for TurboAPI - Form, File, UploadFile.
2
+
3
+ FastAPI-compatible parameter markers and file handling classes.
4
+ """
5
+
6
+ import io
7
+ import tempfile
8
+ from typing import Any, Optional
9
+
10
+
11
+ class Form:
12
+ """Marker class for form data parameters.
13
+
14
+ Usage:
15
+ @app.post("/login")
16
+ async def login(username: str = Form(), password: str = Form()):
17
+ return {"username": username}
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ default: Any = ...,
23
+ *,
24
+ alias: Optional[str] = None,
25
+ title: Optional[str] = None,
26
+ description: Optional[str] = None,
27
+ min_length: Optional[int] = None,
28
+ max_length: Optional[int] = None,
29
+ regex: Optional[str] = None,
30
+ media_type: str = "application/x-www-form-urlencoded",
31
+ ):
32
+ self.default = default
33
+ self.alias = alias
34
+ self.title = title
35
+ self.description = description
36
+ self.min_length = min_length
37
+ self.max_length = max_length
38
+ self.regex = regex
39
+ self.media_type = media_type
40
+
41
+
42
+ class File:
43
+ """Marker class for file upload parameters.
44
+
45
+ Usage:
46
+ @app.post("/upload")
47
+ async def upload(file: bytes = File()):
48
+ return {"file_size": len(file)}
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ default: Any = ...,
54
+ *,
55
+ alias: Optional[str] = None,
56
+ title: Optional[str] = None,
57
+ description: Optional[str] = None,
58
+ max_length: Optional[int] = None,
59
+ media_type: str = "multipart/form-data",
60
+ ):
61
+ self.default = default
62
+ self.alias = alias
63
+ self.title = title
64
+ self.description = description
65
+ self.max_length = max_length
66
+ self.media_type = media_type
67
+
68
+
69
+ class UploadFile:
70
+ """Represents an uploaded file.
71
+
72
+ Usage:
73
+ @app.post("/upload")
74
+ async def upload(file: UploadFile):
75
+ contents = await file.read()
76
+ return {"filename": file.filename, "size": len(contents)}
77
+ """
78
+
79
+ def __init__(
80
+ self,
81
+ filename: Optional[str] = None,
82
+ file: Optional[io.IOBase] = None,
83
+ content_type: str = "application/octet-stream",
84
+ *,
85
+ size: Optional[int] = None,
86
+ headers: Optional[dict] = None,
87
+ ):
88
+ self.filename = filename
89
+ self.content_type = content_type
90
+ self.size = size
91
+ self.headers = headers or {}
92
+ if file is None:
93
+ self.file = tempfile.SpooledTemporaryFile(max_size=1024 * 1024)
94
+ else:
95
+ self.file = file
96
+
97
+ async def read(self, size: int = -1) -> bytes:
98
+ """Read file contents."""
99
+ if hasattr(self.file, "read"):
100
+ return self.file.read(size)
101
+ return b""
102
+
103
+ async def write(self, data: bytes) -> None:
104
+ """Write data to the file."""
105
+ if hasattr(self.file, "write"):
106
+ self.file.write(data)
107
+
108
+ async def seek(self, offset: int) -> None:
109
+ """Seek to a position in the file."""
110
+ if hasattr(self.file, "seek"):
111
+ self.file.seek(offset)
112
+
113
+ async def close(self) -> None:
114
+ """Close the file."""
115
+ if hasattr(self.file, "close"):
116
+ self.file.close()
117
+
118
+ def __repr__(self) -> str:
119
+ return f"UploadFile(filename={self.filename!r}, content_type={self.content_type!r}, size={self.size})"
120
+
121
+
122
+ class Header:
123
+ """Marker class for header parameters.
124
+
125
+ Usage:
126
+ @app.get("/items")
127
+ async def read_items(x_token: str = Header()):
128
+ return {"X-Token": x_token}
129
+ """
130
+
131
+ def __init__(
132
+ self,
133
+ default: Any = ...,
134
+ *,
135
+ alias: Optional[str] = None,
136
+ title: Optional[str] = None,
137
+ description: Optional[str] = None,
138
+ convert_underscores: bool = True,
139
+ ):
140
+ self.default = default
141
+ self.alias = alias
142
+ self.title = title
143
+ self.description = description
144
+ self.convert_underscores = convert_underscores
145
+
146
+
147
+ class Cookie:
148
+ """Marker class for cookie parameters.
149
+
150
+ Usage:
151
+ @app.get("/items")
152
+ async def read_items(session_id: str = Cookie()):
153
+ return {"session_id": session_id}
154
+ """
155
+
156
+ def __init__(
157
+ self,
158
+ default: Any = ...,
159
+ *,
160
+ alias: Optional[str] = None,
161
+ title: Optional[str] = None,
162
+ description: Optional[str] = None,
163
+ ):
164
+ self.default = default
165
+ self.alias = alias
166
+ self.title = title
167
+ self.description = description
168
+
169
+
170
+ class Query:
171
+ """Marker class for query parameters with validation.
172
+
173
+ Usage:
174
+ @app.get("/items")
175
+ async def read_items(q: str = Query(min_length=3)):
176
+ return {"q": q}
177
+ """
178
+
179
+ def __init__(
180
+ self,
181
+ default: Any = ...,
182
+ *,
183
+ alias: Optional[str] = None,
184
+ title: Optional[str] = None,
185
+ description: Optional[str] = None,
186
+ min_length: Optional[int] = None,
187
+ max_length: Optional[int] = None,
188
+ regex: Optional[str] = None,
189
+ gt: Optional[float] = None,
190
+ ge: Optional[float] = None,
191
+ lt: Optional[float] = None,
192
+ le: Optional[float] = None,
193
+ ):
194
+ self.default = default
195
+ self.alias = alias
196
+ self.title = title
197
+ self.description = description
198
+ self.min_length = min_length
199
+ self.max_length = max_length
200
+ self.regex = regex
201
+ self.gt = gt
202
+ self.ge = ge
203
+ self.lt = lt
204
+ self.le = le
205
+
206
+
207
+ class Path:
208
+ """Marker class for path parameters with validation.
209
+
210
+ Usage:
211
+ @app.get("/items/{item_id}")
212
+ async def read_item(item_id: int = Path(gt=0)):
213
+ return {"item_id": item_id}
214
+ """
215
+
216
+ def __init__(
217
+ self,
218
+ default: Any = ...,
219
+ *,
220
+ alias: Optional[str] = None,
221
+ title: Optional[str] = None,
222
+ description: Optional[str] = None,
223
+ gt: Optional[float] = None,
224
+ ge: Optional[float] = None,
225
+ lt: Optional[float] = None,
226
+ le: Optional[float] = None,
227
+ ):
228
+ self.default = default
229
+ self.alias = alias
230
+ self.title = title
231
+ self.description = description
232
+ self.gt = gt
233
+ self.ge = ge
234
+ self.lt = lt
235
+ self.le = le
236
+
237
+
238
+ class Body:
239
+ """Marker class for body parameters.
240
+
241
+ Usage:
242
+ @app.post("/items")
243
+ async def create_item(name: str = Body(), price: float = Body()):
244
+ return {"name": name, "price": price}
245
+ """
246
+
247
+ def __init__(
248
+ self,
249
+ default: Any = ...,
250
+ *,
251
+ embed: bool = False,
252
+ alias: Optional[str] = None,
253
+ title: Optional[str] = None,
254
+ description: Optional[str] = None,
255
+ media_type: str = "application/json",
256
+ ):
257
+ self.default = default
258
+ self.embed = embed
259
+ self.alias = alias
260
+ self.title = title
261
+ self.description = description
262
+ self.media_type = media_type