def _parse_box_dots(item): for idx, char in enumerate(item): if char == '[': return item[:idx], item[idx:] elif char == '.': return item[:idx], item[idx + 1:] raise BoxError('Could not split box dots properly')
def to_toml(self, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict"): raise BoxError( 'toml is unavailable on this system, please install the "toml" package' )
def from_yaml( cls, yaml_string: str = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs, ): """ Transform a yaml object string into a BoxList object. :param yaml_string: string to pass to `yaml.load` :param filename: filename to open and pass to `yaml.load` :param encoding: File encoding :param errors: How to handle encoding errors :param kwargs: parameters to pass to `BoxList()` or `yaml.load` :return: BoxList object from yaml data """ box_args = {} for arg in list(kwargs.keys()): if arg in BOX_PARAMETERS: box_args[arg] = kwargs.pop(arg) data = _from_yaml(yaml_string=yaml_string, filename=filename, encoding=encoding, errors=errors, **kwargs) if not data: return cls(**box_args) if not isinstance(data, list): raise BoxError(f"yaml data not returned as a list but rather a {type(data).__name__}") return cls(data, **box_args)
def __getattr__(self, item): try: try: value = self.__getitem__(item, _ignore_default=True) except KeyError: value = object.__getattribute__(self, item) except AttributeError as err: if item == "__getstate__": raise BoxKeyError(item) from _exception_cause(err) if item == "_box_config": raise BoxError( "_box_config key must exist") from _exception_cause(err) if self._box_config["conversion_box"]: safe_key = self._safe_attr(item) if safe_key in self._box_config["__safe_keys"]: return self.__getitem__( self._box_config["__safe_keys"][safe_key]) if self._box_config["default_box"]: if item.startswith("_") and item.endswith("_"): raise BoxKeyError( f"{item}: Does not exist and internal methods are never defaulted" ) return self.__get_default(item, attr=True) raise BoxKeyError(str(err)) from _exception_cause(err) return value
def from_json( cls, json_string: str = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs, ) -> "Box": """ Transform a json object string into a Box object. If the incoming json is a list, you must use BoxList.from_json. :param json_string: string to pass to `json.loads` :param filename: filename to open and pass to `json.load` :param encoding: File encoding :param errors: How to handle encoding errors :param kwargs: parameters to pass to `Box()` or `json.loads` :return: Box object from json data """ box_args = {} for arg in kwargs.copy(): if arg in BOX_PARAMETERS: box_args[arg] = kwargs.pop(arg) data = _from_json(json_string, filename=filename, encoding=encoding, errors=errors, **kwargs) if not isinstance(data, dict): raise BoxError( f"json data not returned as a dictionary, but rather a {type(data).__name__}" ) return cls(data, **box_args)
def from_msgpack( cls, msgpack_bytes: bytes = None, filename: Union[str, PathLike] = None, **kwargs, ) -> "Box": """ Transforms msgpack bytes or file into a Box object :param msgpack_bytes: string to pass to `msgpack.unpackb` :param filename: filename to open and pass to `msgpack.unpack` :param kwargs: parameters to pass to `Box()` :return: Box object """ box_args = {} for arg in kwargs.copy(): if arg in BOX_PARAMETERS: box_args[arg] = kwargs.pop(arg) data = _from_msgpack(msgpack_bytes=msgpack_bytes, filename=filename, **kwargs) if not isinstance(data, dict): raise BoxError( f"msgpack data not returned as a dictionary but rather a {type(data).__name__}" ) return cls(data, **box_args)
def _to_yaml(data): try: return Box.from_yaml(data) except YAMLError: raise BoxError('File is not YAML as expected') except BoxError: return BoxList.from_yaml(data)
def from_yaml( cls, yaml_string: str = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs, ) -> "Box": """ Transform a yaml object string into a Box object. By default will use SafeLoader. :param yaml_string: string to pass to `yaml.load` :param filename: filename to open and pass to `yaml.load` :param encoding: File encoding :param errors: How to handle encoding errors :param kwargs: parameters to pass to `Box()` or `yaml.load` :return: Box object from yaml data """ box_args = {} for arg in kwargs.copy(): if arg in BOX_PARAMETERS: box_args[arg] = kwargs.pop(arg) data = _from_yaml(yaml_string=yaml_string, filename=filename, encoding=encoding, errors=errors, **kwargs) if not isinstance(data, dict): raise BoxError( f"yaml data not returned as a dictionary but rather a {type(data).__name__}" ) return cls(data, **box_args)
def _to_json(data): try: return Box.from_json(data) except JSONDecodeError: raise BoxError('File is not JSON as expected') except BoxError: return BoxList.from_json(data)
def _to_csv(box_list, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs): csv_column_names = list(box_list[0].keys()) for row in box_list: if list(row.keys()) != csv_column_names: raise BoxError( "BoxList must contain the same dictionary structure for every item to convert to csv" ) if filename: _exists(filename, create=True) out_data = open(filename, "w", encoding=encoding, errors=errors, newline="") else: out_data = StringIO("") writer = csv.DictWriter(out_data, fieldnames=csv_column_names, **kwargs) writer.writeheader() for data in box_list: writer.writerow(data) if not filename: return out_data.getvalue() # type: ignore out_data.close()
def from_toml( cls, toml_string: str = None, filename: Union[str, PathLike] = None, key_name: str = "toml", encoding: str = "utf-8", errors: str = "strict", **kwargs, ): """ Transforms a toml string or file into a BoxList object :param toml_string: string to pass to `toml.load` :param filename: filename to open and pass to `toml.load` :param key_name: Specify the name of the key to pull the list from (cannot directly convert from toml) :param encoding: File encoding :param errors: How to handle encoding errors :param kwargs: parameters to pass to `Box()` :return: """ box_args = {} for arg in list(kwargs.keys()): if arg in BOX_PARAMETERS: box_args[arg] = kwargs.pop(arg) data = _from_toml(toml_string=toml_string, filename=filename, encoding=encoding, errors=errors) if key_name not in data: raise BoxError(f"{key_name} was not found.") return cls(data[key_name], **box_args)
def from_msgpack(cls, msgpack_bytes: bytes = None, filename: Union[str, PathLike] = None, **kwargs): """ Transforms a toml string or file into a BoxList object :param msgpack_bytes: string to pass to `msgpack.packb` :param filename: filename to open and pass to `msgpack.pack` :param kwargs: parameters to pass to `Box()` :return: """ box_args = {} for arg in list(kwargs.keys()): if arg in BOX_PARAMETERS: box_args[arg] = kwargs.pop(arg) data = _from_msgpack(msgpack_bytes=msgpack_bytes, filename=filename, **kwargs) if not isinstance(data, list): raise BoxError( f"msgpack data not returned as a list but rather a {type(data).__name__}" ) return cls(data, **box_args)
def popitem(self): if self._box_config["frozen_box"]: raise BoxError("Box is frozen") try: key = next(self.__iter__()) except StopIteration: raise BoxKeyError("Empty box") from None return key, self.pop(key)
def _to_yaml(file, encoding, errors, **kwargs): if not yaml_available: raise BoxError( f'File "{file}" is yaml but no package is available to open it. Please install "ruamel.yaml" or "PyYAML"' ) try: return Box.from_yaml(filename=file, encoding=encoding, errors=errors, **kwargs) except YAMLError: raise BoxError("File is not YAML as expected") except BoxError: return BoxList.from_yaml(filename=file, encoding=encoding, errors=errors, **kwargs)
def items(self, dotted: Union[bool] = False): if not dotted: return super().items() if not self._box_config["box_dots"]: raise BoxError("Cannot return dotted keys as this Box does not have `box_dots` enabled") return [(k, self[k]) for k in self.keys(dotted=True)]
def __delattr__(self, item): if self._box_config['frozen_box']: raise BoxError('Box is frozen') if item == '_box_config': raise BoxError('"_box_config" is protected') if item in self._protected_keys: raise BoxKeyError(f'Key name "{item}" is protected') try: self.__delitem__(item) except KeyError as err: if self._box_config['conversion_box']: safe_key = self._safe_attr(item) if safe_key in self._box_config['__safe_keys']: self.__delitem__(self._box_config['__safe_keys'][safe_key]) del self._box_config['__safe_keys'][safe_key] return raise BoxKeyError(err)
def __delattr__(self, item): if self._box_config["frozen_box"]: raise BoxError("Box is frozen") if item == "_box_config": raise BoxError('"_box_config" is protected') if item in self._protected_keys: raise BoxKeyError(f'Key name "{item}" is protected') try: self.__delitem__(item) except KeyError as err: if self._box_config["conversion_box"]: safe_key = self._safe_attr(item) if safe_key in self._box_config["__safe_keys"]: self.__delitem__(self._box_config["__safe_keys"][safe_key]) del self._box_config["__safe_keys"][safe_key] return raise BoxKeyError(str(err)) from None
def to_yaml( self, filename: Union[str, PathLike] = None, default_flow_style: bool = False, encoding: str = "utf-8", errors: str = "strict", **yaml_kwargs, ): raise BoxError('yaml is unavailable on this system, please install the "ruamel.yaml" or "PyYAML" package')
def from_msgpack( cls, msgpack_bytes: bytes = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs, ): raise BoxError('msgpack is unavailable on this system, please install the "msgpack" package')
def from_toml( cls, toml_string: str = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs, ) -> "Box": raise BoxError('toml is unavailable on this system, please install the "toml" package')
def from_yaml( cls, yaml_string: str = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs, ): raise BoxError('yaml is unavailable on this system, please install the "ruamel.yaml" or "PyYAML" package')
def _from_yaml( yaml_string: str = None, filename: Union[str, PathLike] = None, encoding: str = "utf-8", errors: str = "strict", ruamel_typ: str = "rt", ruamel_attrs: Optional[Dict] = None, **kwargs, ): if not ruamel_attrs: ruamel_attrs = {} if filename: _exists(filename) with open(filename, "r", encoding=encoding, errors=errors) as f: if ruamel_available: yaml_loader = YAML(typ=ruamel_typ) for attr, value in ruamel_attrs.items(): setattr(yaml_loader, attr, value) data = yaml_loader.load(stream=f) elif pyyaml_available: if "Loader" not in kwargs: kwargs["Loader"] = yaml.SafeLoader data = yaml.load(f, **kwargs) else: raise BoxError( "No YAML Parser available, please install ruamel.yaml>0.15 or PyYAML" ) elif yaml_string: if ruamel_available: yaml_loader = YAML(typ=ruamel_typ) for attr, value in ruamel_attrs.items(): setattr(yaml_loader, attr, value) data = yaml_loader.load(stream=yaml_string) elif pyyaml_available: if "Loader" not in kwargs: kwargs["Loader"] = yaml.SafeLoader data = yaml.load(yaml_string, **kwargs) else: raise BoxError( "No YAML Parser available, please install ruamel.yaml>0.17 or PyYAML" ) else: raise BoxError("from_yaml requires a string or filename") return data
def __setattr__(self, key, value): if key != '_box_config' and self._box_config[ 'frozen_box'] and self._box_config['__created']: raise BoxError('Box is frozen') if key in self._protected_keys: raise BoxKeyError(f'Key name "{key}" is protected') if key == '_box_config': return object.__setattr__(self, key, value) value = self.__recast(key, value) self.__setitem__(key, value)
def _parse_box_dots(bx, item, setting=False): for idx, char in enumerate(item): if char == "[": return item[:idx], item[idx:] elif char == ".": if item[:idx] in bx: return item[:idx], item[idx + 1:] if setting and "." in item: return item.split(".", 1) raise BoxError("Could not split box dots properly")
def __setitem__(self, key, value): if self.box_options.get("frozen_box"): raise BoxError("BoxList is frozen") if self.box_options.get("box_dots") and isinstance(key, str) and key.startswith("["): list_pos = _list_pos_re.search(key) pos = int(list_pos.groups()[0]) if len(list_pos.group()) == len(key): return super(BoxList, self).__setitem__(pos, value) return super(BoxList, self).__getitem__(pos).__setitem__(key[len(list_pos.group()) :].lstrip("."), value) super(BoxList, self).__setitem__(key, value)
def __setattr__(self, key, value): if key != "_box_config" and self._box_config["frozen_box"] and self._box_config["__created"]: raise BoxError("Box is frozen") if key in self._protected_keys: raise BoxKeyError(f'Key name "{key}" is protected') if key == "_box_config": return object.__setattr__(self, key, value) safe_key = self._safe_attr(key) if safe_key in self._box_config["__safe_keys"]: key = self._box_config["__safe_keys"][safe_key] self.__setitem__(key, value)
def __delitem__(self, key): if self.box_options.get("frozen_box"): raise BoxError("BoxList is frozen") if self.box_options.get("box_dots") and isinstance(key, str) and key.startswith("["): list_pos = _list_pos_re.search(key) pos = int(list_pos.groups()[0]) if len(list_pos.group()) == len(key): return super(BoxList, self).__delitem__(pos) if hasattr(self[pos], "__delitem__"): return self[pos].__delitem__(key[len(list_pos.group()) :].lstrip(".")) # type: ignore super(BoxList, self).__delitem__(key)
def _from_msgpack(msgpack_bytes: bytes = None, filename: Union[str, PathLike] = None, **kwargs): if filename: _exists(filename) with open(filename, "rb") as f: data = msgpack.unpack(f, **kwargs) elif msgpack_bytes: data = msgpack.unpackb(msgpack_bytes, **kwargs) else: raise BoxError("from_msgpack requires a string or filename") return data
def pop(self, key, *args): if self._box_config["frozen_box"]: raise BoxError("Box is frozen") if args: if len(args) != 1: raise BoxError('pop() takes only one optional argument "default"') try: item = self[key] except KeyError: return args[0] else: del self[key] return item try: item = self[key] except KeyError: raise BoxKeyError(f"{key}") from None else: del self[key] return item
def _from_toml(toml_string=None, filename=None, encoding="utf-8", errors="strict"): if filename: _exists(filename) with open(filename, 'r', encoding=encoding, errors=errors) as f: data = toml.load(f) elif toml_string: data = toml.loads(toml_string) else: raise BoxError('from_toml requires a string or filename') return data