robyn 0.73.0__cp311-cp311-macosx_10_12_x86_64.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.

Potentially problematic release.


This version of robyn might be problematic. Click here for more details.

Files changed (57) hide show
  1. robyn/__init__.py +757 -0
  2. robyn/__main__.py +4 -0
  3. robyn/ai.py +308 -0
  4. robyn/argument_parser.py +129 -0
  5. robyn/authentication.py +96 -0
  6. robyn/cli.py +136 -0
  7. robyn/dependency_injection.py +71 -0
  8. robyn/env_populator.py +35 -0
  9. robyn/events.py +6 -0
  10. robyn/exceptions.py +32 -0
  11. robyn/jsonify.py +13 -0
  12. robyn/logger.py +80 -0
  13. robyn/mcp.py +461 -0
  14. robyn/openapi.py +448 -0
  15. robyn/processpool.py +226 -0
  16. robyn/py.typed +0 -0
  17. robyn/reloader.py +164 -0
  18. robyn/responses.py +208 -0
  19. robyn/robyn.cpython-311-darwin.so +0 -0
  20. robyn/robyn.pyi +421 -0
  21. robyn/router.py +410 -0
  22. robyn/scaffold/mongo/Dockerfile +12 -0
  23. robyn/scaffold/mongo/app.py +43 -0
  24. robyn/scaffold/mongo/requirements.txt +2 -0
  25. robyn/scaffold/no-db/Dockerfile +12 -0
  26. robyn/scaffold/no-db/app.py +12 -0
  27. robyn/scaffold/no-db/requirements.txt +1 -0
  28. robyn/scaffold/postgres/Dockerfile +32 -0
  29. robyn/scaffold/postgres/app.py +31 -0
  30. robyn/scaffold/postgres/requirements.txt +3 -0
  31. robyn/scaffold/postgres/supervisord.conf +14 -0
  32. robyn/scaffold/prisma/Dockerfile +15 -0
  33. robyn/scaffold/prisma/app.py +32 -0
  34. robyn/scaffold/prisma/requirements.txt +2 -0
  35. robyn/scaffold/prisma/schema.prisma +13 -0
  36. robyn/scaffold/sqlalchemy/Dockerfile +12 -0
  37. robyn/scaffold/sqlalchemy/__init__.py +0 -0
  38. robyn/scaffold/sqlalchemy/app.py +13 -0
  39. robyn/scaffold/sqlalchemy/models.py +21 -0
  40. robyn/scaffold/sqlalchemy/requirements.txt +2 -0
  41. robyn/scaffold/sqlite/Dockerfile +12 -0
  42. robyn/scaffold/sqlite/app.py +22 -0
  43. robyn/scaffold/sqlite/requirements.txt +1 -0
  44. robyn/scaffold/sqlmodel/Dockerfile +11 -0
  45. robyn/scaffold/sqlmodel/app.py +46 -0
  46. robyn/scaffold/sqlmodel/models.py +10 -0
  47. robyn/scaffold/sqlmodel/requirements.txt +2 -0
  48. robyn/status_codes.py +137 -0
  49. robyn/swagger.html +32 -0
  50. robyn/templating.py +30 -0
  51. robyn/types.py +44 -0
  52. robyn/ws.py +67 -0
  53. robyn-0.73.0.dist-info/METADATA +32 -0
  54. robyn-0.73.0.dist-info/RECORD +57 -0
  55. robyn-0.73.0.dist-info/WHEEL +4 -0
  56. robyn-0.73.0.dist-info/entry_points.txt +3 -0
  57. robyn-0.73.0.dist-info/licenses/LICENSE +25 -0
robyn/router.py ADDED
@@ -0,0 +1,410 @@
1
+ import inspect
2
+ import logging
3
+ from abc import ABC, abstractmethod
4
+ from functools import wraps
5
+ from types import CoroutineType
6
+ from typing import Callable, Dict, List, NamedTuple, Optional, Union
7
+
8
+ from robyn import status_codes
9
+ from robyn.authentication import AuthenticationHandler, AuthenticationNotConfiguredError
10
+ from robyn.dependency_injection import DependencyMap
11
+ from robyn.jsonify import jsonify
12
+ from robyn.openapi import OpenAPI
13
+ from robyn.responses import FileResponse, StreamingResponse
14
+ from robyn.robyn import FunctionInfo, Headers, HttpMethod, Identity, MiddlewareType, QueryParams, Request, Response, Url
15
+ from robyn.types import Body, Files, FormData, IPAddress, Method, PathParams
16
+ from robyn.ws import WebSocket
17
+
18
+ _logger = logging.getLogger(__name__)
19
+
20
+
21
+ def lower_http_method(method: HttpMethod):
22
+ return (str(method))[11:].lower()
23
+
24
+
25
+ class Route(NamedTuple):
26
+ route_type: HttpMethod
27
+ route: str
28
+ function: FunctionInfo
29
+ is_const: bool
30
+ auth_required: bool
31
+ openapi_name: str
32
+ openapi_tags: List[str]
33
+
34
+
35
+ class RouteMiddleware(NamedTuple):
36
+ middleware_type: MiddlewareType
37
+ route: str
38
+ function: FunctionInfo
39
+ route_type: HttpMethod
40
+
41
+
42
+ class GlobalMiddleware(NamedTuple):
43
+ middleware_type: MiddlewareType
44
+ function: FunctionInfo
45
+
46
+
47
+ class BaseRouter(ABC):
48
+ @abstractmethod
49
+ def add_route(*args) -> Union[Callable, CoroutineType, WebSocket]: ...
50
+
51
+
52
+ class Router(BaseRouter):
53
+ def __init__(self) -> None:
54
+ super().__init__()
55
+ self.routes: List[Route] = []
56
+
57
+ def _format_tuple_response(self, res: tuple) -> Response:
58
+ if len(res) != 3:
59
+ raise ValueError("Tuple should have 3 elements")
60
+
61
+ description, headers, status_code = res
62
+ description = self._format_response(description).description
63
+ new_headers: Headers = Headers(headers)
64
+ if new_headers.contains("Content-Type"):
65
+ headers.set("Content-Type", new_headers.get("Content-Type"))
66
+
67
+ return Response(
68
+ status_code=status_code,
69
+ headers=headers,
70
+ description=description,
71
+ )
72
+
73
+ def _format_response(
74
+ self,
75
+ res: Union[Dict, Response, StreamingResponse, bytes, tuple, str],
76
+ ) -> Union[Response, StreamingResponse]:
77
+ if isinstance(res, Response):
78
+ return res
79
+
80
+ if isinstance(res, StreamingResponse):
81
+ return res
82
+
83
+ if isinstance(res, dict):
84
+ return Response(
85
+ status_code=status_codes.HTTP_200_OK,
86
+ headers=Headers({"Content-Type": "application/json"}),
87
+ description=jsonify(res),
88
+ )
89
+
90
+ if isinstance(res, FileResponse):
91
+ response: Response = Response(
92
+ status_code=res.status_code,
93
+ headers=res.headers,
94
+ description=res.file_path,
95
+ )
96
+ response.file_path = res.file_path
97
+ return response
98
+
99
+ if isinstance(res, bytes):
100
+ return Response(
101
+ status_code=status_codes.HTTP_200_OK,
102
+ headers=Headers({"Content-Type": "application/octet-stream"}),
103
+ description=res,
104
+ )
105
+
106
+ if isinstance(res, tuple):
107
+ return self._format_tuple_response(tuple(res))
108
+
109
+ return Response(
110
+ status_code=status_codes.HTTP_200_OK,
111
+ headers=Headers({"Content-Type": "text/plain"}),
112
+ description=str(res).encode("utf-8"),
113
+ )
114
+
115
+ def add_route( # type: ignore
116
+ self,
117
+ route_type: HttpMethod,
118
+ endpoint: str,
119
+ handler: Callable,
120
+ is_const: bool,
121
+ auth_required: bool,
122
+ openapi_name: str,
123
+ openapi_tags: List[str],
124
+ exception_handler: Optional[Callable],
125
+ injected_dependencies: dict,
126
+ ) -> Union[Callable, CoroutineType]:
127
+ def wrapped_handler(*args, **kwargs):
128
+ # In the execute functions the request is passed into *args
129
+ request = next(filter(lambda it: isinstance(it, Request), args), None)
130
+
131
+ handler_params = inspect.signature(handler).parameters
132
+
133
+ if not request or (len(handler_params) == 1 and next(iter(handler_params)) is Request):
134
+ return handler(*args, **kwargs)
135
+
136
+ type_mapping = {
137
+ "request": Request,
138
+ "query_params": QueryParams,
139
+ "headers": Headers,
140
+ "path_params": PathParams,
141
+ "body": Body,
142
+ "method": Method,
143
+ "url": Url,
144
+ "form_data": FormData,
145
+ "files": Files,
146
+ "ip_addr": IPAddress,
147
+ "identity": Identity,
148
+ }
149
+
150
+ type_filtered_params = {}
151
+
152
+ for handler_param in iter(handler_params):
153
+ for type_name in type_mapping:
154
+ handler_param_type = handler_params[handler_param].annotation
155
+ handler_param_name = handler_params[handler_param].name
156
+ if handler_param_type is Request:
157
+ type_filtered_params[handler_param_name] = request
158
+ elif handler_param_type is type_mapping[type_name]:
159
+ type_filtered_params[handler_param_name] = getattr(request, type_name)
160
+ elif inspect.isclass(handler_param_type):
161
+ if issubclass(handler_param_type, Body):
162
+ type_filtered_params[handler_param_name] = getattr(request, "body")
163
+ elif issubclass(handler_param_type, QueryParams):
164
+ type_filtered_params[handler_param_name] = getattr(request, "query_params")
165
+
166
+ request_components = {
167
+ "r": request,
168
+ "req": request,
169
+ "request": request,
170
+ "query_params": request.query_params,
171
+ "headers": request.headers,
172
+ "path_params": request.path_params,
173
+ "body": request.body,
174
+ "method": request.method,
175
+ "url": request.url,
176
+ "ip_addr": request.ip_addr,
177
+ "identity": request.identity,
178
+ "form_data": request.form_data,
179
+ "files": request.files,
180
+ "router_dependencies": injected_dependencies["router_dependencies"],
181
+ "global_dependencies": injected_dependencies["global_dependencies"],
182
+ **kwargs,
183
+ }
184
+
185
+ name_filtered_params = {k: v for k, v in request_components.items() if k in handler_params and k not in type_filtered_params}
186
+
187
+ filtered_params = dict(**type_filtered_params, **name_filtered_params)
188
+
189
+ if len(filtered_params) != len(handler_params):
190
+ invalid_args = set(handler_params) - set(filtered_params)
191
+ raise SyntaxError(f"Unexpected request params found: {invalid_args}")
192
+
193
+ return handler(**filtered_params)
194
+
195
+ @wraps(handler)
196
+ async def async_inner_handler(*args, **kwargs):
197
+ try:
198
+ response = self._format_response(
199
+ await wrapped_handler(*args, **kwargs),
200
+ )
201
+ except Exception as err:
202
+ if exception_handler is None:
203
+ raise
204
+ response = self._format_response(
205
+ exception_handler(err),
206
+ )
207
+ return response
208
+
209
+ @wraps(handler)
210
+ def inner_handler(*args, **kwargs):
211
+ try:
212
+ response = self._format_response(
213
+ wrapped_handler(*args, **kwargs),
214
+ )
215
+ except Exception as err:
216
+ if exception_handler is None:
217
+ raise
218
+ response = self._format_response(
219
+ exception_handler(err),
220
+ )
221
+ return response
222
+
223
+ # these are the arguments
224
+ params = dict(inspect.signature(handler).parameters)
225
+
226
+ new_injected_dependencies = {}
227
+ for dependency in injected_dependencies:
228
+ if dependency in params:
229
+ new_injected_dependencies[dependency] = injected_dependencies[dependency]
230
+ else:
231
+ _logger.debug(f"Dependency {dependency} is not used in the handler {handler.__name__}")
232
+
233
+ if inspect.iscoroutinefunction(handler):
234
+ function = FunctionInfo(
235
+ async_inner_handler,
236
+ True,
237
+ len(params),
238
+ params,
239
+ new_injected_dependencies,
240
+ )
241
+ self.routes.append(Route(route_type, endpoint, function, is_const, auth_required, openapi_name, openapi_tags))
242
+ return async_inner_handler
243
+ else:
244
+ function = FunctionInfo(
245
+ inner_handler,
246
+ False,
247
+ len(params),
248
+ params,
249
+ new_injected_dependencies,
250
+ )
251
+ self.routes.append(Route(route_type, endpoint, function, is_const, auth_required, openapi_name, openapi_tags))
252
+ return inner_handler
253
+
254
+ def prepare_routes_openapi(self, openapi: OpenAPI, included_routers: List) -> None:
255
+ for route in self.routes:
256
+ openapi.add_openapi_path_obj(lower_http_method(route.route_type), route.route, route.openapi_name, route.openapi_tags, route.function.handler)
257
+
258
+ # TODO! after include_routes does not immediately merge all the routes
259
+ # for router in included_routers:
260
+ # for route in router:
261
+ # openapi.add_openapi_path_obj(lower_http_method(route.route_type), route.route, route.openapi_name, route.openapi_tags, route.function.handler)
262
+
263
+ def get_routes(self) -> List[Route]:
264
+ return self.routes
265
+
266
+
267
+ class MiddlewareRouter(BaseRouter):
268
+ def __init__(self, dependencies: DependencyMap = DependencyMap()) -> None:
269
+ super().__init__()
270
+ self.global_middlewares: List[GlobalMiddleware] = []
271
+ self.route_middlewares: List[RouteMiddleware] = []
272
+ self.authentication_handler: Optional[AuthenticationHandler] = None
273
+ self.dependencies = dependencies
274
+
275
+ def set_authentication_handler(self, authentication_handler: AuthenticationHandler):
276
+ self.authentication_handler = authentication_handler
277
+
278
+ def add_route( # type: ignore
279
+ self,
280
+ middleware_type: MiddlewareType,
281
+ endpoint: str,
282
+ route_type: HttpMethod,
283
+ handler: Callable,
284
+ injected_dependencies: dict,
285
+ ) -> Callable:
286
+ params = dict(inspect.signature(handler).parameters)
287
+
288
+ new_injected_dependencies = {}
289
+ for dependency in injected_dependencies:
290
+ if dependency in params:
291
+ new_injected_dependencies[dependency] = injected_dependencies[dependency]
292
+ else:
293
+ _logger.debug(f"Dependency {dependency} is not used in the middleware handler {handler.__name__}")
294
+
295
+ function = FunctionInfo(
296
+ handler,
297
+ inspect.iscoroutinefunction(handler),
298
+ len(params),
299
+ params,
300
+ new_injected_dependencies,
301
+ )
302
+ self.route_middlewares.append(RouteMiddleware(middleware_type, endpoint, function, route_type))
303
+ return handler
304
+
305
+ def add_auth_middleware(self, endpoint: str, route_type: HttpMethod):
306
+ """
307
+ This method adds an authentication middleware to the specified endpoint.
308
+ """
309
+
310
+ injected_dependencies: dict = {}
311
+
312
+ def decorator(handler):
313
+ @wraps(handler)
314
+ def inner_handler(request: Request, *args):
315
+ if not self.authentication_handler:
316
+ raise AuthenticationNotConfiguredError()
317
+ identity = self.authentication_handler.authenticate(request)
318
+ if identity is None:
319
+ return self.authentication_handler.unauthorized_response
320
+ request.identity = identity
321
+
322
+ return request
323
+
324
+ self.add_route(
325
+ MiddlewareType.BEFORE_REQUEST,
326
+ endpoint,
327
+ route_type,
328
+ inner_handler,
329
+ injected_dependencies,
330
+ )
331
+ return inner_handler
332
+
333
+ return decorator
334
+
335
+ # These inner functions are basically a wrapper around the closure(decorator) being returned.
336
+ # They take a handler, convert it into a closure and return the arguments.
337
+ # Arguments are returned as they could be modified by the middlewares.
338
+ def add_middleware(self, middleware_type: MiddlewareType, endpoint: Optional[str]) -> Callable[..., None]:
339
+ # no dependency injection here
340
+ injected_dependencies: dict = {}
341
+
342
+ def inner(handler):
343
+ @wraps(handler)
344
+ async def async_inner_handler(*args, **kwargs):
345
+ return await handler(*args, **kwargs)
346
+
347
+ @wraps(handler)
348
+ def inner_handler(*args, **kwargs):
349
+ return handler(*args, **kwargs)
350
+
351
+ if endpoint is not None:
352
+ if inspect.iscoroutinefunction(handler):
353
+ self.add_route(
354
+ middleware_type,
355
+ endpoint,
356
+ HttpMethod.GET,
357
+ async_inner_handler,
358
+ injected_dependencies,
359
+ )
360
+ else:
361
+ self.add_route(middleware_type, endpoint, HttpMethod.GET, inner_handler, injected_dependencies)
362
+ else:
363
+ params = dict(inspect.signature(handler).parameters)
364
+
365
+ if inspect.iscoroutinefunction(handler):
366
+ self.global_middlewares.append(
367
+ GlobalMiddleware(
368
+ middleware_type,
369
+ FunctionInfo(
370
+ async_inner_handler,
371
+ True,
372
+ len(params),
373
+ params,
374
+ injected_dependencies,
375
+ ),
376
+ )
377
+ )
378
+ else:
379
+ self.global_middlewares.append(
380
+ GlobalMiddleware(
381
+ middleware_type,
382
+ FunctionInfo(
383
+ inner_handler,
384
+ False,
385
+ len(params),
386
+ params,
387
+ injected_dependencies,
388
+ ),
389
+ )
390
+ )
391
+
392
+ return inner
393
+
394
+ def get_route_middlewares(self) -> List[RouteMiddleware]:
395
+ return self.route_middlewares
396
+
397
+ def get_global_middlewares(self) -> List[GlobalMiddleware]:
398
+ return self.global_middlewares
399
+
400
+
401
+ class WebSocketRouter(BaseRouter):
402
+ def __init__(self) -> None:
403
+ super().__init__()
404
+ self.routes: dict = {}
405
+
406
+ def add_route(self, endpoint: str, web_socket: WebSocket) -> None: # type: ignore
407
+ self.routes[endpoint] = web_socket
408
+
409
+ def get_routes(self) -> Dict[str, WebSocket]:
410
+ return self.routes
@@ -0,0 +1,12 @@
1
+ FROM python:3.11-bookworm
2
+
3
+ WORKDIR /workspace
4
+
5
+ COPY . .
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
8
+
9
+
10
+ EXPOSE 8080
11
+
12
+ CMD ["python3", "app.py", "--log-level=DEBUG"]
@@ -0,0 +1,43 @@
1
+ from pymongo import MongoClient
2
+
3
+ from robyn import Robyn
4
+
5
+ app = Robyn(__file__)
6
+ db = MongoClient("URL HERE")
7
+
8
+ users = db.users # define a collection
9
+
10
+
11
+ @app.get("/")
12
+ def index():
13
+ return "Hello World!"
14
+
15
+
16
+ # create a route
17
+ @app.get("/users")
18
+ async def get_users():
19
+ all_users = await users.find().to_list(length=None)
20
+ return {"users": all_users}
21
+
22
+
23
+ # create a route to add a new user
24
+ @app.post("/users")
25
+ async def add_user(request):
26
+ user_data = await request.json()
27
+ result = await users.insert_one(user_data)
28
+ return {"success": True, "inserted_id": str(result.inserted_id)}
29
+
30
+
31
+ # create a route to fetch a single user by ID
32
+ @app.get("/users/{user_id}")
33
+ async def get_user(request):
34
+ user_id = request.path_params["user_id"]
35
+ user = await users.find_one({"_id": user_id})
36
+ if user:
37
+ return user
38
+ else:
39
+ return {"error": "User not found"}, 404
40
+
41
+
42
+ if __name__ == "__main__":
43
+ app.start(host="0.0.0.0", port=8080)
@@ -0,0 +1,2 @@
1
+ robyn
2
+ pymongo
@@ -0,0 +1,12 @@
1
+ FROM python:3.11-bookworm
2
+
3
+ WORKDIR /workspace
4
+
5
+ COPY . .
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
8
+
9
+
10
+ EXPOSE 8080
11
+
12
+ CMD ["python3", "app.py", "--log-level=DEBUG"]
@@ -0,0 +1,12 @@
1
+ from robyn import Robyn
2
+
3
+ app = Robyn(__file__)
4
+
5
+
6
+ @app.get("/")
7
+ def index():
8
+ return "Hello World!"
9
+
10
+
11
+ if __name__ == "__main__":
12
+ app.start(host="0.0.0.0", port=8080)
@@ -0,0 +1 @@
1
+ robyn
@@ -0,0 +1,32 @@
1
+ # ---- Build the PostgreSQL Base ----
2
+ FROM postgres:latest AS postgres-base
3
+
4
+ ENV POSTGRES_USER=postgres
5
+ ENV POSTGRES_PASSWORD=password
6
+ ENV POSTGRES_DB=postgresDB
7
+
8
+ # ---- Build the Python App ----
9
+ FROM python:3.11-bookworm
10
+
11
+ # Install supervisor
12
+ RUN apt-get update && apt-get install -y supervisor
13
+
14
+ WORKDIR /workspace
15
+
16
+ COPY . .
17
+
18
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
19
+
20
+
21
+ # Copy PostgreSQL binaries from the first stage
22
+ COPY --from=postgres-base /usr/local/bin /usr/local/bin
23
+ COPY --from=postgres-base /usr/lib/postgresql /usr/lib/postgresql
24
+ COPY --from=postgres-base /usr/share/postgresql /usr/share/postgresql
25
+ COPY --from=postgres-base /var/lib/postgresql /var/lib/postgresql
26
+
27
+ # Add supervisord config
28
+ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
29
+
30
+ EXPOSE 8080 5455
31
+
32
+ CMD ["/usr/bin/supervisord"]
@@ -0,0 +1,31 @@
1
+ import psycopg2
2
+
3
+ from robyn import Robyn
4
+
5
+ DB_NAME = "postgresDB"
6
+ DB_HOST = "localhost"
7
+ DB_USER = "postgres"
8
+ DB_PASS = "password"
9
+ DB_PORT = "5455"
10
+
11
+ conn = psycopg2.connect(database=DB_NAME, host=DB_HOST, user=DB_USER, password=DB_PASS, port=DB_PORT)
12
+
13
+ app = Robyn(__file__)
14
+
15
+
16
+ # create a route to fetch all users
17
+ @app.get("/users")
18
+ def get_users():
19
+ cursor = conn.cursor()
20
+ cursor.execute("SELECT * FROM users")
21
+ all_users = cursor.fetchall()
22
+ return {"users": all_users}
23
+
24
+
25
+ @app.get("/")
26
+ def index():
27
+ return "Hello World!"
28
+
29
+
30
+ if __name__ == "__main__":
31
+ app.start(host="0.0.0.0", port=8080)
@@ -0,0 +1,3 @@
1
+ robyn
2
+ psycopg2; platform_system=="Windows"
3
+ psycopg2-binary; platform_system!="Windows"
@@ -0,0 +1,14 @@
1
+ [supervisord]
2
+ nodaemon=true
3
+
4
+ [program:python-app]
5
+ command=python3 /workspace/app.py --log-level=DEBUG
6
+ autostart=true
7
+ autorestart=true
8
+ redirect_stderr=true
9
+
10
+ [program:postgres]
11
+ command=postgres -c 'config_file=/etc/postgresql/postgresql.conf'
12
+ autostart=true
13
+ autorestart=true
14
+ redirect_stderr=true
@@ -0,0 +1,15 @@
1
+ FROM python:3.11-bookworm
2
+
3
+ WORKDIR /workspace
4
+
5
+ COPY . .
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
8
+
9
+
10
+ RUN python3 -m prisma generate
11
+ RUN python3 -m prisma migrate dev --name init
12
+
13
+ EXPOSE 8080
14
+
15
+ CMD ["python3", "app.py", "--log-level=DEBUG"]
@@ -0,0 +1,32 @@
1
+ from prisma import Prisma
2
+ from prisma.models import User
3
+
4
+ from robyn import Robyn
5
+
6
+ app = Robyn(__file__)
7
+ prisma = Prisma(auto_register=True)
8
+
9
+
10
+ @app.startup_handler
11
+ async def startup_handler() -> None:
12
+ await prisma.connect()
13
+
14
+
15
+ @app.shutdown_handler
16
+ async def shutdown_handler() -> None:
17
+ if prisma.is_connected():
18
+ await prisma.disconnect()
19
+
20
+
21
+ @app.get("/")
22
+ async def h():
23
+ user = await User.prisma().create(
24
+ data={
25
+ "name": "Robert",
26
+ },
27
+ )
28
+ return user.json(indent=2)
29
+
30
+
31
+ if __name__ == "__main__":
32
+ app.start(host="0.0.0.0", port=8080)
@@ -0,0 +1,2 @@
1
+ robyn
2
+ prisma
@@ -0,0 +1,13 @@
1
+ datasource db {
2
+ provider = "sqlite"
3
+ url = "file:dev.db"
4
+ }
5
+
6
+ generator py {
7
+ provider = "prisma-client-py"
8
+ }
9
+
10
+ model User {
11
+ id String @id @default(cuid())
12
+ name String
13
+ }
@@ -0,0 +1,12 @@
1
+ FROM python:3.11-bookworm
2
+
3
+ WORKDIR /workspace
4
+
5
+ COPY . .
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
8
+
9
+
10
+ EXPOSE 8080
11
+
12
+ CMD ["python3", "app.py", "--log-level=DEBUG"]
File without changes
@@ -0,0 +1,13 @@
1
+ from robyn import Robyn
2
+
3
+ app = Robyn(__file__)
4
+
5
+
6
+ @app.get("/")
7
+ def index():
8
+ return "Hello World!"
9
+
10
+
11
+ if __name__ == "__main__":
12
+ # create a configured "Session" class
13
+ app.start(host="0.0.0.0", port=8080)