Source code for matador.orm.orm

# coding: utf-8
# Distributed under the terms of the MIT license.

""" This file implements the base DataContainer class which wraps raw
matador dictionaries and adds useful methods to be inherited by its


import copy
import math
from abc import ABC

[docs]class DataContainer(ABC): """Base class for matador data classes. This class is a read-only store of the underlying dictionary of raw data; its children can implement useful methods to inspect and analyse the underlying data. """ required_keys = [] def __init__(self, data=None, mutable=False, **kwargs): """Initalise copy of raw data.""" if isinstance(data, dict): self._data = copy.deepcopy(dict(data)) else: self._data = {key: kwargs[key] for key in kwargs} self._validate_inputs() self._mutable = mutable self._root_source = "unknown" @property def root_source(self): from matador.utils.chem_utils import get_root_source try: if "source" in self._data: self._root_source = get_root_source(self._data["source"]) except RuntimeError: pass return self._root_source @property def provenance(self) -> str: """Returns a string containing a guess of the provenance (i.e., CSP method or structure source) for this object. """ from matador.utils.cursor_utils import get_guess_doc_provenance return get_guess_doc_provenance(self.source) @property def source(self): """Return the source of the data.""" if "source" not in self._data: return ["unknown"] return self._data["source"] def _validate_inputs(self): """Validate the incoming data by checking the existence of the required keys for the subclass. """ missing_keys = [] for key in self.required_keys: if key not in self._data: missing_keys.append(key) if missing_keys: raise RuntimeError( "Unable to create object of type {} as the following keys are missing: {}".format( type(self).__name__, missing_keys ) ) def __getitem__(self, key): """Allow properties to be used with key access. Parameters: key (str): name of key or attribute to get. Raises: AttributeError: if key or attribute can't be found. """ try: return getattr(self, key) except AttributeError: pass try: return self._data[key] except KeyError: raise KeyError( f'Object {self} has no data or implementation for requested key: "{key}"' ) def __delitem__(self, key: str): raise AttributeError( f"Object {self} does not support deletion of keys in `_data`." )
[docs] def pop(self, key): return self._data.pop(key, None)
def __setitem__(self, key: str, item): if self._mutable: self._data[key] = item return if key not in self._data or self._data[key] is None: self._data[key] = item return elif self._data[key] != item: try: if math.isnan(item) and math.isnan(self._data[key]): return except TypeError: pass raise AttributeError( "Cannot assign value {} to existing key {} with value {}".format( item, key, self._data[key] ) ) def __contains__(self, key): return key in self._data
[docs] def get(self, *args): """Overload dictionary.get() method. Parameters: key (str): key to try and obtain. Keyword arguments: default: return value raise if key is not present. Returns: :attr:`_data[key]` if it exists, else None. """ key = args[0] if len(args) > 1: default = args[1] else: default = None if len(args) > 2: raise TypeError("get() takes up to 2 arguments, not {}".format(len(args))) return self._data.get(key, default)