Exemple #1
0
 def unset(self, of: t.Union[_T_Enum, t.Type[_T]]) -> None:
     preconditions.check_instance_of(of, (type, enum.Enum))
     if isinstance(of, enum.Enum):
         self._enums.get(type(of), set()).discard(of)
     else:
         assert isinstance(of, type)
         self._objects.pop(of, None)
Exemple #2
0
    def apply(
        self,
        plugin_name: t.Optional[str] = None,
        from_project: t.Union[None, str, 'Project'] = None,
        merge: bool = True,
    ) -> Namespace:
        """
    Loads a plugin and applies it to the project. Plugins are loaded via #Context.plugin_loader and applied to the
    project immediately after. The default implementation for loading plugins uses Python package entrypoints
    as configured in the #Context.

    Returns the extension namespace created for the plugin. The extension is also merged into the #Project.ext
    namespace unless the *merge* parameter is set to #False.
    """

        if plugin_name is not None:
            if from_project is not None:
                raise TypeError(
                    'plugin_name and from_project should not be specified at the same time'
                )
            check_instance_of(plugin_name, str, 'plugin_name')
            plugin = self.context.plugin_loader.load_plugin(plugin_name)
            plugin.apply(self)

        elif from_project is not None:
            from_project = self.subproject(from_project) if isinstance(
                from_project, str) else from_project
            check_instance_of(from_project, (str, Project), 'from_project')
            if not from_project._on_apply:
                raise ValueError(f'{from_project} has no on_apply handler')
            from_project._on_apply(self)
            from_project.exports.merge_into(self.extensions)

        else:
            raise TypeError('need plugin_name or from_project')
Exemple #3
0
 def do_last(self, action: t.Union['Action', Closure]) -> None:
     from craftr.core.actions import Action, LambdaAction
     check_instance_of(action, (Action, Closure), 'action')
     if isinstance(action, Closure):
         closure = action
         action = LambdaAction(
             lambda context: closure(self, context).apply(self))
     self.do_last_actions.append(action)
Exemple #4
0
 def get(self, of):
     preconditions.check_instance_of(of, (type, enum.Enum))
     if isinstance(of, enum.Enum):
         return of if of in self._enums.get(type(of), set()) else None
     result = self._objects.get(of)
     if result is None and self._parent is not None:
         result = self._parent.get(of)
     return result
Exemple #5
0
 def get_converters(self, type_: BaseType,
                    direction: Direction) -> t.Iterable[Converter]:
     preconditions.check_instance_of(type_, BaseType)  # type: ignore
     if isinstance(type_, ConcreteType
                   ) and type_.type in self.__converters_by_type[direction]:
         yield self.__converters_by_type[direction][type_.type]
     elif type(type_) in self.__converters_by_type[direction]:
         yield self.__converters_by_type[direction][type(type_)]
     for module in reversed(self.__converter_providers):
         yield from module.get_converters(type_, direction)
Exemple #6
0
def dataclass_to_schema(
        dataclass_type: t.Type,
        type_hint_adapter: t.Optional[TypeHintAdapter] = None) -> Schema:
    """
  Converts the given *dataclass_type* to a #Schema. The dataclass fields are converted to #BaseType#s
  via the given *type_hint_adapter*. If no adapter is specified, the #DefaultTypeHintAdapter will be
  used (note that this adapter does _not_ expand #ConcreteType#s of dataclasses to #ObjectType#s).
  """

    preconditions.check_instance_of(dataclass_type, type)
    preconditions.check_argument(is_dataclass(dataclass_type),
                                 'expected @dataclass type')

    if type_hint_adapter is None:
        type_hint_adapter = DefaultTypeHintAdapter()

    fields: t.Dict[str, Field] = {}
    annotations = get_type_hints(dataclass_type)
    resolver = ModuleForwardReferenceResolver(
        sys.modules[dataclass_type.__module__])

    for field in _get_fields(dataclass_type):
        if not field.init:
            # If we cannot initialize the field in the constructor, we should also
            # exclude it from the definition of the type for de-/serializing.
            continue

        # NOTE (NiklasRosenstein): We do not use #field.type because if it contains a #t.ForwardRef,
        #   it will not be resolved and we can't convert that to our type representation.
        field_type_hint = type_hint_adapter.adapt_type_hint(
            annotations[field.name], None, resolver)
        field_annotations = list(
            field.metadata.get(ANNOTATIONS_METADATA_KEY, []))

        # Handle field(metadata={'alias': ...}). The value can be a string or list of strings.
        if not any(isinstance(x, A.alias) for x in field_annotations):
            if 'alias' in field.metadata:
                aliases = field.metadata['alias']
                if isinstance(aliases, str):
                    aliases = [aliases]
                field_annotations.append(A.alias(*aliases))

        field_default_factory = field.default_factory  # type: ignore
        fields[field.name] = Field(
            field.name, field_type_hint, field_annotations,
            NotSet.Value if field.default == _MISSING else field.default,
            NotSet.Value
            if field_default_factory == _MISSING else field_default_factory)

    return Schema(
        dataclass_type.__name__,
        fields,
        list(A.get_type_annotations(dataclass_type).values()),
        dataclass_type,
    )
Exemple #7
0
    def depends_on(self, *tasks: t.Union[str, 'Task']) -> None:
        """
    Specify that the task dependends on the specified other tasks. Strings are resolved from the tasks own project.
    """

        for index, item in enumerate(tasks):
            check_instance_of(item, (str, Task), lambda: 'task ' + str(index))
            if isinstance(item, str):
                self.dependencies += self.project.tasks.resolve(item)
            elif isinstance(item, Task):
                self.dependencies.append(item)
Exemple #8
0
 def __init__(
     self,
     func: t.Callable[[t.Optional[U], t.Optional[V]], T],
     left: Provider[U],
     right: Provider[V],
 ) -> None:
     check_instance_of(left, Provider)  # type: ignore  # python/mypy#5374
     check_instance_of(right, Provider)  # type: ignore  # python/mypy#5374
     self._func = func
     self._left = left
     self._right = right
Exemple #9
0
    def depends_on(self, *tasks: t.Union[str, Task]) -> None:
        """
    Specify that the task dependends on the specified other tasks. Strings are resolved from the tasks own project.
    """

        if self.finalized:
            raise RuntimeError(f'Task is finalized, cannot add dependency')

        for index, item in enumerate(tasks):
            check_instance_of(item, (str, Task), lambda: 'task ' + str(index))
            if isinstance(item, str):
                self.dependencies += self.project.tasks.resolve(item)
            elif isinstance(item, Task):
                self.dependencies.append(item)
Exemple #10
0
 def convert(self,
   direction: Direction,
   value: t.Any,
   type_hint: t.Union[BaseType, t.Type[T]],
   filename: t.Optional[str] = None,
   position: t.Optional[Position] = None,
   key: t.Union[str, int, None] = None,
   annotations: t.Optional[t.List[t.Any]] = None,
   settings: t.Optional[t.List[t.Any]] = None,
 ) -> T:
   preconditions.check_instance_of(direction, Direction)
   type_ = self.adapt_type_hint(type_hint)
   field = Field('$', type_, annotations or [])
   loc = Location(None, type_, key, filename, position)
   ctx = Context(None, self, self, self,
     Settings(*(settings or []), parent=self.settings), direction, value, loc, field)
   return ctx.convert()
Exemple #11
0
    def convert(self, ctx: Context) -> t.Any:
        preconditions.check_instance_of(ctx.type, ConcreteType)
        preconditions.check_argument(
            t.cast(ConcreteType, ctx.type).type is decimal.Decimal,
            'must be Decimal')
        context = Optional(ctx.get_annotation(A.precision))\
          .map(lambda b: b.to_context()).or_else(None)
        fieldinfo = ctx.get_annotation(A.fieldinfo) or A.fieldinfo()

        if ctx.direction == Direction.deserialize:
            if (not fieldinfo.strict
                    and isinstance(ctx.value, (int, float))) or isinstance(
                        ctx.value, str):
                return decimal.Decimal(ctx.value, context)
            raise ctx.type_error(expected='str')

        else:
            if not isinstance(ctx.value, decimal.Decimal):
                raise ctx.type_error(expected=decimal.Decimal)
            return str(ctx.value)
Exemple #12
0
    def create_instance(self,
                        type: t.Type[T],
                        fqn: str,
                        config_key: str = '<notset>') -> T:
        """
    Creates an instance of the specified type from a fully qualified name. If the class implements
    the #LoadableFromSettings protocol, the method will be called to create an instance.
    """

        class_ = load_class(fqn)
        try:
            if issubclass(class_, LoadableFromSettings):
                instance = class_.from_settings(self)
            else:
                instance = class_()
        except Exception:
            raise ClassInstantiationError(
                f'Error while instantiating instance of `{class_.__module__}.{class_.__qualname__}` '
                f'from configuration key `{config_key}`')
        check_instance_of(instance, type)
        return instance
Exemple #13
0
    def convert(self, ctx: Context) -> t.Any:
        preconditions.check_instance_of(ctx.type, ConcreteType)
        type_ = t.cast(ConcreteType, ctx.type).type
        datefmt = ctx.get_annotation(A.datefmt) or (
            self.DEFAULT_DATE_FMT if type_ == datetime.date else
            self.DEFAULT_TIME_FMT if type_ == datetime.time else
            self.DEFAULT_DATETIME_FMT if type_ == datetime.datetime else None)
        assert datefmt is not None

        if ctx.direction == Direction.deserialize:
            if isinstance(ctx.value, type_):
                return ctx.value
            elif isinstance(ctx.value, str):
                dt = datefmt.parse(
                    type_, ctx.value
                )  # TODO(NiklasRosenstein): Rethrow as ConversionError
                assert isinstance(dt, type_)
                return dt
            raise ctx.type_error(expected=f'str|{type_.__name__}')

        else:
            if not isinstance(ctx.value, type_):
                raise ctx.type_error(expected=type_)
            return datefmt.format(ctx.value)
Exemple #14
0
    def subtype(extends: t.Type,
                name: str = None) -> t.Callable[[T_Type], T_Type]:
        """
    Decorator for subtypes of the #@union-decorated type *extends*. The *extends* class must
    use #union.Subtypes.Dynamic. If a *name* is specified, the class will also be decorated
    with the #typeinfo annotation.

    The decorated class _must_ be a subclass of the *extends* class, otherwise a #TypeError is
    raised.

    Example:

    ```py
    @dataclass
    @union.subtype(Person)
    class Student(Person):
      courses: t.Set[str]
    ```
    """

        preconditions.check_instance_of(extends, type)
        inst = preconditions.check_not_none(
            get_annotation(extends, union, None),
            lambda: f'{extends.__name__} is not annotated with @union')
        subtypes = preconditions.check_instance_of(
            inst.subtypes, DynamicSubtypes,
            lambda: f'{extends.__name__} is not using union.Subtypes.Dynamic')

        def decorator(subtype: T_Type) -> T_Type:
            preconditions.check_subclass_of(subtype, extends)
            if name is not None:
                subtype = typeinfo(name)(subtype)
            subtypes.add_type(typeinfo.get_name(subtype), subtype)
            return subtype

        return decorator
Exemple #15
0
 def convert(self, ctx: Context) -> t.Any:
     source_type = type(ctx.value)
     target_type = preconditions.check_instance_of(ctx.location.type,
                                                   ConcreteType).type
     fieldinfo = ctx.get_annotation(A.fieldinfo) or A.fieldinfo()
     strict = ctx.direction == Direction.serialize or fieldinfo.strict
     func = (self._strict_adapters if strict else self._nonstrict_adapters)\
         .get((source_type, target_type))
     if func is None:
         raise ctx.error(
             f'unable to {ctx.direction.name} {source_type.__name__} -> {target_type.__name__}'
         )
     try:
         return func(ctx.value)
     except ValueError as exc:
         raise ctx.error(str(exc))
Exemple #16
0
 def add_converter_for_type(self,
                            type_: t.Type,
                            converter: Converter,
                            direction: Direction = None) -> None:
     preconditions.check_instance_of(type_, type)
     preconditions.check_instance_of(converter, Converter)  # type: ignore
     if direction is not None:
         preconditions.check_instance_of(direction, Direction)
         self.__converters_by_type[direction][type_] = converter
     else:
         self.__converters_by_type[Direction.deserialize][type_] = converter
         self.__converters_by_type[Direction.serialize][type_] = converter
Exemple #17
0
 def add_converter_provider(self, provider: ConverterProvider) -> None:
     preconditions.check_instance_of(provider,
                                     ConverterProvider)  # type: ignore
     self.__converter_providers.append(provider)
Exemple #18
0
 def __init__(self, func: t.Callable[[t.Optional[U]], t.Optional[T]],
              inner: Provider[U]) -> None:
     check_instance_of(inner, Provider)  # type: ignore  # python/mypy#5374
     self._func = func
     self._inner = inner