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"')
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)
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
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
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
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])
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])
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
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
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
def __hash__(self): if self.box_options.get("frozen_box"): hashing = 98765 hashing ^= hash(tuple(self)) return hashing raise BoxTypeError("unhashable type: 'BoxList'")
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