class API: """API Class of Arche Framework""" def __init__(self, templates_dir="templates", static_dir="static"): self.routes = {} self.templates_env = Environment( loader=FileSystemLoader(os.path.abspath(templates_dir))) self.exception_handler = None self.whitenoise = WhiteNoise(self._wsgi_app, root=static_dir) self.middleware = Middleware(self) def add_route(self, path, handler): assert path not in self.routes, "A route already exists" self.routes[path] = handler def route(self, path): assert path not in self.routes, "A route already exists" def wrapper(handler): self.add_route(path, handler) return handler return wrapper def _wsgi_app(self, env, start_response): request = Request(env) response = self.handle_request(request) return response(env, start_response) def __call__(self, env, start_response): path_info = env["PATH_INFO"] if path_info.startswith("/static"): env["PATH_INFO"] = path_info[len("/static"):] return self.whitenoise(env, start_response) return self.middleware(env, start_response) def handle_request(self, request): response = Response() try: handler, kwargs = self.find_handler(request_path=request.path) if handler is not None: if inspect.isclass(handler): handler = getattr(handler(), request.method.lower(), None) if handler is None: raise AttributeError("Method Not Allowed: ", request.method) handler(request, response, **kwargs) else: self.default_response(response) except Exception as e: if self.exception_handler is None: raise e else: self.exception_handler(request, response, e) return response def find_handler(self, request_path): for path, handler in self.routes.items(): parse_result = parse(path, request_path) if parse_result is not None: return handler, parse_result.named return None, None def default_response(self, response): response.status_code = 404 response.text = "OOPS! Your requested page is not Found." # TODO: def add_exception_handler(self, exception_handler): self.exception_handler = exception_handler def template(self, template_name, context=None): if context is None: context = {} return self.templates_env.get_template(template_name).render(**context) def add_middleware(self, middleware_cls): self.middleware.add(middleware_cls) # Test Session def test_session(self, base_url="http://testserver"): session = RequestSession() session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self)) return session
class API: def __init__(self, templates_dir="templates", static_dir="static"): self.routes = {} self.exception_handler = None self.templates_env = Environment(loader=FileSystemLoader( searchpath=os.path.abspath(templates_dir))) self.whitenoise = WhiteNoise(application=self.wsgi_app, root=static_dir) self.middleware = Middleware(self) def __call__(self, environ, start_response): """ We need to distinguish if the request if for static files and use WhiteNoise, or for data and use the Middleware """ path_info = environ["PATH_INFO"] if path_info.startswith("/static"): environ["PATH_INFO"] = path_info[len("/static"):] return self.whitenoise(environ, start_response) return self.middleware(environ, start_response) def wsgi_app(self, environ, start_response): request = Request(environ) response = self.handle_request(request) return response(environ, start_response) def test_session(self, base_url=BASE_URL): session = RequestsSession() session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self)) return session def add_middleware(self, middleware_cls): self.middleware.add(middleware_cls) @staticmethod def default_response(response): response.status_code = 404 response.text = "Not found." def add_exception_handler(self, exception_handler): self.exception_handler = exception_handler def find_handler(self, request_path): """ Look for the corresponding handler for the incoming path """ for path, handler_data in self.routes.items(): parsed_result = parse(path, request_path) if parsed_result is not None: return handler_data, parsed_result.named return None, None def handle_request(self, request): response = Response() handler_data, kwargs = self.find_handler(request_path=request.path) try: if handler_data: handler = handler_data["handler"] allowed_methods = handler_data["allowed_methods"] if inspect.isclass(handler): handler = getattr(handler(), request.method.lower(), None) if handler is None: raise AttributeError("Method not allowed", request.method) else: if request.method.lower() not in allowed_methods: raise AttributeError("Method not allowed", request.method) handler(request, response, **kwargs) else: self.default_response(response) except Exception as e: if self.exception_handler is None: raise e else: self.exception_handler(request, response, e) return response def add_route(self, path, handler, allowed_methods=None): assert path not in self.routes, "Such route already exists." if allowed_methods is None: allowed_methods = [ "get", "post", "put", "patch", "delete", "options" ] self.routes[path] = { "handler": handler, "allowed_methods": allowed_methods } def route(self, path, allowed_methods=None): if path in self.routes.keys(): raise AssertionError("Duplicated route") def wrapper(handler): self.add_route(path, handler, allowed_methods) return handler return wrapper def template(self, template_name, context=None): context = {} or context return self.templates_env.get_template(name=template_name).render( **context)
class API: def __init__(self, templates_dir="templates", static_dir="static"): self.routes = {} self.templates_env = Environment( loader=FileSystemLoader(os.path.abspath(templates_dir))) self.exception_handler = None self.whitenoise = WhiteNoise(self.wsgi_app, root=static_dir) self.static_dir = os.path.abspath(static_dir) self._static_root = "/static" self.middleware = Middleware(self) def wsgi_app(self, environ, start_response): request = Request(environ) response = self.handle_request(request) return response(environ, start_response) def __call__(self, environ, start_response): path_info = environ["PATH_INFO"] if request_for_static(path_info, self._static_root): environ["PATH_INFO"] = cut_static_root(path_info, self._static_root) return self.whitenoise(environ, start_response) return self.middleware(environ, start_response) def add_middleware(self, middleware_cls): self.middleware.add(middleware_cls) def route(self, path): def wrapper(handler): self.add_route(path, handler) return handler return wrapper def add_route(self, path, handler): assert path not in self.routes, f"{path} already exists." self.routes[path] = handler def test_session(self, base_url="http:''testserver"): session = RequestsSession() session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self)) return session def handle_request(self, request): response = Response() handler, kwargs = self.find_handler(request_path=request.path) try: if handler is not None: if inspect.isclass(handler): handler = getattr(handler(), request.method.lower(), None) if handler is None: raise AttributeError("Method in not allowed", request.method) handler(request, response, **kwargs) else: self.default_response(response) except Exception as e: if self.exception_handler is None: raise e else: self.exception_handler(request, response, e) return response def default_response(self, response): response.status_code = 404 response.text = "Not found" def find_handler(self, request_path): for path, handler in self.routes.items(): parse_result = parse(path, request_path) if parse_result is not None: return handler, parse_result.named return None, None def template(self, template_name, context=None): if context is None: context = {} return self.templates_env.get_template(template_name).render(**context) def add_exception_handler(self, exception_handler): self.exception_handler = exception_handler
class API(object): def __init__(self, templates_dir="templates", static_dir="static"): self.templates_dir = templates_dir self.static_dir = static_dir # url路由 self.routes = {} # html文件夹 self.templates_env = Environment( loader=FileSystemLoader(os.path.abspath(self.templates_dir))) # css、JavaScript文件夹 self.whitenoise = WhiteNoise(self.wsgi_app, root=static_dir) # 自定义错误 self.exception_handler = None # 请求中间件,将api对象传入 self.middleware = Middleware(self) def template(self, template_name, context=None): """返回模板内容""" if context is None: context = {} return self.templates_env.get_template(template_name).render(**context) # 不用webob只能直接返回二进制数据 # def __call__(self, environ, start_response): # response_body = b'Hello, World!' # status = '200 OK' # start_response(status, headers=[]) # return iter([response_body]) def wsgi_app(self, environ, start_response): """通过 webob 将请求的环境信息转为request对象""" request = Request(environ) response = self.handle_request(request) return response(environ, start_response) def __call__(self, environ, start_response): path_info = environ["PATH_INFO"] static = "/" + self.static_dir # 以 /static 开头 或 中间件为空 if path_info.startswith(static) or not self.middleware: # "/static/index.css" -> 只取 /index.css, /static开头只是用于判断 environ["PATH_INFO"] = path_info[len(static):] return self.whitenoise(environ, start_response) return self.middleware(environ, start_response) def handle_request(self, request): """请求调度""" response = Response() handler, kwargs = self.find_handler(request.path) try: if handler is not None: if inspect.isclass(handler): # 如果是类,则获取其中的方法 handler = getattr(handler(), request.method.lower(), None) if handler is None: # 类中该方法不存在,则该类不支持该请求类型 raise AttributeError("Method now allowed", request.method) handler(request, response, **kwargs) else: self.defalut_response(response) except Exception as e: if self.exception_handler is None: raise e else: # 自定义错误返回形式 self.exception_handler(request, response, e) return response def find_handler(self, request_path): for path, handler in self.routes.items(): parse_result = parse(path, request_path) if parse_result is not None: return handler, parse_result.named return None, None def defalut_response(self, response): response.status_code = 404 response.text = "Not Found" def route(self, path): # 添加路由的装饰器 def wrapper(handler): self.add_route(path, handler) return handler return wrapper def add_route(self, path, handler): # 相同路径不可重复添加 assert path not in self.routes, "Such route already exists" self.routes[path] = handler def add_exception_handler(self, exception_handler): # 添加自定义error handler self.exception_handler = exception_handler def add_middleware(self, middleware_cls): # 添加中间件 self.middleware.add(middleware_cls)
class API: def __init__(self, templates_directory="templates", static_dir="static"): self.routes = {} #paths are the keys and handlers (functions or classes) are the values self.templates_environment = Environment( loader=FileSystemLoader(os.path.abspath(templates_directory)) ) self.exception_handler = None self.whitenoise = WhiteNoise(self.wsgi_application, root=static_dir) #wrap wsgi application to serve static files self.middleware = Middleware(self) def __call__(self, environ, start_response): """ Compatible WSGI server will call for each client HTTP request. Request for static files are treated differently from other request. """ print("Callable was triggered due to request from client application at time: " + str(time.time())) path_info = environ["PATH_INFO"] if path_info.startswith("/static"): environ["PATH_INFO"] = path_info[len("/static"):] print(environ["PATH_INFO"]) return self.whitenoise(environ, start_response) else: return self.middleware(environ, start_response) def wsgi_application(self, environ, start_response): request = Request(environ) response = self.handle_request(request) return response(environ, start_response) """ Middleware """ def add_middleware(self, middleware_cls): self.middleware.add(middleware_cls) """ TEST CLIENT """ def test_session(self, base_url="http://testserver"): """ Creates a test client associated with the given a url. The test session will be a requests session to be able to emulate a client in the browser. """ session = RequestsSession() session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self)) return session """ ROUTING """ def add_route(self, path, handler): """ Adds a route which is a key-value pair. The key is the url path and the value is either a function handler or a class-based route. """ if path in self.routes: raise AssertionError("Failed. Such a route already exists.") self.routes[path] = handler def route(self, path): """ Adds a route via a decorator function. """ def wrapper(handler): self.routes[path] = handler return handler return wrapper """ TEMPLATING """ def get_template(self, template_name, context=None): """ Gets the template based on the template name """ if context is None: context = {} return self.templates_environment.get_template(template_name).render(**context) """ HANDLING """ def handle_request(self, request): """ Handles the client request, which is an webob object. """ response = Response() handler, kwargs = self.lookup_handler(request_path=request.path) try: if handler is not None: if inspect.isclass(handler): handler_method = self.get_class_method(handler, request) if handler_method is None: raise AttributeError("Method not allowed", request.method) handler_method(request, response, **kwargs) else: handler(request, response, **kwargs) else: self.default_response(response) except Exception as exception: if self.exception_handler is None: raise exception else: self.exception_handler(request, response, exception) return response def get_class_method(self, handler, request): """ Gets the method associated with the requested path. This function is used with class based routes. """ handler_method = getattr(handler(),request.method.lower(), None) return handler_method def lookup_handler(self, request_path): """ Finds the function handler associated with the requested path """ for path, handler in self.routes.items(): parse_result = parse(path, request_path) if parse_result is not None: return handler, parse_result.named return None, None def add_exception_handler(self, exception_handler): """ Adds a function to the application that handle exceptions """ self.exception_handler = exception_handler """ RESPONSES """ def default_response(self, response): response.status_code = 404 response.text = "Requested path not found."
class AppFactory: def __init__(self, templates_dir='templates', static_dir='static'): self.routes = {} self.templates_env = Environment( loader=FileSystemLoader(os.path.abspath(templates_dir))) self.exception_handler = None self.whitenoise = WhiteNoise(self.wsgi_app, root=static_dir) self.middleware = Middleware(self) def wsgi_app(self, environ, start_response): request = Request(environ) response = self.handle_request(request) return response(environ, start_response) def __call__(self, environ, start_response): path_info = environ['PATH_INFO'] if path_info.startswith('/static'): environ['PATH_INFO'] = path_info[len('/static'):] return self.whitenoise(environ, start_response) return self.middleware(environ, start_response) def add_route(self, route, handler): assert route not in self.routes, "Route already exists" self.routes[route] = handler def route(self, path): def wrapper(handler): self.add_route(path, handler) return handler return wrapper def default_response(self, response): response.status_code = 404 response.text = "Not Found" def get_handler(self, request_route): for path, handler in self.routes.items(): parse_params = parse(path, request_route) if parse_params: return handler, parse_params.named return None, None def handle_request(self, request): response = Response() handler, params = self.get_handler(request.path) try: if handler: if inspect.isclass(handler): handler = getattr(handler(), request.method.lower(), None) if not handler: raise AttributeError("Method not allowed", request.method) handler(request, response, **params) else: self.default_response(response) except Exception as e: if self.exception_handler is None: raise e else: self.exception_handler(request, response, e) return response def test_client(self, base_url='http://testserv'): session = RequestsSession() session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self)) return session def template(self, template_name, context=None): context = context if context else {} return self.templates_env.get_template(template_name).render(**context) def add_exception_handler(self, exception_handler): self.exception_handler = exception_handler def add_middleware(self, middleware_cls): self.middleware.add(middleware_cls)