Source code for dict2obj

# -*- coding: utf-8 -*-
"""
:Purpose:   This class module is used to convert a Python dictionary (``dict``)
            or JSON file into a object - where the dictionary's key/value
            pairs become object attribute/value pairs.

:Platform:  Linux/Windows | Python 3.6+
:Developer: J Berendt
:Email:     support@s3dev.uk

:Comments:  Basic concept `attribution`_.


.. _attribution: https://stackoverflow.com/a/1639197/6340496

"""
# pylint: disable=too-few-public-methods

import os
import json
from string import punctuation


[docs] class Dict2Obj: """Create a Python object from a standard Python dictionary, or JSON file. Args: dictionary (dict, optional): A standard Python dictionary where all key/value pairs will be converted into an object. Defaults to None. source (str, optional): Source for the conversion. Defaults to 'dict'. - 'dict': a standard Python dictionary - 'json': uses content from a JSON file filepath (str, optional): Full file path to the JSON file to be used. Defaults to None. :Design: A Python object is created from the passed dictionary (or JSON file), where each of the dictionary's key/value pairs is turned into an object attribute/value pair. :Note: #. The dictionary or JSON file *should* be in a flat format. If a nested source is provided, the value of the object will be the nested structure. In other the object will *not* be nested. #. This can be useful when loading a JSON config file into memory, as you can then access it like an object, rather than a dictionary. :Example: To convert a dictionary into an object:: >>> from utils4.dict2obj import Dict2Obj >>> d = dict(a=1, b=2, title='This is a title.') >>> obj = Dict2Obj(dictionary=d) >>> print(obj.title) This is a title. """ _VALID = ['dict', 'json']
[docs] def __init__(self, *, dictionary=None, source='dict', filepath=None): """Class initialiser.""" self._dict = dictionary self._src = source self._fpath = filepath self._create()
[docs] def _create(self): """Validate and create the object. Raises: TypeError: If a key is not a string, or is a string yet begins with any type other than a string.. """ if self._validate(): if self._src.lower() == 'json': # Read from json file. dict_ = self._read_json() else: # Create object from passed dictionary. dict_ = self._dict # Create replacement translation. trans = str.maketrans({p: '' for p in punctuation}) trans.update({32: '_'}) # Loop through the dict and set class attributes. for k, v in dict_.items(): if isinstance(k, str) & (not str(k)[0].isdigit()): k = k.translate(trans) setattr(self, k, v) else: raise TypeError(f'Key error, string expected. Received {type(k)} for key: {k}.')
[docs] def _read_json(self) -> dict: """Read values from a JSON file into a dictionary. Returns: dict: A dictionary containing the JSON data. """ with open(self._fpath, 'r', encoding='utf-8') as f: return json.loads(f.read())
[docs] def _validate(self) -> bool: """Run the following validation tests: - The ``source`` value is valid. - If 'json' source, a file path is provided. - If 'json' source, the provided file path exists. Returns: bool: True if **all** tests pass, otherwise False. """ # pylint: disable=multiple-statements s = self._validate_source_value() if s: s = self._validate_source() if s: s = self._validate_is_dict() if s: s = self._validate_fileexists() return s
[docs] def _validate_fileexists(self) -> bool: """Validation test: If a 'json' source, test the file path exists. Raises: ValueError: If the passed filepath is not a '.json' extension. ValueError: If the passed filepath does not exist. Returns: bool: True if the source is 'dict'; or if source is 'json' and the file exists, otherwise False. """ success = False if self._src.lower() == 'json': if os.path.exists(self._fpath): if os.path.splitext(self._fpath)[1].lower() == '.json': success = True else: raise ValueError(f'The file provided must be a JSON file:\n- {self._fpath}') else: raise ValueError(f'The file provided does not exist:\n- {self._fpath}') else: success = True return success
[docs] def _validate_is_dict(self) -> bool: """Validation test: Verify the object is a ``dict``. Raises: TypeError: If the passed object is not a ``dict``. Returns: bool: True if the passed object is a ``dict``. """ if self._src == 'dict': if not isinstance(self._dict, dict): raise TypeError(f'Unexpected type. Expected a dict, received a {type(self._dict)}.') return True
[docs] def _validate_source(self) -> bool: """Validation test: If a 'json' source, test a file path is provided. Raises: ValueError: If the source is 'json' and a filepath is not provided. Returns: bool: True if the source is 'dict'; or if source is 'json' and a file path is provided. """ if all([self._src.lower() == 'json', not self._fpath]): raise ValueError('A file path must be provided for the JSON file.') return True
[docs] def _validate_source_value(self) -> bool: """Validation test: The value of the ``source`` parameter is valid. Raises: ValueError: If the source string is invalid. Returns: bool: True if a valid source. """ if self._src not in self._VALID: raise ValueError(f'The source provided ({self._src}) is invalid. ' f'Valid options are: {self._VALID}') return True