def add_non_field_param_to_dependency( *, param: inspect.Parameter, dependant: Dependant ) -> Optional[bool]: """ Checks if the annotated class is subclass of `Request`, `WebSocket`, `Response`, `BackgroundTasks` or `SecurityScopes`, and sets one of the `dependant.*_param_name` fields to `param.name` accordingly. :returns: `True` or `None`. """ if lenient_issubclass(param.annotation, Request): dependant.request_param_name = param.name return True elif lenient_issubclass(param.annotation, WebSocket): dependant.websocket_param_name = param.name return True elif lenient_issubclass(param.annotation, Response): dependant.response_param_name = param.name return True elif lenient_issubclass(param.annotation, BackgroundTasks): dependant.background_tasks_param_name = param.name return True elif lenient_issubclass(param.annotation, SecurityScopes): dependant.security_scopes_param_name = param.name return True return None
def get_dependant(*, path: str, call: Callable, name: str = None) -> Dependant: path_param_names = get_path_param_names(path) endpoint_signature = inspect.signature(call) signature_params = endpoint_signature.parameters dependant = Dependant(call=call, name=name) for param_name in signature_params: param = signature_params[param_name] if isinstance(param.default, params.Depends): sub_dependant = get_sub_dependant(param=param, path=path) dependant.dependencies.append(sub_dependant) for param_name in signature_params: param = signature_params[param_name] if ((param.default == param.empty) or isinstance( param.default, params.Path)) and (param_name in path_param_names): assert (lenient_issubclass(param.annotation, param_supported_types) or param.annotation == param.empty ), f"Path params must be of one of the supported types" param = signature_params[param_name] add_param_to_fields( param=param, dependant=dependant, default_schema=params.Path, force_type=params.ParamTypes.path, ) elif (param.default == param.empty or param.default is None or isinstance(param.default, param_supported_types)) and ( param.annotation == param.empty or lenient_issubclass( param.annotation, param_supported_types)): add_param_to_fields(param=param, dependant=dependant, default_schema=params.Query) elif isinstance(param.default, params.Param): if param.annotation != param.empty: origin = getattr(param.annotation, "__origin__", None) param_all_types = param_supported_types + (list, tuple, set) if isinstance(param.default, (params.Query, params.Header)): assert lenient_issubclass( param.annotation, param_all_types ) or lenient_issubclass( origin, param_all_types ), f"Parameters for Query and Header must be of type str, int, float, bool, list, tuple or set: {param}" else: assert lenient_issubclass( param.annotation, param_supported_types ), f"Parameters for Path and Cookies must be of type str, int, float, bool: {param}" add_param_to_fields(param=param, dependant=dependant, default_schema=params.Query) elif lenient_issubclass(param.annotation, Request): dependant.request_param_name = param_name elif lenient_issubclass(param.annotation, BackgroundTasks): dependant.background_tasks_param_name = param_name elif not isinstance(param.default, params.Depends): add_param_to_body_fields(param=param, dependant=dependant) return dependant
def add_non_field_param_to_dependency(*, param: inspect.Parameter, dependant: Dependant) -> Optional[bool]: if lenient_issubclass(param.annotation, Request): dependant.request_param_name = param.name return True elif lenient_issubclass(param.annotation, WebSocket): dependant.websocket_param_name = param.name return True elif lenient_issubclass(param.annotation, BackgroundTasks): dependant.background_tasks_param_name = param.name return True elif lenient_issubclass(param.annotation, SecurityScopes): dependant.security_scopes_param_name = param.name return True return None
def get_flat_dependant( dependant: Dependant, *, skip_repeats: bool = False, visited: Optional[List[CacheKey]] = None, ) -> Dependant: if visited is None: visited = [] visited.append(dependant.cache_key) flat_dependant = Dependant( path_params=dependant.path_params.copy(), query_params=dependant.query_params.copy(), header_params=dependant.header_params.copy(), cookie_params=dependant.cookie_params.copy(), body_params=dependant.body_params.copy(), security_schemes=dependant.security_requirements.copy(), use_cache=dependant.use_cache, path=dependant.path, ) for sub_dependant in dependant.dependencies: if skip_repeats and sub_dependant.cache_key in visited: continue flat_sub = get_flat_dependant(sub_dependant, skip_repeats=skip_repeats, visited=visited) flat_dependant.path_params.extend(flat_sub.path_params) flat_dependant.query_params.extend(flat_sub.query_params) flat_dependant.header_params.extend(flat_sub.header_params) flat_dependant.cookie_params.extend(flat_sub.cookie_params) flat_dependant.body_params.extend(flat_sub.body_params) flat_dependant.security_requirements.extend( flat_sub.security_requirements) return flat_dependant
def get_dependant(*, path: str, call: Callable, name: str = None) -> Dependant: path_param_names = get_path_param_names(path) endpoint_signature = inspect.signature(call) signature_params = endpoint_signature.parameters dependant = Dependant(call=call, name=name) for param_name in signature_params: param = signature_params[param_name] if isinstance(param.default, params.Depends): sub_dependant = get_sub_dependant(param=param, path=path) dependant.dependencies.append(sub_dependant) for param_name in signature_params: param = signature_params[param_name] if ((param.default == param.empty) or isinstance( param.default, params.Path)) and (param_name in path_param_names): assert ( lenient_issubclass(param.annotation, param_supported_types) or param.annotation == param.empty ), f"Path params must be of type str, int, float or boot: {param}" param = signature_params[param_name] add_param_to_fields( param=param, dependant=dependant, default_schema=params.Path, force_type=params.ParamTypes.path, ) elif (param.default == param.empty or param.default is None or type(param.default) in param_supported_types) and ( param.annotation == param.empty or lenient_issubclass( param.annotation, param_supported_types)): add_param_to_fields(param=param, dependant=dependant, default_schema=params.Query) elif isinstance(param.default, params.Param): if param.annotation != param.empty: assert lenient_issubclass( param.annotation, param_supported_types ), f"Parameters for Path, Query, Header and Cookies must be of type str, int, float or bool: {param}" add_param_to_fields(param=param, dependant=dependant, default_schema=params.Query) elif lenient_issubclass(param.annotation, Request): dependant.request_param_name = param_name elif not isinstance(param.default, params.Depends): add_param_to_body_fields(param=param, dependant=dependant) return dependant
def get_dependant( *, path: str, call: Callable, name: str = None, security_scopes: List[str] = None, use_cache: bool = True, ) -> Dependant: path_param_names = get_path_param_names(path) endpoint_signature = get_typed_signature(call) signature_params = endpoint_signature.parameters if inspect.isgeneratorfunction(call) or inspect.isasyncgenfunction(call): check_dependency_contextmanagers() dependant = Dependant(call=call, name=name, path=path, use_cache=use_cache) for param_name, param in signature_params.items(): if isinstance(param.default, params.Depends): sub_dependant = get_param_sub_dependant( param=param, path=path, security_scopes=security_scopes) dependant.dependencies.append(sub_dependant) for param_name, param in signature_params.items(): if isinstance(param.default, params.Depends): continue if add_non_field_param_to_dependency(param=param, dependant=dependant): continue param_field = get_param_field(param=param, default_field_info=params.Query, param_name=param_name) if param_name in path_param_names: assert is_scalar_field( field=param_field ), f"Path params must be of one of the supported types" if isinstance(param.default, params.Path): ignore_default = False else: ignore_default = True param_field = get_param_field( param=param, param_name=param_name, default_field_info=params.Path, force_type=params.ParamTypes.path, ignore_default=ignore_default, ) add_param_to_fields(field=param_field, dependant=dependant) elif is_scalar_field(field=param_field): add_param_to_fields(field=param_field, dependant=dependant) elif isinstance( param.default, (params.Query, params.Header)) and is_scalar_sequence_field(param_field): add_param_to_fields(field=param_field, dependant=dependant) else: field_info = get_field_info(param_field) assert isinstance( field_info, params.Body ), f"Param: {param_field.name} can only be a request body, using Body(...)" dependant.body_params.append(param_field) return dependant
def fix_query_dependencies(dependant: Dependant): dependant.body_params.extend(dependant.query_params) dependant.query_params = [] for field in dependant.body_params: if not isinstance(field.field_info, Params): field.field_info.embed = True for sub_dependant in dependant.dependencies: fix_query_dependencies(sub_dependant)
def get_dependant( *, path: str, call: Callable[..., Any], name: Optional[str] = None, security_scopes: Optional[List[str]] = None, use_cache: bool = True, ) -> Dependant: path_param_names = get_path_param_names(path) endpoint_signature = get_typed_signature(call) signature_params = endpoint_signature.parameters if is_gen_callable(call) or is_async_gen_callable(call): check_dependency_contextmanagers() dependant = Dependant(call=call, name=name, path=path, use_cache=use_cache) for param_name, param in signature_params.items(): if isinstance(param.default, params.Depends): sub_dependant = get_param_sub_dependant( param=param, path=path, security_scopes=security_scopes) dependant.dependencies.append(sub_dependant) continue if add_non_field_param_to_dependency(param=param, dependant=dependant): continue param_field = get_param_field(param=param, default_field_info=params.Query, param_name=param_name) if param_name in path_param_names: assert is_scalar_field( field=param_field ), "Path params must be of one of the supported types" if isinstance(param.default, params.Path): ignore_default = False else: ignore_default = True param_field = get_param_field( param=param, param_name=param_name, default_field_info=params.Path, force_type=params.ParamTypes.path, ignore_default=ignore_default, ) add_param_to_fields(field=param_field, dependant=dependant) elif is_scalar_field(field=param_field): add_param_to_fields(field=param_field, dependant=dependant) elif isinstance( param.default, (params.Query, params.Header)) and is_scalar_sequence_field(param_field): add_param_to_fields(field=param_field, dependant=dependant) else: field_info = param_field.field_info if isinstance(field_info, params.Body): dependant.body_params.append(param_field) elif isinstance(field_info, params.Query): dependant.query_params.append(param_field) return dependant
def get_dependant( *, path: str, call: Callable, name: str = None, security_scopes: List[str] = None, use_cache: bool = True, ) -> Dependant: path_param_names = get_path_param_names(path) endpoint_signature = inspect.signature(call) signature_params = endpoint_signature.parameters dependant = Dependant(call=call, name=name, path=path, use_cache=use_cache) for param_name, param in signature_params.items(): if isinstance(param.default, params.Depends): sub_dependant = get_param_sub_dependant( param=param, path=path, security_scopes=security_scopes) dependant.dependencies.append(sub_dependant) for param_name, param in signature_params.items(): if isinstance(param.default, params.Depends): continue if add_non_field_param_to_dependency(param=param, dependant=dependant): continue param_field = get_param_field(param=param, default_schema=params.Query) if param_name in path_param_names: assert param.default == param.empty or isinstance( param.default, params.Path ), "Path params must have no defaults or use Path(...)" assert is_scalar_field( field=param_field ), f"Path params must be of one of the supported types" param_field = get_param_field( param=param, default_schema=params.Path, force_type=params.ParamTypes.path, ) add_param_to_fields(field=param_field, dependant=dependant) elif is_scalar_field(field=param_field): add_param_to_fields(field=param_field, dependant=dependant) elif isinstance( param.default, (params.Query, params.Header)) and is_scalar_sequence_field(param_field): add_param_to_fields(field=param_field, dependant=dependant) else: assert isinstance( param_field.schema, params.Body ), f"Param: {param_field.name} can only be a request body, using Body(...)" dependant.body_params.append(param_field) return dependant
def get_flat_dependant(dependant: Dependant) -> Dependant: flat_dependant = Dependant( path_params=dependant.path_params.copy(), query_params=dependant.query_params.copy(), header_params=dependant.header_params.copy(), cookie_params=dependant.cookie_params.copy(), body_params=dependant.body_params.copy(), security_schemes=dependant.security_requirements.copy(), ) for sub_dependant in dependant.dependencies: flat_sub = get_flat_dependant(sub_dependant) flat_dependant.path_params.extend(flat_sub.path_params) flat_dependant.query_params.extend(flat_sub.query_params) flat_dependant.header_params.extend(flat_sub.header_params) flat_dependant.cookie_params.extend(flat_sub.cookie_params) flat_dependant.body_params.extend(flat_sub.body_params) flat_dependant.security_requirements.extend(flat_sub.security_requirements) return flat_dependant
def __init__( self, entrypoint: 'Entrypoint', path: str, *, name: str = None, errors: Sequence[Type[BaseError]] = None, common_dependencies: Sequence[Depends] = None, response_class: Type[Response] = JSONResponse, **kwargs, ): name = name or 'entrypoint' _, path_format, _ = compile_path(path) _Request = JsonRpcRequest common_dependant = Dependant(path=path_format) if common_dependencies: insert_dependencies(common_dependant, common_dependencies) fix_query_dependencies(common_dependant) common_dependant = get_flat_dependant(common_dependant, skip_repeats=True) if common_dependant.body_params: _Request = make_request_model(name, entrypoint.callee_module, common_dependant.body_params) # This is only necessary for generating OpenAPI def endpoint(__request__: _Request): del __request__ responses = errors_responses(errors) super().__init__( path, endpoint, methods=['POST'], name=name, response_class=response_class, response_model=JsonRpcResponse, responses=responses, **kwargs, ) flat_dependant = get_flat_dependant(self.dependant, skip_repeats=True) if len(flat_dependant.body_params) > 1: body_params = [p for p in flat_dependant.body_params if p.type_ is not _Request] raise RuntimeError( f"Entrypoint shared dependencies can't use 'Body' parameters: " f"params={body_params}" ) if flat_dependant.query_params: raise RuntimeError( f"Entrypoint shared dependencies can't use 'Query' parameters: " f"params={flat_dependant.query_params}" ) self.shared_dependant = clone_dependant(self.dependant) # No shared 'Body' params, because each JSON-RPC request in batch has own body self.shared_dependant.body_params = [] # Add dependencies and other parameters from common_dependant for correct OpenAPI generation self.dependant.path_params.extend(common_dependant.path_params) self.dependant.header_params.extend(common_dependant.header_params) self.dependant.cookie_params.extend(common_dependant.cookie_params) self.dependant.dependencies.extend(common_dependant.dependencies) self.dependant.security_requirements.extend(common_dependant.security_requirements) self.app = request_response(self.handle_http_request) self.entrypoint = entrypoint self.common_dependencies = common_dependencies
def clone_dependant(dependant: Dependant) -> Dependant: new_dependant = Dependant() new_dependant.path_params = dependant.path_params new_dependant.query_params = dependant.query_params new_dependant.header_params = dependant.header_params new_dependant.cookie_params = dependant.cookie_params new_dependant.body_params = dependant.body_params new_dependant.dependencies = dependant.dependencies new_dependant.security_requirements = dependant.security_requirements new_dependant.request_param_name = dependant.request_param_name new_dependant.websocket_param_name = dependant.websocket_param_name new_dependant.response_param_name = dependant.response_param_name new_dependant.background_tasks_param_name = dependant.background_tasks_param_name new_dependant.security_scopes = dependant.security_scopes new_dependant.security_scopes_param_name = dependant.security_scopes_param_name new_dependant.name = dependant.name new_dependant.call = dependant.call new_dependant.use_cache = dependant.use_cache new_dependant.path = dependant.path new_dependant.cache_key = dependant.cache_key return new_dependant