示例#1
0
    def prepare_item(self, new_item: Any) -> Any:
        """
        This method when an item in this collection is mutated in the
        `mutate_value` method. It:
        (1) Transforms the new item under the attribute-specific item-preparer.
        (2) Automatically casts stand-alone spec-class keys to a spec-class
            instance with that key.

        Args:
            new_item: The incoming item.

        Returns:
            The transformed item ready for further mutation/transformation.

        Note: This is called *after* we know which base value we are using
        but before we fallback to constructors and/or apply passed attributes
        and transforms.
        """
        if self.attr_spec.prepare_item:
            new_item = self.attr_spec.prepare_item(self.instance, new_item)
        if (  # Convert to spec-class if key was provided.
            self.attr_spec.item_spec_key_type
            and new_item is not MISSING
            and not check_type(new_item, self.attr_spec.item_type)
            and check_type(new_item, self.attr_spec.item_spec_key_type)
        ):
            new_item = self.attr_spec.item_spec_type(new_item)
        return new_item
示例#2
0
 def _validate_item(self, item: ItemType) -> Tuple[ItemType, KeyType]:
     key = self.key(item)
     if hasattr(self._type, "__args__"):
         item_type, key_type = self._type.__args__
         if not check_type(item, item_type):
             raise TypeError(
                 f"Invalid item type. Got: `{repr(item)}`; Expected instance of: `{type_label(item_type)}`."
             )
         if not check_type(key, key_type):
             raise TypeError(
                 f"Invalid key type. Got: `{repr(key)}`; Expected instance of: `{type_label(key_type)}`."
             )
     return item, key
示例#3
0
    def _extractor(  # pylint: disable=arguments-differ
        self,
        value_or_index,
        raise_if_missing=False,
        by_index=MISSING,
    ) -> IndexedItem:
        if self.collection is MISSING or value_or_index is MISSING:
            return None, MISSING

        if by_index is MISSING:
            by_index = not check_type(value_or_index, self.attr_spec.item_type)

        if by_index:
            try:
                return (value_or_index, self.collection[value_or_index])
            except IndexError:
                if raise_if_missing:
                    raise IndexError(
                        f"Index `{repr(value_or_index)}` not found in collection `{self.attr_spec.qualified_name}`."
                    ) from None
                return (value_or_index, MISSING)

        try:
            value_index = self.collection.index(value_or_index)
        except ValueError:
            value_index = None

        if raise_if_missing and value_index is None:
            raise ValueError(
                f"Item `{repr(value_or_index)}` not found in collection `{self.attr_spec.qualified_name}`."
            )
        return (value_index, value_or_index)
示例#4
0
 def add_items(self, items: Iterable):
     if not check_type(items, Iterable):
         raise TypeError(
             f"Incoming collection for `{self.attr_spec.qualified_name}` is not iterable."
         )
     for item in items:
         self.add_item(item)
     return self
示例#5
0
 def _inserter(self, index, item, replace=True):  # pylint: disable=arguments-differ
     if not check_type(item, self.attr_spec.item_type):
         raise ValueError(
             f"Attempted to add an invalid item `{repr(item)}` to `{self.attr_spec.qualified_name}`. Expected item of type `{type_label(self.attr_spec.item_type)}`."
         )
     if index and replace:
         self.collection.discard(index)
     self.collection.add(item)
示例#6
0
 def add_items(self, items: Mapping):
     if not check_type(items, Mapping):
         raise TypeError(
             f"Incoming collection for `{self.attr_spec.qualified_name}` is not a mapping."
         )
     for k, v in items.items():
         self.add_item(k, v)
     return self
示例#7
0
 def _inserter(self, index, item, insert=False):  # pylint: disable=arguments-differ
     if not check_type(item, self.attr_spec.item_type):
         raise ValueError(
             f"Attempted to add an invalid item `{repr(item)}` to `{self.attr_spec.qualified_name}`. Expected item of type `{type_label(self.attr_spec.item_type)}`."
         )
     if index is None:
         self.collection.append(item)
     elif insert:
         self.collection.insert(index, item)
     else:
         self.collection[index] = item
示例#8
0
 def prepare(self):
     if self.collection in (MISSING, None):
         self.collection = self._create_collection()
     if not check_type(self.collection, self.attr_spec.type):
         items = self.collection
         self.collection = self._create_collection()
         self.add_items(items)
         return self
     if self.collection and self.prepare_item:
         self._prepare_items()
     return self
示例#9
0
    def __spec_class_check_type__(cls, instance: Any, type_: Type) -> bool:
        """
        Returns `True` if this instance is a valid instance of `type_` and this
        class.
        """
        if not isinstance(instance, cls):
            return False

        if hasattr(type_, "__args__"):
            item_type, key_type = type_.__args__

            for (
                    key,
                    value,
            ) in instance._dict.items():  # pylint: disable=protected-access
                if not check_type(key, key_type):
                    return False
                if not check_type(value, item_type):
                    return False
        return True
示例#10
0
 def validator(obj):
     if not check_type(obj, numeric_type):
         return False
     if ge is not None and obj < ge:
         return False
     if gt is not None and obj <= gt:
         return False
     if le is not None and obj > le:
         return False
     if lt is not None and obj >= lt:
         return False
     return True
示例#11
0
    def __get__(self, instance, owner=None):
        # If lookup occuring on owner class.
        if instance is None:
            return self

        # If value exists in cache or has been overridden
        if (self.overridable
                or self.cache) and self.attr_name in instance.__dict__:
            return instance.__dict__[self.attr_name]

        # If there is no assigned getter
        if self.fget is None:
            raise AttributeError(
                f"Property override for `{self._qualified_name}` does not have a getter method."
            )

        # Get value from getter
        try:
            value = self.fget(instance)
        except AttributeError as e:
            if self.allow_attribute_error:
                raise
            raise NestedAttributeError(e) from e

        # If attribute is annotated with a `spec_class` type, apply any
        # transforms using `_prepare_foo()` methods, and then check that the
        # attribute type is correct.
        spec_metadata = getattr(instance, "__spec_class__", None)
        if spec_metadata and self.attr_name in spec_metadata.attrs:
            attr_spec = spec_metadata.attrs[self.attr_name]
            value = prepare_attr_value(attr_spec, instance, value)
            if not check_type(value, attr_spec.type):
                raise ValueError(
                    f"Property override for `{owner.__name__ if owner else ''}.{self.attr_name or ''}` returned an invalid type [got `{repr(value)}`; expecting `{type_label(attr_spec.type)}`]."
                )

        # Store value in cache is cache is enabled
        if self.cache:
            instance.__dict__[self.attr_name] = value

        return value
    def test_type_checking(self):

        assert check_type("string", str)
        assert check_type([], list)

        assert check_type([], List)
        assert not check_type("a", List)
        assert check_type(["a", "b"], List[str])
        assert not check_type([1, 2], List[str])

        assert check_type({}, Dict)
        assert not check_type("a", Dict)
        assert check_type({"a": 1, "b": 2}, Dict[str, int])
        assert not check_type({"a": "1", "b": "2"}, Dict[str, int])
        assert not check_type({1: "a", 2: "b"}, Dict[str, int])

        assert check_type(set(), Set)
        assert not check_type("a", Set)
        assert check_type({"a", "b"}, Set[str])
        assert not check_type({1, 2}, Set[str])

        assert check_type(lambda x: x, Callable)

        assert check_type([1, "a"], List[Union[str, int]])
示例#13
0
 def _inserter(self, index, item):
     if not check_type(item, self.attr_spec.item_type):
         raise ValueError(
             f"Attempted to add an invalid item `{repr(item)}` to `{self.attr_spec.qualified_name}`. Expected item of type `{type_label(self.attr_spec.item_type)}`."
         )
     self.collection[index] = item