1+ import traceback
12import types
23
3- from fastapi import FastAPI
4- from starlette .middleware .wsgi import WSGIMiddleware # todo replace this with a2wsgi
5- from osbot_fast_api .utils .Version import Version
6- from osbot_utils .base_classes .Type_Safe import Type_Safe
7- from starlette .middleware .cors import CORSMiddleware
8- from starlette .responses import RedirectResponse
9- from starlette .staticfiles import StaticFiles
10- from osbot_utils .utils .Misc import list_set
11- from osbot_utils .decorators .lists .index_by import index_by
12- from osbot_utils .decorators .methods .cache_on_self import cache_on_self
13- from starlette .testclient import TestClient
14- from osbot_fast_api .api .routes .Routes_Config import Routes_Config
15- from osbot_fast_api .utils .http_shell .Http_Shell__Server import Model__Shell_Command , Http_Shell__Server
16- from osbot_fast_api .utils .Fast_API_Utils import Fast_API_Utils
17- from osbot_fast_api .utils ._extra_osbot_utils import list_minus_list
18-
19- DEFAULT_ROUTES_PATHS = ['/' , '/config/status' , '/config/version' ]
4+ from fastapi import FastAPI , Request , HTTPException
5+ from fastapi .exceptions import RequestValidationError
6+ from starlette .middleware .wsgi import WSGIMiddleware # todo replace this with a2wsgi
7+ from osbot_fast_api .api .Fast_API__Http_Events import Fast_API__Http_Events
8+ from osbot_fast_api .api .middlewares .Middleware__Http_Request import Middleware__Http_Request
9+ from osbot_fast_api .api .middlewares .Middleware__Http_Request__Duration import Middleware__Http_Request__Duration
10+ from osbot_fast_api .api .middlewares .Middleware__Http_Request__Trace_Calls import Middleware__Http_Request__Trace_Calls
11+ from osbot_fast_api .utils .Version import Version
12+ from osbot_utils .base_classes .Type_Safe import Type_Safe
13+ from starlette .middleware .cors import CORSMiddleware
14+ from starlette .responses import RedirectResponse , JSONResponse
15+ from starlette .staticfiles import StaticFiles
16+ from osbot_utils .utils .Lists import list_index_by
17+ from osbot_utils .utils .Misc import list_set
18+ from osbot_utils .decorators .lists .index_by import index_by
19+ from osbot_utils .decorators .methods .cache_on_self import cache_on_self
20+ from starlette .testclient import TestClient
21+ from osbot_fast_api .api .routes .Routes_Config import Routes_Config
22+ from osbot_fast_api .utils .http_shell .Http_Shell__Server import Model__Shell_Command , Http_Shell__Server
23+ from osbot_fast_api .utils .Fast_API_Utils import Fast_API_Utils
24+
25+ DEFAULT_ROUTES_PATHS = ['/' , '/config/status' , '/config/version' ]
26+ DEFAULT__NAME__FAST_API = 'Fast_API'
2027
2128class Fast_API (Type_Safe ):
22- enable_cors : bool
29+ base_path : str = '/'
30+ enable_cors : bool = False
31+ default_routes : bool = True
32+ name : str = None
33+ http_events : Fast_API__Http_Events
34+
35+ def __init__ (self , ** kwargs ):
36+ super ().__init__ (** kwargs )
37+ self .name = self .__class__ .__name__
38+ self .http_events .fast_api_name = self .name
39+
40+ def add_global_exception_handlers (self ): # todo: move to Fast_API
41+ app = self .app ()
42+ @app .exception_handler (Exception )
43+ async def global_exception_handler (request : Request , exc : Exception ):
44+ stack_trace = traceback .format_exc ()
45+ content = { "detail" : "An unexpected error occurred." ,
46+ "error" : str (exc ) ,
47+ "stack_trace" : stack_trace }
48+ return JSONResponse ( status_code = 500 , content = content )
49+
50+ @app .exception_handler (HTTPException )
51+ async def http_exception_handler (request : Request , exc : HTTPException ):
52+ return JSONResponse ( status_code = exc .status_code , content = {"detail" : exc .detail })
53+
54+ @app .exception_handler (RequestValidationError )
55+ async def validation_exception_handler (request : Request , exc : RequestValidationError ):
56+ return JSONResponse ( status_code = 400 , content = {"detail" : exc .errors ()} )
57+
2358
2459 def add_flask_app (self , path , flask_app ):
2560 self .app ().mount (path , WSGIMiddleware (flask_app ))
@@ -42,12 +77,12 @@ def add_route_post(self, function):
4277 return self .add_route (function = function , methods = ['POST' ])
4378
4479 def add_routes (self , class_routes ):
45- class_routes (self .app ())
80+ class_routes (app = self .app ()). setup ( )
4681 return self
4782
4883 @cache_on_self
49- def app (self ):
50- return FastAPI ()
84+ def app (self , ** kwargs ):
85+ return FastAPI (** kwargs )
5186
5287 def app_router (self ):
5388 return self .app ().router
@@ -61,35 +96,54 @@ def fast_api_utils(self):
6196 def path_static_folder (self ): # override this to add support for serving static files from this directory
6297 return None
6398
99+ def mount (self , parent_app ):
100+ parent_app .mount (self .base_path , self .app ())
101+
64102 def setup (self ):
65- self .setup_middlewares () # overwrite to add middlewares
66- self .setup_default_middlewares ()
67- self .setup_default_routes ()
68- self .setup_static_routes ()
69- self .setup_routes () # overwrite to add routes
103+ self .add_global_exception_handlers ()
104+ self .setup_middlewares () # overwrite to add middlewares
105+ self .setup_default_routes ()
106+ self .setup_static_routes ()
107+ self .setup_routes () # overwrite to add routes
70108 return self
71109
72110 @index_by
73- def routes (self , include_default = False ):
74- return self .fast_api_utils ().fastapi_routes (include_default = include_default )
111+ def routes (self , include_default = False , expand_mounts = False ):
112+ return self .fast_api_utils ().fastapi_routes (include_default = include_default , expand_mounts = expand_mounts )
113+
114+ def route_remove (self , path ):
115+ for route in self .app ().routes :
116+ if getattr (route , 'path' , '' ) == path :
117+ self .app ().routes .remove (route )
118+ print (f'removed route: { path } : { route } ' )
119+ return True
120+ return False
75121
76122 def routes_methods (self ):
77123 return list_set (self .routes (index_by = 'method_name' ))
78124
79125
80- def routes_paths (self , include_default = False ):
81- paths = list_set (self .routes (index_by = 'http_path' ))
82- if include_default :
83- return paths
84- return list_minus_list (list_a = paths , list_b = DEFAULT_ROUTES_PATHS )
126+ def routes_paths (self , include_default = False , expand_mounts = False ):
127+ routes_paths = self .routes (include_default = include_default , expand_mounts = expand_mounts )
128+ return list_set (list_index_by (routes_paths , 'http_path' ))
129+ # paths = list_set(self.routes(index_by='http_path'))
130+ # if include_default:
131+ # return paths
132+ # return list_minus_list(list_a=paths, list_b=DEFAULT_ROUTES_PATHS)
133+
134+ def setup_middlewares (self ): # overwrite to add more middlewares
135+ self .setup_middleware__http_events ()
136+ if self .enable_cors :
137+ self .setup_middleware__cors ()
138+ return self
85139
86- def setup_middlewares (self ): return self # overwrite to add middlewares
87140 def setup_routes (self ): return self # overwrite to add rules
88141
89142
90143 def setup_default_routes (self ):
91- self .setup_add_root_route ()
92- self .add_routes (Routes_Config )
144+ if self .default_routes :
145+ self .setup_add_root_route ()
146+ self .add_routes (Routes_Config )
93147
94148 def setup_add_root_route (self ):
95149 def redirect_to_docs ():
@@ -104,10 +158,6 @@ def setup_static_routes(self):
104158 path_name = "static"
105159 self .app ().mount (path_static , StaticFiles (directory = path_static_folder ), name = path_name )
106160
107- def setup_default_middlewares (self ):
108- if self .enable_cors :
109- self .setup_middleware__cors ()
110-
111161 def setup_middleware__cors (self ): # todo: double check that this is working see bug test
112162 self .app ().add_middleware (CORSMiddleware ,
113163 allow_origins = ["*" ] ,
@@ -116,6 +166,12 @@ def setup_middleware__cors(self): # todo: double check that this i
116166 allow_headers = ["Content-Type" , "X-Requested-With" , "Origin" , "Accept" , "Authorization" ],
117167 expose_headers = ["Content-Type" , "X-Requested-With" , "Origin" , "Accept" , "Authorization" ])
118168
169+ def setup_middleware__http_events (self , ):
170+ # note the invocation order is the reverse of the order they are added
171+ self .app ().add_middleware (Middleware__Http_Request__Trace_Calls , http_events = self .http_events )
172+ self .app ().add_middleware (Middleware__Http_Request__Duration , http_events = self .http_events )
173+ self .app ().add_middleware (Middleware__Http_Request , http_events = self .http_events )
174+ return self
119175
120176 def user_middlewares (self ):
121177 middlewares = []
0 commit comments