jac-scale 0.1.1__py3-none-any.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.
Files changed (57) hide show
  1. jac_scale/__init__.py +0 -0
  2. jac_scale/abstractions/config/app_config.jac +30 -0
  3. jac_scale/abstractions/config/base_config.jac +26 -0
  4. jac_scale/abstractions/database_provider.jac +51 -0
  5. jac_scale/abstractions/deployment_target.jac +64 -0
  6. jac_scale/abstractions/image_registry.jac +54 -0
  7. jac_scale/abstractions/logger.jac +20 -0
  8. jac_scale/abstractions/models/deployment_result.jac +27 -0
  9. jac_scale/abstractions/models/resource_status.jac +38 -0
  10. jac_scale/config_loader.jac +31 -0
  11. jac_scale/context.jac +14 -0
  12. jac_scale/factories/database_factory.jac +43 -0
  13. jac_scale/factories/deployment_factory.jac +43 -0
  14. jac_scale/factories/registry_factory.jac +32 -0
  15. jac_scale/factories/utility_factory.jac +34 -0
  16. jac_scale/impl/config_loader.impl.jac +131 -0
  17. jac_scale/impl/context.impl.jac +24 -0
  18. jac_scale/impl/memory_hierarchy.main.impl.jac +63 -0
  19. jac_scale/impl/memory_hierarchy.mongo.impl.jac +239 -0
  20. jac_scale/impl/memory_hierarchy.redis.impl.jac +186 -0
  21. jac_scale/impl/serve.impl.jac +1785 -0
  22. jac_scale/jserver/__init__.py +0 -0
  23. jac_scale/jserver/impl/jfast_api.impl.jac +731 -0
  24. jac_scale/jserver/impl/jserver.impl.jac +79 -0
  25. jac_scale/jserver/jfast_api.jac +162 -0
  26. jac_scale/jserver/jserver.jac +101 -0
  27. jac_scale/memory_hierarchy.jac +138 -0
  28. jac_scale/plugin.jac +218 -0
  29. jac_scale/plugin_config.jac +175 -0
  30. jac_scale/providers/database/kubernetes_mongo.jac +137 -0
  31. jac_scale/providers/database/kubernetes_redis.jac +110 -0
  32. jac_scale/providers/registry/dockerhub.jac +64 -0
  33. jac_scale/serve.jac +118 -0
  34. jac_scale/targets/kubernetes/kubernetes_config.jac +215 -0
  35. jac_scale/targets/kubernetes/kubernetes_target.jac +841 -0
  36. jac_scale/targets/kubernetes/utils/kubernetes_utils.impl.jac +519 -0
  37. jac_scale/targets/kubernetes/utils/kubernetes_utils.jac +85 -0
  38. jac_scale/tests/__init__.py +0 -0
  39. jac_scale/tests/conftest.py +29 -0
  40. jac_scale/tests/fixtures/test_api.jac +159 -0
  41. jac_scale/tests/fixtures/todo_app.jac +68 -0
  42. jac_scale/tests/test_abstractions.py +88 -0
  43. jac_scale/tests/test_deploy_k8s.py +265 -0
  44. jac_scale/tests/test_examples.py +484 -0
  45. jac_scale/tests/test_factories.py +149 -0
  46. jac_scale/tests/test_file_upload.py +444 -0
  47. jac_scale/tests/test_k8s_utils.py +156 -0
  48. jac_scale/tests/test_memory_hierarchy.py +247 -0
  49. jac_scale/tests/test_serve.py +1835 -0
  50. jac_scale/tests/test_sso.py +711 -0
  51. jac_scale/utilities/loggers/standard_logger.jac +40 -0
  52. jac_scale/utils.jac +16 -0
  53. jac_scale-0.1.1.dist-info/METADATA +658 -0
  54. jac_scale-0.1.1.dist-info/RECORD +57 -0
  55. jac_scale-0.1.1.dist-info/WHEEL +5 -0
  56. jac_scale-0.1.1.dist-info/entry_points.txt +3 -0
  57. jac_scale-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,731 @@
1
+ import logging;
2
+
3
+ """Helper function to convert TransportResponse to JSONResponse (similar to HTTPTransport.send)."""
4
+ def _convert_transport_response_to_json_response(
5
+ transport_response: TransportResponse
6
+ ) -> JSONResponse {
7
+ import json;
8
+ import from fastapi.responses { JSONResponse }
9
+ import from jaclang.runtimelib.transport { TransportResponse }
10
+ # Extract HTTP status from meta.extra or use defaults (same logic as HTTPTransport.send)
11
+ status = 200 if transport_response.ok else 500;
12
+ if transport_response.meta and transport_response.meta.extra {
13
+ status = transport_response.meta.extra.get('http_status', status);
14
+ }
15
+ # Build response body following TransportResponse envelope pattern (same as HTTPTransport.send)
16
+ response_body = {
17
+ 'ok': transport_response.ok,
18
+ 'type': transport_response.type,
19
+ 'data': transport_response.data,
20
+ 'error': None
21
+ };
22
+ # Serialize error if present
23
+ if not transport_response.ok and transport_response.error {
24
+ error_obj = {};
25
+ if getattr(transport_response.error, "code", None) {
26
+ error_obj['code'] = transport_response.error.code;
27
+ }
28
+ if getattr(transport_response.error, "message", None) {
29
+ error_obj['message'] = transport_response.error.message;
30
+ }
31
+ if getattr(transport_response.error, "details", None) is not None {
32
+ error_obj['details'] = transport_response.error.details;
33
+ }
34
+ response_body['error'] = error_obj;
35
+ }
36
+ # Include metadata if present
37
+ if transport_response.meta {
38
+ meta_dict = {};
39
+ if getattr(transport_response.meta, "request_id", None) {
40
+ meta_dict['request_id'] = transport_response.meta.request_id;
41
+ }
42
+ if getattr(transport_response.meta, "trace_id", None) {
43
+ meta_dict['trace_id'] = transport_response.meta.trace_id;
44
+ }
45
+ if getattr(transport_response.meta, "timestamp", None) {
46
+ meta_dict['timestamp'] = transport_response.meta.timestamp;
47
+ }
48
+ if getattr(transport_response.meta, "extra", None) {
49
+ meta_dict['extra'] = transport_response.meta.extra;
50
+ }
51
+ if meta_dict {
52
+ response_body['meta'] = meta_dict;
53
+ }
54
+ }
55
+ return JSONResponse(status_code=status, content=response_body);
56
+ }
57
+
58
+ """Custom logging handler that routes logs to jac-super console."""
59
+ class ConsoleLogHandler(logging.Handler) {
60
+ """A logging handler that forwards log messages to jac-super's console."""
61
+ def emit(self: ConsoleLogHandler, record: logging.LogRecord) -> None {
62
+ import from jaclang.cli.console { console }
63
+ message = self.format(record);
64
+ console.print(f" {message}");
65
+ }
66
+ }
67
+
68
+ """Run the FastAPI server using Uvicorn."""
69
+ impl JFastApiServer.run_server(
70
+ self: JFastApiServer, host: str = '0.0.0.0', port: int = 8000
71
+ ) -> None {
72
+ app = self.create_server();
73
+ # Create custom handler for console logging
74
+ console_handler = ConsoleLogHandler();
75
+ console_handler.setFormatter(logging.Formatter("%(message)s"));
76
+ # Suppress startup logs but route access logs through jac-super console
77
+ log_config = {
78
+ "version": 1,
79
+ "disable_existing_loggers": False,
80
+ "formatters": {
81
+ "default": {"format": "%(message)s"},
82
+ "access": {"format": "%(message)s"},
83
+
84
+ },
85
+ "handlers": {
86
+ "default": {
87
+ "formatter": "default",
88
+ "class": "logging.StreamHandler",
89
+ "stream": "ext://sys.stderr"
90
+ },
91
+ "access": {"()": lambda : console_handler },
92
+
93
+ },
94
+ "loggers": {
95
+ "uvicorn": {
96
+ "handlers": ["default"],
97
+ "level": "WARNING",
98
+ "propagate": False
99
+ },
100
+ "uvicorn.error": {"level": "WARNING", "propagate": False},
101
+ "uvicorn.access": {
102
+ "handlers": ["access"],
103
+ "level": "INFO",
104
+ "propagate": False
105
+ },
106
+
107
+ },
108
+
109
+ };
110
+ uvicorn.run(app, host=host, port=port, log_config=log_config);
111
+ }
112
+
113
+ """Get the underlying FastAPI application instance."""
114
+ impl JFastApiServer.get_app(self: JFastApiServer) -> FastAPI {
115
+ return self.app;
116
+ }
117
+
118
+ """Create a Pydantic response model from configuration."""
119
+ impl JFastApiServer._create_response_model(
120
+ self: JFastApiServer, response_config: (dict[(str, Any)] | None) = None
121
+ ) -> (type[BaseModel] | None) {
122
+ if not response_config {
123
+ return None;
124
+ }
125
+ model_name = response_config.get('name', 'ResponseModel');
126
+ fields = response_config.get('fields', {});
127
+ if not fields {
128
+ return None;
129
+ }
130
+ pydantic_fields: dict[(str, Any)] = {};
131
+ for (field_name, field_config) in fields.items() {
132
+ field_type = self._get_python_type(field_config.get('type', 'str'));
133
+ required = field_config.get('required', True);
134
+ description = field_config.get('description', '');
135
+ if required {
136
+ pydantic_fields[field_name] = (
137
+ field_type,
138
+ Field(..., description=description)
139
+ );
140
+ } else {
141
+ default_value = field_config.get('default');
142
+ pydantic_fields[field_name] = (
143
+ (field_type | None),
144
+ Field(default_value, description=description)
145
+ );
146
+ }
147
+ }
148
+ model = create_model(model_name, **pydantic_fields);
149
+ self._models[model_name] = model;
150
+ return model;
151
+ }
152
+
153
+ """Convert string type to Python type."""
154
+ impl JFastApiServer._get_python_type(
155
+ self: JFastApiServer, type_string: str
156
+ ) -> type[Any] {
157
+ if (type_string.startswith("<class '") and type_string.endswith("'>")) {
158
+ type_string = type_string[8:-2];
159
+ }
160
+ type_mapping: dict[(str, type[Any])] = {
161
+ 'str': str,
162
+ 'string': str,
163
+ 'int': int,
164
+ 'integer': int,
165
+ 'float': float,
166
+ 'number': float,
167
+ 'bool': bool,
168
+ 'boolean': bool,
169
+ 'list': <>list,
170
+ 'dict': <>dict,
171
+ 'object': <>dict
172
+ };
173
+ return type_mapping.get(type_string.lower(), str);
174
+ }
175
+
176
+ """Implementation of PATH parameter handler."""
177
+ impl PathParameterHandler.generate_param_string(param: APIParameter) -> str {
178
+ type_name = self.get_type_name(param);
179
+ description = param.description or '';
180
+ return f"{param.name}: {type_name} = Path(..., description='{description}')";
181
+ }
182
+
183
+ """Implementation of QUERY parameter handler."""
184
+ impl QueryParameterHandler.generate_param_string(param: APIParameter) -> str {
185
+ type_name = self.get_type_name(param);
186
+ description = param.description or '';
187
+ if param.required {
188
+ return f"{param.name}: {type_name} = Query(..., description='{description}')";
189
+ } elif (param.default is None) {
190
+ return f"{param.name}: Optional[{type_name}] = Query(description='{description}')";
191
+ } else {
192
+ return f"{param.name}: Optional[{type_name}] = Query({repr(param.default)}, description='{description}')";
193
+ }
194
+ }
195
+
196
+ """Implementation of HEADER parameter handler."""
197
+ impl HeaderParameterHandler.generate_param_string(param: APIParameter) -> str {
198
+ type_name = self.get_type_name(param);
199
+ description = param.description or '';
200
+ if param.required {
201
+ return f"{param.name}: {type_name} = Header(..., description='{description}')";
202
+ } elif (param.default is None) {
203
+ return f"{param.name}: Optional[{type_name}] = Header(default=None, description='{description}')";
204
+ } else {
205
+ return f"{param.name}: Optional[{type_name}] = Header({repr(param.default)}, description='{description}')";
206
+ }
207
+ }
208
+
209
+ """Implementation of FILE parameter handler."""
210
+ impl FileParameterHandler.generate_param_string(param: APIParameter) -> str {
211
+ description = param.description or '';
212
+ is_multiple = 'list' in param.data_type.lower();
213
+ if is_multiple {
214
+ if param.required {
215
+ return f"{param.name}: List[UploadFile] = File(..., description='{description}')";
216
+ } else {
217
+ return f"{param.name}: Optional[List[UploadFile]] = File(default=None, description='{description}')";
218
+ }
219
+ } else {
220
+ if param.required {
221
+ return f"{param.name}: UploadFile = File(..., description='{description}')";
222
+ } else {
223
+ return f"{param.name}: Optional[UploadFile] = File(default=None, description='{description}')";
224
+ }
225
+ }
226
+ }
227
+
228
+ """Implementation of Form BODY parameter handler."""
229
+ impl FormBodyParameterHandler.generate_param_string(param: APIParameter) -> str {
230
+ type_name = self.get_type_name(param);
231
+ description = param.description or '';
232
+ if param.required {
233
+ return f"{param.name}: {type_name} = Form(..., description='{description}')";
234
+ } elif (param.default is None) {
235
+ return f"{param.name}: Optional[{type_name}] = Form(default=None, description='{description}')";
236
+ } else {
237
+ return f"{param.name}: Optional[{type_name}] = Form(default={repr(
238
+ param.default
239
+ )}, description='{description}')";
240
+ }
241
+ }
242
+
243
+ """Implementation of JSON BODY parameter handler - generate Pydantic model."""
244
+ impl JSONBodyParameterHandler.generate_body_model -> (type[BaseModel] | None) {
245
+ model_fields: dict[(str, Any)] = {};
246
+ for param in self.body_params {
247
+ if not param.name {
248
+ continue;
249
+ }
250
+ param_type = self.type_converter._get_python_type(param.data_type);
251
+ description = param.description or '';
252
+ if param.required {
253
+ model_fields[param.name] = (
254
+ param_type,
255
+ Field(..., description=description)
256
+ );
257
+ } else {
258
+ if (param.default is None) {
259
+ model_fields[param.name] = (
260
+ (param_type | None),
261
+ Field(description=description)
262
+ );
263
+ } else {
264
+ model_fields[param.name] = (
265
+ (param_type | None),
266
+ Field(param.default, description=description)
267
+ );
268
+ }
269
+ }
270
+ }
271
+ if model_fields {
272
+ return create_model('RequestBody', **model_fields);
273
+ }
274
+ return None;
275
+ }
276
+
277
+ """Create the actual endpoint function with parameter injection."""
278
+ impl JFastApiServer._create_endpoint_function(
279
+ self: JFastApiServer,
280
+ callback: Callable[(..., Any)],
281
+ parameters: list[APIParameter],
282
+ dependencies: list[Any]
283
+ ) -> Callable[..., Any] {
284
+ sig = inspect.signature(callback);
285
+ accepts_kwargs = <>any(
286
+ (p.kind == inspect.Parameter.VAR_KEYWORD) for p in sig.parameters.values()
287
+ );
288
+ if not parameters {
289
+ if accepts_kwargs {
290
+ if inspect.iscoroutinefunction(callback) {
291
+ async def async_endpoint_wrapper(request: Request) -> EndpointResponse {
292
+ import from jaclang.runtimelib.transport { TransportResponse }
293
+ try {
294
+ query_params = <>dict(request.query_params);
295
+ result = await callback(**query_params);
296
+ # Automatically convert TransportResponse to JSONResponse (like HTTPTransport.send)
297
+ if isinstance(result, TransportResponse) {
298
+ return _convert_transport_response_to_json_response(result);
299
+ }
300
+ return result;
301
+ } except Exception as e {
302
+ raise e from HTTPException(status_code=500, detail=str(e)) ;
303
+ }
304
+ }
305
+ return async_endpoint_wrapper;
306
+ } else {
307
+ def sync_endpoint_wrapper(request: Request) -> EndpointResponse {
308
+ import from jaclang.runtimelib.transport { TransportResponse }
309
+ try {
310
+ query_params = <>dict(request.query_params);
311
+ result = callback(**query_params);
312
+ # Automatically convert TransportResponse to JSONResponse (like HTTPTransport.send)
313
+ if isinstance(result, TransportResponse) {
314
+ return _convert_transport_response_to_json_response(result);
315
+ }
316
+ return result;
317
+ } except Exception as e {
318
+ raise e from HTTPException(status_code=500, detail=str(e)) ;
319
+ }
320
+ }
321
+ return sync_endpoint_wrapper;
322
+ }
323
+ } elif inspect.iscoroutinefunction(callback) {
324
+ async def async_endpoint_wrapper__1 -> EndpointResponse {
325
+ import from jaclang.runtimelib.transport { TransportResponse }
326
+ try {
327
+ result = await callback();
328
+ # Automatically convert TransportResponse to JSONResponse (like HTTPTransport.send)
329
+ if isinstance(result, TransportResponse) {
330
+ return _convert_transport_response_to_json_response(result);
331
+ }
332
+ return result;
333
+ } except Exception as e {
334
+ raise e from HTTPException(status_code=500, detail=str(e)) ;
335
+ }
336
+ }
337
+ return async_endpoint_wrapper__1;
338
+ } else {
339
+ def sync_endpoint_wrapper__1 -> EndpointResponse {
340
+ import from jaclang.runtimelib.transport { TransportResponse }
341
+ try {
342
+ result = callback();
343
+ # Automatically convert TransportResponse to JSONResponse (like HTTPTransport.send)
344
+ if isinstance(result, TransportResponse) {
345
+ return _convert_transport_response_to_json_response(result);
346
+ }
347
+ return result;
348
+ } except Exception as e {
349
+ raise e from HTTPException(status_code=500, detail=str(e)) ;
350
+ }
351
+ }
352
+ return sync_endpoint_wrapper__1;
353
+ }
354
+ }
355
+ # Group parameters by type
356
+ body_params: list[APIParameter] = [];
357
+ path_params: list[APIParameter] = [];
358
+ query_params: list[APIParameter] = [];
359
+ header_params: list[APIParameter] = [];
360
+ file_params: list[APIParameter] = [];
361
+ for param in parameters {
362
+ param_location = param.type;
363
+ if (param_location == ParameterType.BODY) {
364
+ body_params.append(param);
365
+ } elif (param_location == ParameterType.PATH) {
366
+ path_params.append(param);
367
+ } elif (param_location == ParameterType.QUERY) {
368
+ query_params.append(param);
369
+ } elif (param_location == ParameterType.HEADER) {
370
+ header_params.append(param);
371
+ } elif (param_location == ParameterType.FILE) {
372
+ file_params.append(param);
373
+ }
374
+ }
375
+ # Initialize parameter strings and mapping
376
+ param_strs: list[str] = [];
377
+ param_mapping: dict[(str, str)] = {};
378
+ needs_request = 'request' in sig.parameters;
379
+ if accepts_kwargs {
380
+ param_strs.append('request: Request');
381
+ param_mapping['__request__'] = 'request';
382
+ } elif needs_request {
383
+ param_strs.append('request: Request');
384
+ param_mapping['request'] = 'request';
385
+ }
386
+ # Create handler map
387
+ has_file_params = len(file_params) > 0;
388
+ handler_map: dict[(ParameterType, ParameterHandler)] = {
389
+ ParameterType.PATH: PathParameterHandler(type_converter=self),
390
+ ParameterType.QUERY: QueryParameterHandler(type_converter=self),
391
+ ParameterType.HEADER: HeaderParameterHandler(type_converter=self),
392
+ ParameterType.FILE: FileParameterHandler(type_converter=self)
393
+ };
394
+ # Handle body parameters using appropriate handler
395
+ body_model: (type[BaseModel] | None) = None;
396
+ if (len(body_params) >= 1) {
397
+ if has_file_params {
398
+ # Use Form handler when files are present (multipart/form-data)
399
+ form_handler = FormBodyParameterHandler(type_converter=self);
400
+ for param in body_params {
401
+ if param.name {
402
+ param_str = form_handler.generate_param_string(param);
403
+ param_strs.append(param_str);
404
+ param_mapping[param.name] = param.name;
405
+ }
406
+ }
407
+ } else {
408
+ # Use JSON body handler (Pydantic model)
409
+ json_handler = JSONBodyParameterHandler(
410
+ type_converter=self, body_params=body_params
411
+ );
412
+ body_model = json_handler.generate_body_model();
413
+ if body_model {
414
+ param_strs.append('body_data: RequestBody');
415
+ param_mapping['body_data'] = 'body_data';
416
+ }
417
+ }
418
+ }
419
+ # Process other parameter types using their handlers
420
+ param_groups = [
421
+ (path_params, ParameterType.PATH),
422
+ (query_params, ParameterType.QUERY),
423
+ (header_params, ParameterType.HEADER),
424
+ (file_params, ParameterType.FILE)
425
+ ];
426
+ for (param_list, param_type) in param_groups {
427
+ handler = handler_map.get(param_type);
428
+ if handler {
429
+ for param in param_list {
430
+ if param.name {
431
+ param_str = handler.generate_param_string(param);
432
+ param_strs.append(param_str);
433
+ param_mapping[param.name] = param.name;
434
+ }
435
+ }
436
+ }
437
+ }
438
+ params = ', '.join(param_strs);
439
+ callback_args_lines: list[str] = [];
440
+ if body_model {
441
+ for param in body_params {
442
+ param_name = param.name;
443
+ if param_name {
444
+ callback_args_lines.append(
445
+ f" callback_args['{param_name}'] = body_data.{param_name}"
446
+ );
447
+ }
448
+ }
449
+ for name in param_mapping {
450
+ if (name not in ('body_data', '__request__')) {
451
+ callback_args_lines.append(f" callback_args['{name}'] = {name}");
452
+ }
453
+ }
454
+ } else {
455
+ callback_args_lines = [
456
+ f" callback_args['{name}'] = {name}"
457
+ for name in param_mapping
458
+ if (name != '__request__')
459
+ ];
460
+ }
461
+ callback_args_str = '\n'.join(callback_args_lines);
462
+ extra_query_params_code = '';
463
+ if accepts_kwargs {
464
+ declared_params = [
465
+ p.name
466
+ for p in parameters
467
+ if p.name
468
+ ];
469
+ declared_params_str = repr(declared_params);
470
+ extra_query_params_code = f"""
471
+ # Extract additional query parameters not explicitly declared
472
+ declared_params = set({declared_params_str})
473
+ for key, value in request.query_params.items():
474
+ if key not in declared_params:
475
+ callback_args[key] = value
476
+ """;
477
+ }
478
+ if inspect.iscoroutinefunction(callback) {
479
+ func_code = f"""
480
+ async def endpoint_wrapper({params}):
481
+ try:
482
+ callback_args: Dict[str, Any] = {{}}
483
+ {callback_args_str}{extra_query_params_code}
484
+ result = await callback(**callback_args)
485
+ # Automatically convert TransportResponse to JSONResponse (like HTTPTransport.send)
486
+ if isinstance(result, TransportResponse):
487
+ return _convert_transport_response_to_json_response(result)
488
+ return result
489
+ except Exception as e:
490
+ raise HTTPException(status_code=500, detail=str(e))
491
+ """;
492
+ } else {
493
+ func_code = f"""
494
+ def endpoint_wrapper({params}):
495
+ try:
496
+ callback_args: Dict[str, Any] = {{}}
497
+ {callback_args_str}{extra_query_params_code}
498
+ result = callback(**callback_args)
499
+ # Automatically convert TransportResponse to JSONResponse (like HTTPTransport.send)
500
+ if isinstance(result, TransportResponse):
501
+ return _convert_transport_response_to_json_response(result)
502
+ return result
503
+ except Exception as e:
504
+ raise HTTPException(status_code=500, detail=str(e))
505
+ """;
506
+ }
507
+ exec_globals: dict[(str, Any)] = {
508
+ 'callback': callback,
509
+ 'HTTPException': HTTPException,
510
+ 'Query': Query,
511
+ 'Path': Path,
512
+ 'Body': Body,
513
+ 'Header': Header,
514
+ 'File': File,
515
+ 'Form': Form,
516
+ 'UploadFile': UploadFile,
517
+ 'Request': Request,
518
+ 'Optional': Optional,
519
+ 'Field': Field,
520
+ 'create_model': create_model,
521
+ 'Dict': <>dict,
522
+ 'List': <>list,
523
+ 'Any': Any,
524
+ 'int': int,
525
+ 'str': str,
526
+ 'float': float,
527
+ 'bool': bool,
528
+ 'list': <>list,
529
+ 'dict': <>dict,
530
+ 'TransportResponse': TransportResponse,
531
+ '_convert_transport_response_to_json_response': _convert_transport_response_to_json_response,
532
+ 'isinstance': isinstance
533
+ };
534
+ if body_model {
535
+ exec_globals['RequestBody'] = body_model;
536
+ }
537
+ exec(func_code, exec_globals);
538
+ return exec_globals['endpoint_wrapper'];
539
+ }
540
+
541
+ """Get the default status code for an HTTP method."""
542
+ impl JFastApiServer._get_default_status_code(
543
+ self: JFastApiServer, method: HTTPMethod
544
+ ) -> int {
545
+ status_codes = {
546
+ HTTPMethod.GET: 200,
547
+ HTTPMethod.POST: 200,
548
+ HTTPMethod.PUT: 200,
549
+ HTTPMethod.PATCH: 200,
550
+ HTTPMethod.DELETE: 204
551
+ };
552
+ return status_codes.get(method, 200);
553
+ }
554
+
555
+ """
556
+ Create and register a FastAPI route for the given endpoint.
557
+ Args:method (HTTPMethod): The HTTP method for the route
558
+ method (HTTPMethod): The HTTP method for the route
559
+ endpoint (JEndPoint): The endpoint configuration
560
+ """
561
+ impl JFastApiServer._create_fastapi_route(
562
+ self: JFastApiServer, method: HTTPMethod, endpoint: JEndPoint
563
+ ) -> None {
564
+ endpoint_func = self._create_endpoint_function(
565
+ endpoint.callback, (endpoint.parameters or []), []
566
+ );
567
+ route_kwargs: dict[(str, Any)] = {
568
+ 'response_model': endpoint.response_model,
569
+ 'status_code': self._get_default_status_code(method),
570
+ 'summary': (endpoint.summary or f"{method.value} {endpoint.path}"),
571
+ 'description': (
572
+ endpoint.description or endpoint.callback.__doc__
573
+ if endpoint.callback.__doc__
574
+ else ''
575
+ ),
576
+ 'tags': (endpoint.tags or [])
577
+ };
578
+ try {
579
+ hints = get_type_hints(endpoint.callback);
580
+ return_type = hints.get('return');
581
+ if (
582
+ return_type
583
+ and isinstance(return_type, <>type)
584
+ and issubclass(return_type, Response)
585
+ ) {
586
+ route_kwargs['response_class'] = return_type;
587
+ }
588
+ } except Exception {
589
+ ;
590
+ }
591
+ if (method == HTTPMethod.GET) {
592
+ self.app.get(endpoint.path, **route_kwargs)(endpoint_func);
593
+ } elif (method == HTTPMethod.POST) {
594
+ self.app.post(endpoint.path, **route_kwargs)(endpoint_func);
595
+ } elif (method == HTTPMethod.PUT) {
596
+ self.app.put(endpoint.path, **route_kwargs)(endpoint_func);
597
+ } elif (method == HTTPMethod.PATCH) {
598
+ self.app.patch(endpoint.path, **route_kwargs)(endpoint_func);
599
+ } elif (method == HTTPMethod.DELETE) {
600
+ self.app.delete(endpoint.path, **route_kwargs)(endpoint_func);
601
+ }
602
+ }
603
+
604
+ """
605
+ Create a complete FastAPI server with all endpoints registered.
606
+ This method executes all registered endpoints to create FastAPI routes
607
+ and returns the configured FastAPI application.
608
+
609
+ Returns:
610
+ FastAPI: The configured FastAPI application instance
611
+ """
612
+ impl JFastApiServer.create_server(self: JFastApiServer) -> FastAPI {
613
+ if not self.__server_created {
614
+ self.execute();
615
+ self.__server_created = True;
616
+ }
617
+ return self.app;
618
+ }
619
+
620
+ """
621
+ Execute all endpoints by processing them through their respective HTTP method handlers.
622
+ Routes are sorted to ensure more specific paths are registered before generic ones
623
+ to avoid path matching conflicts.
624
+ """
625
+ impl JFastApiServer.execute(self: JFastApiServer) -> None {
626
+ self._endpoints = sorted(self._endpoints, key=self._route_priority);
627
+ super.execute();
628
+ }
629
+
630
+ """
631
+ Calculate route priority for sorting. More specific routes get higher priority (lower number).
632
+ Priority rules:
633
+ 1. Static paths (no parameters) come first
634
+ 2. Paths with fewer parameters come before paths with more parameters
635
+ 3. Longer paths come before shorter paths
636
+ 4. Alphabetical order for tie-breaking
637
+ """
638
+ impl JFastApiServer._route_priority(
639
+ self: JFastApiServer, endpoint: JEndPoint
640
+ ) -> tuple[int, int, int, str] {
641
+ path = endpoint.path;
642
+ is_catchall = ':path}' in path;
643
+ param_count = path.count('{');
644
+ segment_count = len(
645
+ [
646
+ seg
647
+ for seg in path.split('/')
648
+ if seg
649
+ ]
650
+ );
651
+ catchall_priority = 1 if is_catchall else 0;
652
+ priority = param_count;
653
+ return (catchall_priority, priority, -segment_count, path);
654
+ }
655
+
656
+ """
657
+ Handle execution of a DELETE endpoint by registering it with FastAPI.
658
+ Args:
659
+ endpoint (JEndPoint): The DELETE endpoint to execute
660
+ Returns:
661
+ JFastApiServer: Self for method chaining
662
+ """
663
+ impl JFastApiServer._delete(
664
+ self: JFastApiServer, endpoint: JEndPoint
665
+ ) -> 'JFastApiServer' {
666
+ self._create_fastapi_route(HTTPMethod.DELETE, endpoint);
667
+ return self;
668
+ }
669
+
670
+ """
671
+ Handle execution of a PATCH endpoint by registering it with FastAPI.
672
+ Args:
673
+ endpoint (JEndPoint): The PATCH endpoint to execute
674
+ Returns:
675
+ JFastApiServer: Self for method chaining
676
+ """
677
+ impl JFastApiServer._patch(
678
+ self: JFastApiServer, endpoint: JEndPoint
679
+ ) -> 'JFastApiServer' {
680
+ self._create_fastapi_route(HTTPMethod.PATCH, endpoint);
681
+ return self;
682
+ }
683
+
684
+ """
685
+ Handle execution of a PUT endpoint by registering it with FastAPI.
686
+ Args:
687
+ endpoint (JEndPoint): The PUT endpoint to execute
688
+ Returns:
689
+ JFastApiServer: Self for method chaining
690
+ """
691
+ impl JFastApiServer._put(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer' {
692
+ self._create_fastapi_route(HTTPMethod.PUT, endpoint);
693
+ return self;
694
+ }
695
+
696
+ """
697
+ Handle execution of a POST endpoint by registering it with FastAPI.
698
+ Args:
699
+ endpoint (JEndPoint): The POST endpoint to execute
700
+ Returns:
701
+ JFastApiServer: Self for method chaining
702
+ """
703
+ impl JFastApiServer._post(
704
+ self: JFastApiServer, endpoint: JEndPoint
705
+ ) -> 'JFastApiServer' {
706
+ self._create_fastapi_route(HTTPMethod.POST, endpoint);
707
+ return self;
708
+ }
709
+
710
+ """
711
+ Handle execution of a GET endpoint by registering it with FastAPI.
712
+ Args:
713
+ endpoint (JEndPoint): The GET endpoint to execute
714
+ Returns:
715
+ JFastApiServer: Self for method chaining
716
+ """
717
+ impl JFastApiServer._get(self: JFastApiServer, endpoint: JEndPoint) -> 'JFastApiServer' {
718
+ self._create_fastapi_route(HTTPMethod.GET, endpoint);
719
+ return self;
720
+ }
721
+
722
+ impl JFastApiServer.init(
723
+ self: JFastApiServer,
724
+ endpoints: (list[JEndPoint] | None) = None,
725
+ app: (FastAPI | None) = None
726
+ ) -> None {
727
+ super.init((endpoints or []));
728
+ self.app = app or FastAPI();
729
+ self._models: dict[(str, type[BaseModel])] = {};
730
+ self.__server_created = False;
731
+ }