Esempio n. 1
0
 def __hash__(self):
     if self._box_config['frozen_box']:
         hashing = 54321
         for item in self.items():
             hashing ^= hash(item)
         return hashing
     raise BoxTypeError('unhashable type: "Box"')
Esempio n. 2
0
 def __getitem__(self, item, _ignore_default=False):
     try:
         return super().__getitem__(item)
     except KeyError as err:
         if item == "_box_config":
             cause = _exception_cause(err)
             raise BoxKeyError(
                 "_box_config should only exist as an attribute and is never defaulted"
             ) from cause
         if self._box_config["box_dots"] and isinstance(
                 item, str) and ("." in item or "[" in item):
             try:
                 first_item, children = _parse_box_dots(self, item)
             except BoxError:
                 if self._box_config["default_box"] and not _ignore_default:
                     return self.__get_default(item)
                 raise BoxKeyError(str(item)) from _exception_cause(err)
             if first_item in self.keys():
                 if hasattr(self[first_item], "__getitem__"):
                     return self[first_item][children]
         if self._box_config["camel_killer_box"] and isinstance(item, str):
             converted = _camel_killer(item)
             if converted in self.keys():
                 return super().__getitem__(converted)
         if self._box_config["default_box"] and not _ignore_default:
             return self.__get_default(item)
         raise BoxKeyError(str(err)) from _exception_cause(err)
     except TypeError as err:
         if isinstance(item, slice):
             new_box = self._box_config["box_class"](**self.__box_config())
             for x in list(super().keys())[item.start:item.stop:item.step]:
                 new_box[x] = self[x]
             return new_box
         raise BoxTypeError(str(err)) from _exception_cause(err)
Esempio n. 3
0
 def __add__(self, other: dict):
     new_box = self.copy()
     if not isinstance(other, dict):
         raise BoxTypeError(
             f'Box can only merge two boxes or a box and a dictionary.')
     new_box.merge_update(other)
     return new_box
Esempio n. 4
0
 def __ror__(self, other: dict):
     if not isinstance(other, dict):
         raise BoxTypeError(
             f"Box can only merge two boxes or a box and a dictionary.")
     new_box = self.copy()
     new_box.update(other)
     return new_box
Esempio n. 5
0
 def __radd__(self, other: Mapping[Any, Any]):
     if not isinstance(other, dict):
         raise BoxTypeError(
             "Box can only merge two boxes or a box and a dictionary.")
     new_box = self.copy()
     new_box.merge_update(other)
     return new_box
Esempio n. 6
0
 def update(self, *args, **kwargs):
     if self._box_config["frozen_box"]:
         raise BoxError("Box is frozen")
     if (len(args) + int(bool(kwargs))) > 1:
         raise BoxTypeError(
             f"update expected at most 1 argument, got {len(args) + int(bool(kwargs))}"
         )
     single_arg = next(iter(args), None)
     if single_arg:
         if hasattr(single_arg, "keys"):
             for k in single_arg:
                 self.__convert_and_store(k, single_arg[k])
         else:
             for k, v in single_arg:
                 self.__convert_and_store(k, v)
     for k in kwargs:
         self.__convert_and_store(k, kwargs[k])
Esempio n. 7
0
    def merge_update(self, *args, **kwargs):
        merge_type = None
        if "box_merge_lists" in kwargs:
            merge_type = kwargs.pop("box_merge_lists")

        def convert_and_set(k, v):
            intact_type = self._box_config["box_intact_types"] and isinstance(
                v, self._box_config["box_intact_types"])
            if isinstance(v, dict) and not intact_type:
                # Box objects must be created in case they are already
                # in the `converted` box_config set
                v = self._box_config["box_class"](v, **self.__box_config())
                if k in self and isinstance(self[k], dict):
                    self[k].merge_update(v)
                    return
            if isinstance(v, list) and not intact_type:
                v = box.BoxList(v, **self.__box_config())
                if merge_type == "extend" and k in self and isinstance(
                        self[k], list):
                    self[k].extend(v)
                    return
                if merge_type == "unique" and k in self and isinstance(
                        self[k], list):
                    for item in v:
                        if item not in self[k]:
                            self[k].append(item)
                    return
            self.__setitem__(k, v)

        if (len(args) + int(bool(kwargs))) > 1:
            raise BoxTypeError(
                f"merge_update expected at most 1 argument, got {len(args) + int(bool(kwargs))}"
            )
        single_arg = next(iter(args), None)
        if single_arg:
            if hasattr(single_arg, "keys"):
                for k in single_arg:
                    convert_and_set(k, single_arg[k])
            else:
                for k, v in single_arg:
                    convert_and_set(k, v)

        for key in kwargs:
            convert_and_set(key, kwargs[key])
Esempio n. 8
0
    def __init__(self,
                 *args: Any,
                 default_box: bool = False,
                 default_box_attr: Any = NO_DEFAULT,
                 default_box_none_transform: bool = True,
                 frozen_box: bool = False,
                 camel_killer_box: bool = False,
                 conversion_box: bool = True,
                 modify_tuples_box: bool = False,
                 box_safe_prefix: str = 'x',
                 box_duplicates: str = 'ignore',
                 box_intact_types: Union[Tuple, List] = (),
                 box_recast: Dict = None,
                 box_dots: bool = False,
                 **kwargs: Any):
        super(Box, self).__init__()
        self._box_config = _get_box_config()
        self._box_config.update({
            'default_box':
            default_box,
            'default_box_attr':
            self.__class__
            if default_box_attr is NO_DEFAULT else default_box_attr,
            'default_box_none_transform':
            default_box_none_transform,
            'conversion_box':
            conversion_box,
            'box_safe_prefix':
            box_safe_prefix,
            'frozen_box':
            frozen_box,
            'camel_killer_box':
            camel_killer_box,
            'modify_tuples_box':
            modify_tuples_box,
            'box_duplicates':
            box_duplicates,
            'box_intact_types':
            tuple(box_intact_types),
            'box_recast':
            box_recast,
            'box_dots':
            box_dots
        })
        if not self._box_config['conversion_box'] and self._box_config[
                'box_duplicates'] != 'ignore':
            raise BoxError('box_duplicates are only for conversion_boxes')
        if len(args) == 1:
            if isinstance(args[0], str):
                raise BoxValueError('Cannot extrapolate Box from string')
            if isinstance(args[0], Mapping):
                for k, v in args[0].items():
                    if v is args[0]:
                        v = self
                    if v is None and self._box_config[
                            'default_box'] and self._box_config[
                                'default_box_none_transform']:
                        continue
                    self.__setitem__(k, v)
            elif isinstance(args[0], Iterable):
                for k, v in args[0]:
                    self.__setitem__(k, v)
            else:
                raise BoxValueError(
                    'First argument must be mapping or iterable')
        elif args:
            raise BoxTypeError(
                f'Box expected at most 1 argument, got {len(args)}')

        for k, v in kwargs.items():
            if args and isinstance(args[0], Mapping) and v is args[0]:
                v = self
            self.__setitem__(k, v)

        self._box_config['__created'] = True
Esempio n. 9
0
 def __iadd__(self, other: dict):
     if not isinstance(other, dict):
         raise BoxTypeError(
             f"Box can only merge two boxes or a box and a dictionary.")
     self.merge_update(other)
     return self
Esempio n. 10
0
    def __init__(
        self,
        *args: Any,
        default_box: bool = False,
        default_box_attr: Any = NO_DEFAULT,
        default_box_none_transform: bool = True,
        frozen_box: bool = False,
        camel_killer_box: bool = False,
        conversion_box: bool = True,
        modify_tuples_box: bool = False,
        box_safe_prefix: str = "x",
        box_duplicates: str = "ignore",
        box_intact_types: Union[Tuple, List] = (),
        box_recast: Dict = None,
        box_dots: bool = False,
        box_class: Union[Dict, "Box"] = None,
        **kwargs: Any,
    ):
        super().__init__()
        self._box_config = _get_box_config()
        self._box_config.update({
            "default_box":
            default_box,
            "default_box_attr":
            self.__class__
            if default_box_attr is NO_DEFAULT else default_box_attr,
            "default_box_none_transform":
            default_box_none_transform,
            "conversion_box":
            conversion_box,
            "box_safe_prefix":
            box_safe_prefix,
            "frozen_box":
            frozen_box,
            "camel_killer_box":
            camel_killer_box,
            "modify_tuples_box":
            modify_tuples_box,
            "box_duplicates":
            box_duplicates,
            "box_intact_types":
            tuple(box_intact_types),
            "box_recast":
            box_recast,
            "box_dots":
            box_dots,
            "box_class":
            box_class if box_class is not None else self.__class__,
        })
        if not self._box_config["conversion_box"] and self._box_config[
                "box_duplicates"] != "ignore":
            raise BoxError("box_duplicates are only for conversion_boxes")
        if len(args) == 1:
            if isinstance(args[0], str):
                raise BoxValueError("Cannot extrapolate Box from string")
            if isinstance(args[0], Mapping):
                for k, v in args[0].items():
                    if v is args[0]:
                        v = self
                    if v is None and self._box_config[
                            "default_box"] and self._box_config[
                                "default_box_none_transform"]:
                        continue
                    self.__setitem__(k, v)
            elif isinstance(args[0], Iterable):
                for k, v in args[0]:
                    self.__setitem__(k, v)
            else:
                raise BoxValueError(
                    "First argument must be mapping or iterable")
        elif args:
            raise BoxTypeError(
                f"Box expected at most 1 argument, got {len(args)}")

        for k, v in kwargs.items():
            if args and isinstance(args[0], Mapping) and v is args[0]:
                v = self
            self.__setitem__(k, v)

        self._box_config["__created"] = True
Esempio n. 11
0
 def __hash__(self):
     if self.box_options.get("frozen_box"):
         hashing = 98765
         hashing ^= hash(tuple(self))
         return hashing
     raise BoxTypeError("unhashable type: 'BoxList'")
Esempio n. 12
0
 def __ior__(self, other: Mapping[Any, Any]):
     if not isinstance(other, dict):
         raise BoxTypeError(
             "Box can only merge two boxes or a box and a dictionary.")
     self.update(other)
     return self