Source code for libICEpost.src.base.dataStructures.Dictionary

#####################################################################
#                                 DOC                               #
#####################################################################

"""
@author: F. Ramognino       <federico.ramognino@polimi.it>
Last update:        12/06/2023
"""

#####################################################################
#                               IMPORT                              #
#####################################################################


from ..Utilities import Utilities
from collections import OrderedDict

from types import ModuleType
from typing import TypeVar, Iterable, Any, _SpecialGenericAlias
T = TypeVar("T")

import os.path as path

#############################################################################
#                               MAIN CLASSES                                #
#############################################################################

[docs] class Dictionary(OrderedDict, Utilities): """ Ordered dictionary embedding some useful OpenFOAM-like methods. """ path:str|None file:str|None ############################################################################# def __init__(self, *args, _fileName:str=None, **argv): """ Same constructor as collections.OrderedDict class. """ if _fileName is None: # no file assosiaction self.fileName = None self.path = None else: #Relative or absolute path base, file = path.split(_fileName) #Path if (base == "") or (base is None): self.path = "." + path.sep else: self.path = base #File name: if (file == "") or (file is None): raise ValueError(f"Invalid file name {_fileName}") self.fileName = file super().__init__(*args,**argv) #############################################################################
[docs] @classmethod def fromFile(cls, fileName:str): """ fileName: str Path of the file Read the variables stored in a python file (Runs the code in the file and retrieves the local variables) NOTE: local variable 'this' for this file. You can access the local folder as 'this.path' within the dictionary. """ cls.checkType(fileName, str, "fileName") this = cls(_fileName=fileName) _LOCALS = locals().copy() _OLDLOCALS = list(_LOCALS) with open(fileName) as _FILE: exec(_FILE.read()) _LOCALS = locals().copy() for l in _LOCALS.keys(): #If the variable is not a module and is not a local variable of the function, add to the dictionary if not l in (_OLDLOCALS + ["_OLDLOCALS", "_LOCALS", "_FILE"]) and (not isinstance(_LOCALS[l], ModuleType)): this[l] = _LOCALS[l] return this
#############################################################################
[docs] def lookup(self, entryName:str, *, varType:T|Iterable[type]=None) -> T|Any: """ Same as __getitem__ but embeds error handling and type checking. Args: entryName (str): Name of the entry to look for varType (type|Iterable[type], optional): Type of the variable to lookup for. Performes type-checking if it is given. Defaults to None. Raises: KeyError: If the entry is not found TypeError: If the type is not consistent with varType Returns: varType|Any: self[entryName] """ self.checkType(entryName, str, "entryName") if varType is None: pass elif isinstance(varType, Iterable): [self.checkType(t, (type, _SpecialGenericAlias), f"varType[{ii}]") for ii,t in enumerate(varType)] else: self.checkType(varType, (type, _SpecialGenericAlias), f"varType") if not entryName in self: raise KeyError(f"Entry '{entryName}' not found in Dictionary. Available entries are:\n\t" + f"\n\t".join([str(k) for k in self.keys()])) elif (not varType is None) and (not isinstance(self[entryName], varType)): raise TypeError(f"Entry '{entryName}' of wrong type. {varType.__name__ if not isinstance(varType, Iterable) else [v.__name__ for v in varType]} expected but {self[entryName].__class__.__name__} was found.") else: return self[entryName]
#############################################################################
[docs] def pop(self, entryName:str): """ entryName: str Name of the entry to look for Same as dictionary.pop but custom error message """ if not entryName in self: raise KeyError(f"Entry '{entryName}' not found in Dictionary. Available entries are:\n\t" + "\n\t".join([str(k) for k in self.keys()])) else: return super().pop(entryName)
######################################
[docs] def lookupOrDefault(self, entryName:str, default:T, fatal:bool=True) -> T: """ Lookup of give a default value if not found Args: entryName (str): Name of the entry to look for default (T): Instance to return in case the value is not found. It is also used for typeChecking fatal (bool, optional): If the type is not consistent rise a TypeError. Defaults to True. Returns: T: self[entryName] if entryName is found, else default """ self.checkType(entryName, str, "entryName") self.checkType(fatal, bool, "fatal") if not entryName in self: return default else: if not isinstance(self[entryName], type(default)) and fatal: raise TypeError(f"Inconsistent type of returne value ({type(self[entryName]).__name__}) with default ({type(default).__name__}).") return self[entryName]
######################################
[docs] def _correctSubdicts(self): """ Convert recursively every subdictionary into Dictionary classes. """ for entry in self: if isinstance(self[entry], dict) and not isinstance(self[entry], Dictionary): self[entry] = Dictionary(**self[entry]) return self
######################################
[docs] def __setitem__(self, *args, **argv): super().__setitem__(*args, **argv) self._correctSubdicts() return self
######################################
[docs] def update(self, /, dictionary:dict=None, **kwargs): """ Performs like dict.update() method but recursively updates sub-dictionaries. Accepts both a dictionary and keyword arguments. Args: dictionary (dict, optional): Dictionary to update with. Defaults to None. """ if not dictionary is None: self.update(**dictionary) for key in kwargs: if (isinstance(kwargs[key],dict) if (key in self) else False): self[key].update(**kwargs[key]) else: super().update({key:kwargs[key]}) self._correctSubdicts() return self