Exemplo n.º 1
0
 def init_injector(self, components=None):
     app_components = list(WSGI_COMPONENTS + VALIDATION_COMPONENTS)
     for comp in (components or []):
         if isinstance(comp, str):
             comp = import_path(comp, ["components", "COMPONENTS"])
         if isinstance(comp, Component):
             app_components.append(comp)
         elif isinstance(comp, (list, tuple)):
             for c in comp:
                 if not isinstance(c, Component):
                     msg = "Could not load component %r"
                     raise exceptions.ConfigurationError(msg % c)
             app_components += list(comp)
         else:
             msg = "Could not load component %r"
             raise exceptions.ConfigurationError(msg % comp)
     initial_components = {
         'environ': WSGIEnviron,
         'start_response': WSGIStartResponse,
         'exc': Exception,
         'app': App,
         'path_params': PathParams,
         'route': Route,
         'response': Response,
         'settings': Settings
     }
     self.injector = Injector(app_components, initial_components)
Exemplo n.º 2
0
 def __init_subclass__(cls, **kwargs):
     if cls.singleton:
         if cls.can_handle_parameter is not Component.can_handle_parameter:
             msg = (
                 'Component "%s" should not override `can_handle_parameter`, '
                 'since it is a singleton')
             raise exceptions.ConfigurationError(msg % cls.__name__)
Exemplo n.º 3
0
    def generate_fields(self, url, method, handler):
        fields = []
        path_names = [
            item.strip("{}").lstrip("+")
            for item in re.findall("{[^}]*}", url)
        ]
        body_params = []
        parameters = self.injector.resolve_validation_parameters(handler)
        for name, param in parameters.items():
            if name in path_names:
                fields.append(self.generate_path_field(param))
            elif is_schema(param.annotation):
                if method in ("GET", "DELETE"):
                    fields += self.generate_query_fields_from_schema(param)
                else:
                    fields.append(
                        document.Field(name=name,
                                       location="body",
                                       schema=param.annotation))
                    body_params.append(param)
            else:
                fields += self.generate_query_fields(param)

        if len(body_params) > 1:
            params = "\n  ".join(f"{x.name}: {x.annotation.__name__}"
                                 for x in body_params)
            msg = (f"\n\nUsing multiple body fields in {method} handler "
                   f"`{handler.__module__}.{handler.__name__}` is confusing.\n"
                   f"Use only one of the following parameters:\n  {params}\n")
            raise exceptions.ConfigurationError(msg)
        return fields
Exemplo n.º 4
0
 def resolve_validation_parameters(self,
                                   func):
     unique = {}
     for holder, param in self._resolve_validation_parameters(func, set()):
         if param.name in unique:
             cur_holder, cur_param = unique[param.name]
             if cur_param != param:
                 msg = "\nConflicting parameters:\n%s( .. %s ..)\nand\n%s ( .. %s ..)"
                 raise exceptions.ConfigurationError(msg % (cur_holder, cur_param, holder, param))
             elif not cur_param.description and param.description:
                 unique[param.name] = (holder, param)
         else:
             unique[param.name] = (holder, param)
     return {k: v[1] for k, v in unique.items()}
Exemplo n.º 5
0
 def __init__(self, mod):
     tuple_settings = [
         "COMPONENTS",
         "TEMPLATE_DIRS",
         "STATIC_DIRS",
     ]
     for setting in dir(mod):
         if setting.isupper():
             setting_value = getattr(mod, setting)
             if (setting in tuple_settings
                     and not isinstance(setting_value, (list, tuple))):
                 msg = f"The {setting} setting must be a list or a tuple."
                 raise exceptions.ConfigurationError(msg)
             setattr(self, setting, setting_value)
Exemplo n.º 6
0
 def _resolve_validation_parameters(self,
                                    func,
                                    seen_state):
     parameters = []
     signature = inspect.signature(func)
     for parameter in signature.parameters.values():
         if (parameter.annotation in (ReturnValue, inspect.Parameter)
                 or parameter.annotation in self.reverse_initial):
             continue
         for component in self.components:
             if component.can_handle_parameter(parameter):
                 identity = component.identity(parameter)
                 if identity not in seen_state:
                     seen_state.add(identity)
                     params = component.get_validation_parameters(func, parameter)
                     parameters += [(func, Parameter.from_obj(p)) for p in params]
                     parameters += self._resolve_validation_parameters(component.resolve, seen_state)
                 break
         else:
             msg = 'No component able to handle parameter "%s" on function "%s".'
             raise exceptions.ConfigurationError(msg % (parameter.name, func.__qualname__))
     return parameters
Exemplo n.º 7
0
 def can_handle_parameter(self, parameter: inspect.Parameter):
     # Return `True` if this component can handle the given parameter.
     #
     # The default behavior is for components to handle whatever class
     # is used as the return annotation by the `resolve` method.
     #
     # You can override this for more customized styles, for example if you
     # wanted name-based parameter resolution, or if you want to provide
     # a value for a range of different types.
     #
     # Eg. Include the `Request` instance for any parameter named `request`.
     if inspect.isclass(self.resolve):
         return_annotation = self.resolve
     else:
         return_annotation = inspect.signature(
             self.resolve).return_annotation
     if return_annotation is inspect.Signature.empty:
         msg = ('Component "%s" must include a return annotation on the '
                '`resolve()` method, or override `can_handle_parameter`.'
                ) % self.__class__.__name__
         raise exceptions.ConfigurationError(msg)
     return parameter.annotation is return_annotation
Exemplo n.º 8
0
    def resolve_function(self,
                         func,
                         seen_state,
                         output_name=None,
                         parent_parameter=None,
                         set_return=False):

        steps = []
        kwargs = {}
        consts = {}

        signature = inspect.signature(func)

        if output_name is None:
            if inspect.isclass(func):
                return_annotation = func
            else:
                return_annotation = signature.return_annotation
            if return_annotation in self.reverse_initial:
                # some functions can override initial state
                output_name = self.reverse_initial[return_annotation]
            else:
                output_name = 'return_value'

        for parameter in signature.parameters.values():
            if parameter.annotation is ReturnValue:
                kwargs[parameter.name] = 'return_value'
                continue

            # Check if the parameter class exists in 'initial'.
            if parameter.annotation in self.reverse_initial:
                initial_kwarg = self.reverse_initial[parameter.annotation]
                kwargs[parameter.name] = initial_kwarg
                continue

            # The 'Parameter' annotation can be used to get the parameter
            # itself. Used for example in 'Header' components that need the
            # parameter name in order to lookup a particular value.
            if parameter.annotation is inspect.Parameter:
                consts[parameter.name] = parent_parameter
                continue

            # Otherwise, find a component to resolve the parameter.
            for component in self.components:
                if component.can_handle_parameter(parameter):
                    if component in self.singletons:
                        consts[parameter.name] = self.singletons[component]
                    else:
                        identity = component.identity(parameter)
                        kwargs[parameter.name] = identity
                        if identity not in seen_state:
                            seen_state.add(identity)
                            resolved_steps = self.resolve_function(
                                component.resolve,
                                seen_state,
                                output_name=identity,
                                parent_parameter=parameter
                            )
                            steps += resolved_steps
                            if getattr(component, 'singleton', False):
                                steps.append(self.resolve_singleton(component, identity))
                    break
            else:
                msg = 'No component able to handle parameter "%s" on function "%s".'
                raise exceptions.ConfigurationError(msg % (parameter.name, func.__qualname__))

        is_async = asyncio.iscoroutinefunction(func)
        if is_async and not self.allow_async:
            msg = 'Function "%s" may not be async.'
            raise exceptions.ConfigurationError(msg % (func.__qualname__, ))

        step = (func, is_async, kwargs, consts, output_name, set_return)
        steps.append(step)

        return steps