def _validate_directive_implementation(self) -> List[str]: """ Validates that defined directives provide a proper implementation. :return: a list of errors :rtype: List[str] """ errors = [] for directive in self._directive_definitions.values(): for expected in _IMPLEMENTABLE_DIRECTIVE_FUNCTION_HOOKS: attr = getattr(directive.implementation, expected, None) if attr and not is_valid_coroutine(attr): errors.append( f"Directive {directive.name} Method " f"{expected} is not awaitable." ) for expected in _IMPLEMENTABLE_DIRECTIVE_GENERATOR_HOOKS: attr = getattr(directive.implementation, expected, None) if attr and not is_valid_async_generator(attr): errors.append( f"Directive {directive.name} Method " f"{expected} is not an Async Generator." ) return errors
def get_callables(implementation: Any) -> Dict[str, Callable]: """ Computes a dictionary of all attribute name that starts with `on_` and are linked to a callable. :param implementation: the implementation to parse :type implementation: Any :return: a dictionary of attribute name and callable :rtype: Dict[str, Callable] """ return { key: getattr(implementation, key) for key in dir(implementation) if key.startswith("on_") and is_valid_coroutine(getattr(implementation, key)) }
def __call__(self, implementation: Callable) -> Callable: """ Registers the resolver into the schema. :param implementation: implementation of the resolver :type implementation: Callable :return: the implementation of the resolver :rtype: Callable """ if not is_valid_coroutine(implementation): raise NonAwaitableResolver( f"The resolver `{repr(implementation)}` given is not awaitable." ) if self._type_resolver is not None and not callable( self._type_resolver): raise NonCallable( "The < type_resolver > parameter of the resolver " f"`{repr(implementation)}` has to be a callable.") SchemaRegistry.register_resolver(self._schema_name, self) self._implementation = implementation return implementation
async def cook( self, sdl: Union[str, List[str]] = None, error_coercer: Callable[[Exception, Dict[str, Any]], Dict[str, Any]] = None, custom_default_resolver: Optional[Callable] = None, custom_default_type_resolver: Optional[Callable] = None, modules: Optional[Union[str, List[str], List[Dict[str, Any]]]] = None, schema_name: str = None, ) -> None: """ Cook the tartiflette, basically prepare the engine by binding it to given modules using the schema_name as a key. You wont be able to execute a request if the engine wasn't cooked. :param sdl: path or list of path to the files / directories containing the SDL :param error_coercer: callable in charge of transforming a couple Exception/error into an error dictionary :param custom_default_resolver: callable that will replace the builtin default_resolver (called as resolver for each UNDECORATED field) :param custom_default_type_resolver: callable that will replace the tartiflette `default_type_resolver` (will be called on abstract types to deduct the type of a result) :param modules: list of string containing the name of the modules you want the engine to import, usually this modules contains your Resolvers, Directives, Scalar or Subscription code :param schema_name: name of the SDL :type sdl: Union[str, List[str]] :type error_coercer: Callable[[Exception, Dict[str, Any]], Dict[str, Any]] :type custom_default_resolver: Optional[Callable] :type custom_default_type_resolver: Optional[Callable] :type modules: Optional[Union[str, List[str], List[Dict[str, Any]]]] :type schema_name: str """ if self._cooked: return if modules is None: modules = self._modules or [] if isinstance(modules, str): modules = [modules] sdl = sdl or self._sdl if not sdl: raise Exception("Please provide a SDL") schema_name = schema_name or self._schema_name or "default" custom_error_coercer = error_coercer or self._error_coercer if custom_error_coercer and not is_valid_coroutine( custom_error_coercer): raise NonCoroutine( "Given < error_coercer > is not a coroutine callable.") custom_default_resolver = (custom_default_resolver or self._custom_default_resolver) if custom_default_resolver and not is_valid_coroutine( custom_default_resolver): raise NonCoroutine( "Given < custom_default_resolver > is not a coroutine callable." ) custom_default_type_resolver = (custom_default_type_resolver or self._custom_default_type_resolver) if custom_default_type_resolver and not callable( custom_default_type_resolver): raise NonCallable( "Given < custom_default_type_resolver > is not a coroutine callable." ) self._error_coercer = error_coercer_factory(custom_error_coercer or default_error_coercer) self._modules, modules_sdl = await _import_modules( modules, schema_name) SchemaRegistry.register_sdl(schema_name, sdl, modules_sdl) self._schema = await SchemaBakery.bake(schema_name, custom_default_resolver, custom_default_type_resolver) self._build_response = partial(build_response, error_coercer=self._error_coercer) self._cooked = True
async def cook( self, sdl: Union[str, List[str]] = None, error_coercer: Callable[[Exception, Dict[str, Any]], Dict[str, Any]] = None, custom_default_resolver: Optional[Callable] = None, custom_default_type_resolver: Optional[Callable] = None, modules: Optional[Union[str, List[str], List[Dict[str, Any]]]] = None, query_cache_decorator: Optional[Callable] = UNDEFINED_VALUE, json_loader: Optional[Callable[[str], Dict[str, Any]]] = None, custom_default_arguments_coercer: Optional[Callable] = None, coerce_list_concurrently: Optional[bool] = None, schema_name: Optional[str] = None, ) -> None: """ Cook the tartiflette, basically prepare the engine by binding it to given modules using the schema_name as a key. You wont be able to execute a request if the engine wasn't cooked. :param sdl: path or list of path to the files / directories containing the SDL :param error_coercer: callable in charge of transforming a couple Exception/error into an error dictionary :param custom_default_resolver: callable that will replace the builtin default_resolver (called as resolver for each UNDECORATED field) :param custom_default_type_resolver: callable that will replace the tartiflette `default_type_resolver` (will be called on abstract types to deduct the type of a result) :param modules: list of string containing the name of the modules you want the engine to import, usually this modules contains your Resolvers, Directives, Scalar or Subscription code :param query_cache_decorator: callable that will replace the tartiflette default lru_cache decorator to cache query parsing :param json_loader: A callable that will replace default python json module.loads for ast_json loading :param custom_default_arguments_coercer: callable that will replace the tartiflette `default_arguments_coercer` :param coerce_list_concurrently: whether or not list will be coerced concurrently :param schema_name: name of the SDL :type sdl: Union[str, List[str]] :type error_coercer: Callable[[Exception, Dict[str, Any]], Dict[str, Any]] :type custom_default_resolver: Optional[Callable] :type custom_default_type_resolver: Optional[Callable] :type modules: Optional[Union[str, List[str], List[Dict[str, Any]]]] :type query_cache_decorator: Optional[Callable] :type json_loader: Optional[Callable[[str], Dict[str, Any]]] :type custom_default_arguments_coercer: Optional[Callable] :type coerce_list_concurrently: Optional[bool] :type schema_name: Optional[str] """ # pylint: disable=too-many-arguments,too-many-locals if self._cooked: return if modules is None: modules = self._modules or [] if isinstance(modules, str): modules = [modules] sdl = sdl or self._sdl if not sdl: raise Exception("Please provide a SDL") schema_name = schema_name or self._schema_name or "default" custom_error_coercer = error_coercer or self._error_coercer if custom_error_coercer and not is_valid_coroutine( custom_error_coercer): raise NonCoroutine( "Given < error_coercer > is not a coroutine callable.") custom_default_resolver = (custom_default_resolver or self._custom_default_resolver) if custom_default_resolver and not is_valid_coroutine( custom_default_resolver): raise NonCoroutine( "Given < custom_default_resolver > is not a coroutine callable." ) custom_default_type_resolver = (custom_default_type_resolver or self._custom_default_type_resolver) if custom_default_type_resolver and not callable( custom_default_type_resolver): raise NonCallable( "Given < custom_default_type_resolver > is not a coroutine callable." ) custom_default_arguments_coercer = ( custom_default_arguments_coercer or self._custom_default_arguments_coercer) if custom_default_arguments_coercer and not is_valid_coroutine( custom_default_arguments_coercer): raise NonCoroutine( "Given < custom_default_arguments_coercer > is not a " "coroutine callable.") self._error_coercer = error_coercer_factory(custom_error_coercer or default_error_coercer) self._modules, modules_sdl = await _import_modules( modules, schema_name) SchemaRegistry.register_sdl(schema_name, sdl, modules_sdl) self._schema = await SchemaBakery.bake( schema_name, custom_default_resolver, custom_default_type_resolver, custom_default_arguments_coercer, (coerce_list_concurrently if coerce_list_concurrently is not None else self._coerce_list_concurrently), ) self._build_response = partial(build_response, error_coercer=self._error_coercer) ( self._query_executor, self._subscription_executor, ) = self._schema.bake_execute(self._perform_query, self._perform_subscription) if query_cache_decorator is UNDEFINED_VALUE: query_cache_decorator = self._query_cache_decorator self._cached_parse_and_validate_query = ( query_cache_decorator(parse_and_validate_query) if callable(query_cache_decorator) else parse_and_validate_query) self._schema.json_loader = json_loader or self._json_loader self._cooked = True