Exemplo n.º 1
0
def box_from_file(file: Union[str, PathLike],
                  file_type: str = None,
                  encoding: str = "utf-8",
                  errors: str = "strict",
                  **kwargs) -> Union[Box, BoxList]:
    """
    Loads the provided file and tries to parse it into a Box or BoxList object as appropriate.

    :param file: Location of file
    :param encoding: File encoding
    :param errors: How to handle encoding errors
    :param file_type: manually specify file type: json, toml or yaml
    :return: Box or BoxList
    """

    if not isinstance(file, Path):
        file = Path(file)
    if not file.exists():
        raise BoxError(f'file "{file}" does not exist')
    file_type = file_type or file.suffix
    file_type = file_type.lower().lstrip(".")
    if file_type.lower() in converters:
        return converters[file_type.lower()](file, encoding, errors,
                                             **kwargs)  # type: ignore
    raise BoxError(
        f'"{file_type}" is an unknown type. Please use either csv, toml, msgpack, yaml or json'
    )
Exemplo n.º 2
0
 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')
     self.__delitem__(item)
Exemplo n.º 3
0
def _to_msgpack(file, _, __, **kwargs):
    if not msgpack_available:
        raise BoxError(
            f'File "{file}" is msgpack but no package is available to open it. Please install "msgpack"'
        )
    try:
        return Box.from_msgpack(filename=file, **kwargs)
    except (UnpackException, ValueError):
        raise BoxError("File is not msgpack as expected")
    except BoxError:
        return BoxList.from_msgpack(filename=file, **kwargs)
Exemplo n.º 4
0
def _to_toml(file, encoding, errors, **kwargs):
    if not toml_available:
        raise BoxError(
            f'File "{file}" is toml but no package is available to open it. Please install "toml"'
        )
    try:
        return Box.from_toml(filename=file,
                             encoding=encoding,
                             errors=errors,
                             **kwargs)
    except TomlDecodeError:
        raise BoxError("File is not TOML as expected")
Exemplo n.º 5
0
def _exists(filename, create=False):
    path = Path(filename)
    if create:
        try:
            path.touch(exist_ok=True)
        except OSError as err:
            raise BoxError(f'Could not create file {filename} - {err}')
        else:
            return
    if not path.exists():
        raise BoxError(f'File "{filename}" does not exist')
    if not path.is_file():
        raise BoxError(f'{filename} is not a file')
Exemplo n.º 6
0
def _exists(filename: Union[str, PathLike], create: bool = False) -> Path:
    path = Path(filename)
    if create:
        try:
            path.touch(exist_ok=True)
        except OSError as err:
            raise BoxError(f"Could not create file {filename} - {err}")
        else:
            return path
    if not path.exists():
        raise BoxError(f'File "{filename}" does not exist')
    if not path.is_file():
        raise BoxError(f"{filename} is not a file")
    return path
Exemplo n.º 7
0
def _to_yaml(
    obj,
    filename: Union[str, PathLike] = None,
    default_flow_style: bool = False,
    encoding: str = "utf-8",
    errors: str = "strict",
    ruamel_typ: str = "rt",
    ruamel_attrs: Optional[Dict] = None,
    **yaml_kwargs,
):
    if not ruamel_attrs:
        ruamel_attrs = {}
    if filename:
        _exists(filename, create=True)
        with open(filename, "w", encoding=encoding, errors=errors) as f:
            if ruamel_available:
                yaml_dumper = YAML(typ=ruamel_typ)
                yaml_dumper.default_flow_style = default_flow_style
                for attr, value in ruamel_attrs.items():
                    setattr(yaml_dumper, attr, value)
                return yaml_dumper.dump(obj, stream=f, **yaml_kwargs)
            elif pyyaml_available:
                return yaml.dump(obj,
                                 stream=f,
                                 default_flow_style=default_flow_style,
                                 **yaml_kwargs)
            else:
                raise BoxError(
                    "No YAML Parser available, please install ruamel.yaml>0.17 or PyYAML"
                )

    else:
        if ruamel_available:
            yaml_dumper = YAML(typ=ruamel_typ)
            yaml_dumper.default_flow_style = default_flow_style
            for attr, value in ruamel_attrs.items():
                setattr(yaml_dumper, attr, value)
            with StringIO() as string_stream:
                yaml_dumper.dump(obj, stream=string_stream, **yaml_kwargs)
                return string_stream.getvalue()
        elif pyyaml_available:
            return yaml.dump(obj,
                             default_flow_style=default_flow_style,
                             **yaml_kwargs)
        else:
            raise BoxError(
                "No YAML Parser available, please install ruamel.yaml>0.17 or PyYAML"
            )
Exemplo n.º 8
0
        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)
Exemplo n.º 9
0
        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)
Exemplo n.º 10
0
    def from_json(
        cls,
        json_string: str = None,
        filename: Union[str, PathLike] = None,
        encoding: str = "utf-8",
        errors: str = "strict",
        multiline: bool = False,
        **kwargs,
    ):
        """
        Transform a json object string into a BoxList object. If the incoming
        json is a dict, you must use Box.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 multiline: One object per line
        :param kwargs: parameters to pass to `Box()` or `json.loads`
        :return: BoxList object from json data
        """
        box_args = {}
        for arg in list(kwargs.keys()):
            if arg in BOX_PARAMETERS:
                box_args[arg] = kwargs.pop(arg)

        data = _from_json(
            json_string, filename=filename, encoding=encoding, errors=errors, multiline=multiline, **kwargs
        )

        if not isinstance(data, list):
            raise BoxError(f"json data not returned as a list, but rather a {type(data).__name__}")
        return cls(data, **box_args)
Exemplo n.º 11
0
 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
Exemplo n.º 12
0
        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)
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
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()
Exemplo n.º 16
0
    def from_yaml(cls,
                  yaml_string: str = None,
                  filename: Union[str, Path] = None,
                  encoding: str = 'utf-8',
                  errors: str = 'strict',
                  **kwargs):
        """
        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)
Exemplo n.º 17
0
        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)
Exemplo n.º 18
0
 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)
     if key not in self.keys() and (self._box_config['conversion_box'] or
                                    self._box_config['camel_killer_box']):
         if self._box_config['conversion_box']:
             k = _conversion_checks(key, self.keys(), self._box_config)
             self[key if not k else k] = value
         elif self._box_config['camel_killer_box']:
             for each_key in self:
                 if key == _camel_killer(each_key):
                     self[each_key] = value
                     break
             else:
                 self[_camel_killer(key)] = value
     else:
         self[key] = value
     self.__convert_and_store(key, value)
     self.__create_lineage()
Exemplo n.º 19
0
    def from_json(cls,
                  json_string: str = None,
                  filename: Union[str, Path] = None,
                  encoding: str = 'utf-8',
                  errors: str = 'strict',
                  **kwargs):
        """
        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)
Exemplo n.º 20
0
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')
Exemplo n.º 21
0
 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 None
         if item == '_box_config':
             raise BoxError('_box_config key must exist') from None
         kill_camel = self._box_config['camel_killer_box']
         if self._box_config['conversion_box'] and item:
             k = _conversion_checks(item, self.keys(), self._box_config)
             if k:
                 return self.__getitem__(k)
         if kill_camel:
             for k in self.keys():
                 if item == _camel_killer(k):
                     return self.__getitem__(k)
         if self._box_config['default_box']:
             return self.__get_default(item)
         raise BoxKeyError(str(err)) from None
     else:
         if item == '_box_config':
             return value
         # return self.__convert_and_store(item, value)
     return value
Exemplo n.º 22
0
 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'
     )
Exemplo n.º 23
0
    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)]
Exemplo n.º 24
0
 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)
Exemplo n.º 25
0
 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)
Exemplo n.º 26
0
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)
Exemplo n.º 27
0
 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
Exemplo n.º 28
0
 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')
Exemplo n.º 29
0
 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')
Exemplo n.º 30
0
 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')