def crop(d: Mapping, prefix: str, trim: bool = True) -> dict: """Crop mapping to keys, that start with an initial string. Args: d: Mapping that encodes sections by the prefix of string keys prefix: Key prefix as string trim: Determines if the section prefix is removed from the keys of the returned mapping. Default: True Returns: Subset of the original mapping, which only contains keys that start with the given section. Thereby the new keys are trimmed from the initial section string. Examples: >>> crop({'a1': 1, 'a2': 2, 'b1': 3}, 'a') {'1': 1, '2': 2} """ # Check types check.has_type('d', d, Mapping) check.has_type('prefix', prefix, str) # Create new dictionary with section i = len(prefix) if trim: section = {k[i:]: v for k, v in d.items() \ if isinstance(k, str) and k[:i] == prefix} else: section = {k: v for k, v in d.items() \ if isinstance(k, str) and k[:i] == prefix} return section
def distance(x: NpArrayLike, y: NpArrayLike, name: str = 'euclid', axes: NpAxes = 0, **kwds: Any) -> NpArray: """Calculate distance of two arrays along given axes. A vector distance function, also known as metric, is a function d(x, y), which quantifies the proximity of vectors in a vector space as non-negative real numbers. If the distance is zero, then the vectors are equivalent with respect to the distance function. Distance functions are often used as error, loss or risk functions, to evaluate statistical estimations. Args: x: Any sequence that can be interpreted as a numpy ndarray of arbitrary dimension. This includes nested lists, tuples, scalars and existing arrays. y: Any sequence that can be interpreted as a numpy ndarray with the same dimension, shape and datatypes as 'x'. name: Name of distance. Accepted values are: 'minkowski': :term:`Minkowski distance` Remark: requires additional parameter 'p' 'manhattan': :term:`Manhattan distance` 'euclid': :term:`Euclidean distance` (default) 'chebyshev': :term:`Chebyshev distance` 'pmean': :term:`Power mean difference` Remark: requires additional parameter 'p' 'amean': :term:`Mean absolute difference` 'qmean': :term:`Quadratic mean difference` axes: Integer or tuple of integers, that identify the array axes, along which the function is evaluated. In a one-dimensional array the single axis has ID 0. In a two-dimensional array the axis with ID 0 is running across the rows and the axis with ID 1 is running across the columns. For the value None, the function is evaluated with respect to all axes of the array. The default value is 0, which is an evaluation with respect to the first axis in the array. **kwds: Parameters of the given distance or class of distances. The Parameters are documented within the respective 'dist' functions. Returns: :class:`numpy.ndarray` of dimension dim(*x*) - len(*axes*). """ # Try to cast 'x' and 'y' as arrays x = array.cast(x) y = array.cast(y) # Check type of 'axes' check.has_type("'axes'", axes, (int, tuple)) # Check dimensions of 'x' and 'y' if x.shape != y.shape: raise ValueError("arrays 'x' and 'y' can not be broadcasted together") # Get function from catalog f = catalog.pick(Distance, name=name) # Evaluate function return call.safe_call(f, x, y, axes=axes, **kwds)
def add_cols( base: NpRecArray, data: NpRecArray, cols: NpFields = None) -> NpRecArray: """Add columns from source table to target table. Wrapper function to numpy's `rec_append_fields`_. Args: base: Numpy record array with table like data data: Numpy record array storing the fields to add to the base. cols: String or sequence of strings corresponding to the names of the new columns. If cols is None, then all columns of the data table are appended. Default: None Returns: Numpy record array containing the base array, as well as the appended columns. """ cols = cols or getattr(data, 'dtype').names check.has_type("'cols'", cols, (tuple, str)) cols = list(cols) # make cols mutable # Append fields return nprec.rec_append_fields(base, cols, [data[c] for c in cols])
def _get_bindict(self, obj: 'Group') -> dict: binddict = self.binddict if not binddict: return obj.__dict__ check.has_attr(obj, binddict) check.has_type(binddict, getattr(obj, binddict), dict) return getattr(obj, binddict)
def from_dict( d: StrPairDict, labels: StrListPair, nan: Number = NaN) -> NpArray: """Convert dictionary to array. Args: d: Dictionary with keys (<*row*>, <*col*>), where the elemns <*row*> are row labels from the list <*rows*> and <*col*> column labels from the list *columns*. labels: Tuple of format (<*rows*>, <*columns*>), where <*rows*> is a list of row labels, e.g. ['row1', 'row2', ...] and <*columns*> a list of column labels, e.g. ['col1', 'col2', ...]. nan: Value to mask Not Not a Number (NaN) entries. Missing entries in the dictionary are replenished by the NaN value in the array. Default: [IEEE754]_ floating point representation of NaN. Returns: :class:`numpy.ndarray` of shape (*n*, *m*), where *n* equals the number of <*rows*> and *m* the number of <*columns*>. """ # Check type of 'd' check.has_type("'d'", d, dict) # Declare and initialize return value x: NpArray = np.empty(shape=(len(labels[0]), len(labels[1]))) # Get numpy ndarray setit = getattr(x, 'itemset') for i, row in enumerate(labels[0]): for j, col in enumerate(labels[1]): setit((i, j), d.get((row, col), nan)) return x
def get_caller_module_name(frame: int = 0) -> str: """Get name of module, which calls this function. Args: frame: Frame index relative to the current frame in the callstack, which is identified with 0. Negative values consecutively identify previous modules within the callstack. Default: 0 Returns: String with name of module. """ # Check types check.has_type("'frame'", frame, int) # Check values if frame > 0: raise ValueError("'frame' is required to be a negative number or zero") # Traceback frames using inspect mname: str = '' cframe = inspect.currentframe() for _ in range(abs(frame) + 1): if cframe is None: break cframe = cframe.f_back if cframe is not None: mname = cframe.f_globals['__name__'] return mname
def create_field(field: FieldLike) -> Field: """Create a Field object. Args: field: :term:`Field definition` Return: Instance of class :class:`Field` """ # Get Field Arguments if isinstance(field, Field): args = (field.id, field.type) elif isinstance(field, tuple): if len(field) == 1: args = (field[0], NoneType) elif len(field) == 2: args = (field[0], field[1]) else: args = (field, NoneType) # Check Field Arguments check.has_type('field identifier', args[0], Hashable) check.has_type('field type', args[1], type) # Create and return Field return Field(*args)
def __post_init__(self) -> None: check.has_type('type', self.type, int) check.has_type('key', self.key, str) if not self.type in [CONSTANT, VARIABLE]: check.is_callable('value', self.value) check.has_type('priority', self.priority, int) check.has_type('builtin', self.builtin, bool) check.has_type('factory', self.factory, bool)
def distance(x: NpArrayLike, y: NpArrayLike, name: str = 'frobenius', axes: IntPair = (0, 1), **kwds: Any) -> NpArray: """Calculate matrix distances of two arrays along given axes. A matrix distance function, is a function d(x, y), which quantifies the proximity of matrices in a vector space as non-negative real numbers. If the distance is zero, then the matrices are equivalent with respect to the distance function. Distance functions are often used as error, loss or risk functions, to evaluate statistical estimations. Args: x: Any sequence that can be interpreted as a numpy ndarray of two or more dimensions. This includes nested lists, tuples, scalars and existing arrays. y: Any sequence that can be interpreted as a numpy ndarray with the same dimension, shape and datatypes as 'x'. name: Name of used matrix distance. Accepted values are: 'frobenius': :term:`Frobenius distance` (default) axes: Pair (2-tuple) of integers, that identify the array axes, along which the function is evaluated. In a two-dimensional array the axis with ID 0 is running across the rows and the axis with ID 1 is running across the columns. The default value is (0, 1), which is an evaluation with respect to the first two axis in the array. **kwds: Parameters of the given distance or class of distances. The Parameters are documented within the respective 'dist' functions. Returns: :class:`numpy.ndarray` of dimension dim(*x*) - 2. """ # Try to cast 'x' and 'y' as arrays x = array.cast(x) y = array.cast(y) # Check type of 'axes' check.has_type("'axes'", axes, tuple) # Check dimensions of 'x' and 'y' if x.shape != y.shape: raise ValueError("arrays 'x' and 'y' can not be broadcasted together") # Check value of 'axes' check.has_size("argument 'axes'", axes, size=2) if axes[0] == axes[1]: raise np.AxisError("first and second axis have to be different") # Get function from catalog f = catalog.pick(Distance, name=name) # Evaluate function return call.safe_call(f, x=x, y=y, axes=axes, **kwds)
def merge(*args: Mapping, mode: int = 1) -> Mapping: """Recursively right merge mappings. Args: *args: Mappings with arbitrary structure mode: Creation mode for merged mapping: 0: change rightmost dictionary 1: create new dictionary by deepcopy 2: create new dictionary by chain mapping Returns: Dictionary containing right merge of dictionaries. Examples: >>> merge({'a': 1}, {'a': 2, 'b': 2}, {'c': 3}) {'a': 1, 'b': 2, 'c': 3} """ # Check for trivial cases if not args: return {} if len(args) == 1: return args[0] # Check for chain mapping creation mode if mode == 2: import collections return dict(collections.ChainMap(*args)) # Recursively right merge if len(args) == 2: d1, d2 = args[0], args[1] else: d1, d2 = args[0], merge(*args[1:], mode=mode) mode = 0 # Check Type of first and second argument check.has_type("first argument", d1, dict) check.has_type("second argument", d2, dict) # Create new dictionary if mode == 1: d2 = copy.deepcopy(d2) # Right merge couple of dictionaries for k1, v1 in d1.items(): if k1 not in d2: d2[k1] = v1 # type: ignore elif isinstance(v1, dict): merge(v1, d2[k1], mode=0) else: d2[k1] = v1 # type: ignore return d2
def get_dir(dirname: str, *args: Any, pkgname: OptStr = None, **kwds: Any) -> pathlib.Path: """Get application specific environmental directory by name. This function returns application specific system directories by platform independent names to allow platform independent storage for caching, logging, configuration and permanent data storage. Args: dirname: Environmental directory name. Allowed values are: :user_cache_dir: Cache directory of user :user_config_dir: Configuration directory of user :user_data_dir: Data directory of user :user_log_dir: Logging directory of user :site_config_dir: Site global configuration directory :site_data_dir: Site global data directory :site_package_dir: Site global package directory :site_temp_dir: Site global directory for temporary files :package_dir: Current package directory :package_data_dir: Current package data directory *args: Optional arguments that specify the application, as required by the function '.env.update_dirs'. **kwds: Optional keyword arguments that specify the application, as required by the function '.env.update_dirs'. Returns: String containing path of environmental directory or None if the pathname is not supported. """ # Get package name from callers top level module pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2)) # Check type of 'dirname' check.has_type("'dirname'", dirname, str) # Update derectories if not present or if any optional arguments are given try: dirs = globals()['cache'][pkgname]['dirs'] except KeyError: update_dirs(*args, pkgname=pkgname, **kwds) dirs = globals()['cache'][pkgname]['dirs'] # Check value of 'dirname' if dirname not in dirs: raise ValueError(f"directory name '{dirname}' is not valid") return dirs[dirname]
def length(x: NpArrayLike, norm: str = 'euclid', axes: NpAxes = 0, **kwds: Any) -> NpArray: r"""Calculate the length of a vector with respect to a given norm. Args: x: Any sequence that can be interpreted as a numpy ndarray of arbitrary dimension. This includes nested lists, tuples, scalars and existing arrays. norm: String, which identifies the used vector norm: :p-norm: The :term:`p-norm` requires an additional parameter *p* and induces the :term:`Minkowski distance`. :1-norm: The :term:`1-norm` induces the :term:`Manhattan distance`. :euclid: The :term:`Euclidean norm` is the default norm and induces the :term:`Euclidean distance`. :max: The :term:`Maximum norm` induces the :term:`Chebyshev distance`. :pmean: The :term:`Hölder mean` requires an additional parameter *p* and induces the :term:`power mean difference`. :amean: The :term:`mean absolute` induces the :term:`mean absolute difference` :qmean: The :term:`quadratic mean` induces the :term:`quadratic mean difference` axes: Integer or tuple of integers, that identify the array axes, along which the function is evaluated. In a one-dimensional array the single axis has ID 0. In a two-dimensional array the axis with ID 0 is running across the rows and the axis with ID 1 is running across the columns. For the value None, the function is evaluated with respect to all axes of the array. The default value is 0, which is an evaluation with respect to the first axis in the array. **kwds: Additional parameters of the given norm. These norm parameters are documented within the respective 'norm' functions. Returns: :class:`numpy.ndarray` of dimension dim(*x*) - len(*axes*). """ # Try to cast 'x' as array x = array.cast(x) # Check type of 'axes' check.has_type("'axes'", axes, (int, tuple)) # Get function from catalog f = catalog.pick(Norm, name=norm) # Evaluate function return call.safe_call(f, x, axes=axes, **kwds)
def norm(x: NpArrayLike, name: str = 'frobenius', axes: IntPair = (0, 1), **kwds: Any) -> NpArray: """Calculate magnitude of matrix with respect to given norm. Args: x: Any sequence that can be interpreted as a numpy ndarray of two or more dimensions. This includes nested lists, tuples, scalars and existing arrays. name: Name of matrix norm. Accepted values are: :pq: :term:`pq-Norm`. Remark: requires additional parameters *p* and *q* :frobenius: The default norm is the :term:`Frobenius Norm` axes: Pair (2-tuple) of integers, that identify the array axes, along which the function is evaluated. In a two-dimensional array the axis with ID 0 is running across the rows and the axis with ID 1 is running across the columns. The default value is (0, 1), which is an evaluation with respect to the first two axis in the array. **kwds: Parameters of the given norm / class of norms. The norm Parameters are documented within the respective 'norm' functions. Returns: :class:`numpy.ndarray` of dimension dim(*x*) - 2. """ # Try to cast 'x' as array x = array.cast(x) # Check type of 'axes' check.has_type("'axes'", axes, tuple) # Check dimension of 'x' if x.ndim < 2: raise ValueError("'x' is required to have dimension > 1") # Check value of 'axes' check.has_size("argument 'axes'", axes, size=2) if axes[0] == axes[1]: raise np.AxisError("first and second axis have to be different") # Get function from catalog f = catalog.pick(Norm, name=name) # Evaluate function return call.safe_call(f, x=x, axes=axes, **kwds)
def as_tuples(x: NpArray) -> List[tuple]: """Convert two dimensional array list of tuples. Args: x: Numpy ndarray of shape (*n*, *m*), where *n* equals the number of <*rows*> and *m* the number of <*columns*>. Returns: List of tuples. """ # Check type of 'x' check.has_type("'x'", x, np.ndarray) return x.tolist()
def __set__(self, obj: 'Group', val: Any) -> None: # Bypass and type check setter requests if self._get_readonly(obj): raise errors.ReadOnlyAttrError(obj, self.name) if self._is_remote(obj): self._set_remote(obj, val) return dtype = self.dtype if dtype and not isinstance(val, type(self.default)): check.has_type(f"attribute '{self.name}'", val, dtype) if callable(self.fset): self.fset(obj, val) # type: ignore return if isinstance(self.sset, str): getattr(obj, self.sset, void)(val) return binddict = self._get_bindict(obj) bindkey = self._get_bindkey(obj) binddict[bindkey] = val
def create_variable(var: VarLike, default: OptOp = None) -> Variable: """Create variable from variable definition. Args: var: Variable defintion default: Returns: """ # Check Arguments check.has_type('var', var, (str, tuple)) check.not_empty('var', var) # Get Defaults default = default or operator.Identity() # Get Variable Arguments args: VarLike if isinstance(var, str): if var.isidentifier(): args = (var, default, (var, )) else: op = operator.Lambda(expression=var) args = (var, op, op.variables) elif len(var) == 1: args = (var[0], default, (var[0], )) elif len(var) == 2: if callable(var[1]): args = (var[0], var[1], (var[0], )) elif isinstance(var[1], tuple): args = (var[0], default, var[1]) else: args = (var[0], default, (var[1], )) elif callable(var[1]): if isinstance(var[2], tuple): args = (var[0], var[1], var[2]) else: args = (var[0], var[1], (var[2], )) elif isinstance(var[2], tuple): op = operator.Lambda(expression=var[1], domain=(None, var[2])) args = (var[0], op, var[2]) else: op = operator.Lambda(expression=var[1], domain=(None, (var[2], ))) args = (var[0], op, (var[2], )) # Check Variable Arguments check.has_type('variable name', args[0], str) check.has_type('variable operator', args[1], Callable) check.has_type('variable frame', args[2], tuple) # Create and return Variable return Variable(*args)
def as_path(text: str, expand: bool = True) -> pathlib.Path: """Convert text into list. Args: text: String representing a path. expand: Boolen value, whoch determines, if variables in environmental path variables are expanded. Returns: Value of the text as Path. """ # Check types of Arguments check.has_type("first argument 'text'", text, str) check.has_type("'expand'", expand, bool) if expand: return env.expand(text) return pathlib.Path(text)
def decode(text: str, target: OptType = None, undef: OptStr = 'None', **kwds: Any) -> Any: """Decode text representation of object to object. Args: text: String representing the value of a given type in it's respective syntax format. The standard format corresponds to the standard Python representation if available. Some types also accept further formats, which may use additional keywords. target: Target type, in which the text is to be converted. undef: Optional string, which respresents an undefined value. If undef is a string, then the any text, the matches the string is decoded as None, independent from the given target type. **kwds: Supplementary parameters, that specify the encoding format of the target type. Returns: Value of the text in given target format or None. """ # Check Arguments check.has_type("'text'", text, str) check.has_opt_type("'target'", target, type) # Check for undefined value if text == undef: return None # If no target type is given, estimate type target = target or estimate(text) or str # Elementary literals if target == str: return text.strip().replace('\n', '') if target == bool: return text.lower().strip() == 'true' if target in [int, float, complex]: return target(text, **kwds) fname = 'as_' + target.__name__.lower() return pkg.call_attr(fname, text, **kwds)
def as_datetime(text: str, fmt: OptStr = None) -> Date: """Convert text to datetime. Args: text: String representation of datetime fmt: Optional string parameter, that specifies the format, which is used to decode the text to datetime. The default format is the [ISO 8601]_ format, given by the string `%Y-%m-%d %H:%M:%S.%f`. Returns: Value of the text as datetime. """ # Check types of Arguments check.has_type("first argument 'text'", text, str) # Convert text to datetime fmt = fmt or '%Y-%m-%d %H:%M:%S.%f' return Date.strptime(text, fmt)
def has_attr(name: str, module: OptModule = None) -> bool: """Determine if a module has an attribute of given name. Args: name: Name of attribute module: Optional reference to module, which is used to search for the given attribute. By default the current callers module is used. Returns: Result of call. """ # Set default values module = module or stack.get_caller_module() # Check Arguments check.has_type("'name'", name, str) check.has_type("'module'", module, Module) return hasattr(module, name)
def as_dict( x: NpArray, labels: StrListPair, nan: OptNumber = NaN) -> StrPairDict: """Convert two dimensional array to dictionary of pairs. Args: x: Numpy ndarray of shape (*n*, *m*), where *n* equals the number of <*rows*> and *m* the number of <*columns*>. labels: Tuple of format (<*rows*>, <*columns*>), where <*rows*> is a list of row labels, e.g. ['row1', 'row2', ...] and <*columns*> a list of column labels, e.g. ['col1', 'col2', ...]. na: Optional value to mask Not a Number (NaN) entries. For cells in the array, which have this value, no entry in the returned dictionary is created. If nan is None, then for all numbers entries are created. Default: [IEEE754]_ floating point representation of NaN. Returns: Dictionary with keys (<*row*>, <*col*>), where the elemns <*row*> are row labels from the list <*rows*> and <*col*> column labels from the list *columns*. """ # Check type of 'x' check.has_type("'x'", x, np.ndarray) # Check dimension of 'x' if x.ndim != 2: raise TypeError( "Numpy ndarray 'x' is required to have dimension 2" f", not '{x.ndim}'") # Get dictionary with pairs as keys d: StrPairDict = {} for i, row in enumerate(labels[0]): for j, col in enumerate(labels[1]): val = x.item(i, j) if nan is None or not np.isnan(val): d[(row, col)] = val return d
def select(d: Mapping, pattern: str) -> dict: """Filter mappings to keys, that match a given pattern. Args: d: Mapping, which keys aregiven by strings pattern: Wildcard pattern as described in the standard library module :mod:`fnmatch`. Returns: Subset of the original mapping, which only contains keys, that match the given pattern. Examples: >>> select({'a1': 1, 'a2': 2, 'b1': 3}, 'a*') {'a1': 1, 'a2': 2} """ # Check types check.has_type('d', d, Mapping) check.has_type('pattern', pattern, str) valid = fnmatch.filter(d.keys(), pattern) return {k: d[k] for k in valid}
def as_set(text: str, delim: str = ',') -> set: """Convert text into set. Args: text: String representing a set. Valid representations are: Python format: Allows elements of arbitrary types: Example: "{'a', 'b', 3}" Delimiter separated values (DSV): Allows string elements: Example: "a, b, c" delim: A string, which is used as delimiter for the separatation of the text. This parameter is only used in the DSV format. Returns: Value of the text as set. """ # Check types of Arguments check.has_type("first argument 'text'", text, str) check.has_type("'delim'", delim, str) # Return empty set if the string is blank if not text or not text.strip(): return set() # Python standard format val = None if delim == ',': try: val = set(ast.literal_eval(text)) except (SyntaxError, ValueError, Warning): pass if isinstance(val, set): return val # Delimited string format return {item.strip() for item in text.split(delim)}
def get_caller_name(frame: int = 0) -> str: """Get name of the callable, which calls this function. Args: frame: Frame index relative to the current frame in the callstack, which is identified with 0. Negative values consecutively identify previous modules within the callstack. Default: 0 Returns: String with name of the caller. """ # Check types check.has_type("'frame'", frame, int) # Check value of 'frame' if frame > 0: raise ValueError("'frame' is required to be a negative number or zero") # Get name of caller using inspect stack = inspect.stack()[abs(frame - 1)] mname = inspect.getmodule(stack[0]).__name__ fbase = stack[3] return '.'.join([mname, fbase])
def __init__(self, fget: OptCallOrStr = None, fset: OptCallOrStr = None, fdel: OptCallOrStr = None, doc: OptStr = None, dtype: OptClassInfo = None, readonly: bool = False, default: Any = None, factory: OptCallOrStr = None, binddict: OptStr = None, bindkey: OptStr = None, remote: bool = False, inherit: bool = False, category: OptStr = None) -> None: # Initialize Property Class super().__init__( # type: ignore fget=fget if callable(fget) else None, fset=fset if callable(fset) else None, fdel=fdel if callable(fdel) else None, doc=doc) # Check Types check.has_opt_type("'dtype'", dtype, TypeHint) check.has_type("'readonly'", readonly, bool) check.has_opt_type("'binddict'", binddict, str) check.has_opt_type("'bindkey'", bindkey, str) check.has_type("'remote'", inherit, bool) check.has_type("'inherit'", inherit, bool) check.has_opt_type("'category'", category, str) # Bind Instance Attributes to given Arguments self.sget = fget if isinstance(fget, str) else None self.sset = fset if isinstance(fset, str) else None self.sdel = fdel if isinstance(fdel, str) else None self.dtype = dtype self.default = default self.factory = factory self.readonly = readonly self.binddict = binddict self.bindkey = bindkey self.remote = remote self.inherit = inherit self.category = category
def create_domain(domain: DomLike = None, defaults: Keywords = None) -> Domain: """Create Domain object from domain definition. Args: domain: Optional :term:`domain like` parameter, that specifies the type and (if required) the frame of a domain. defaults: Optional :term:`mapping` which is used to complete the given domain definition. The key `'type'` specifies the default domain type and is required to be given as a :class:`type`. The key `'fields'` specifies a default ordered basis for the domain and is required to be given as a single or a tuple of :term:`field definitions<field definition>`. Returns: Instance of the class :class:`Domain` """ # Check Arguments check.has_opt_type('domain', domain, (Hashable, Field, tuple)) check.has_opt_type('defaults', defaults, Mapping) # Get Defaults defaults = defaults or {} dtype = defaults.get('type', NoneType) dfields = defaults.get('fields', tuple()) # Get Domain Arguments if not domain: args = (dtype, *create_basis(dfields)) elif isinstance(domain, Domain): args = (domain.type, domain.frame, domain.basis) elif isinstance(domain, tuple): args = (domain[0] or dtype, *create_basis(domain[1] or dfields)) else: args = (domain, *create_basis(dfields)) # Check Domain Arguments check.has_type('domain type', args[0], type) check.has_type('domain frame', args[1], tuple) check.no_dublicates('domain frame', args[1]) check.has_type('domain basis', args[2], dict) # Create and return Domain return Domain(*args)
def decode(text: str, scheme: OptScheme = None, autocast: bool = False, flat: OptBool = None) -> Scheme: """Load configuration dictionary from INI-formated text. Args: text: Text, that describes a configuration in INI-format. scheme: Dictionary of dictionaries, which determines the structure of the configuration dictionary. If scheme is None, the INI-file is completely imported and all values are interpreted as strings. If the scheme is a dictionary of dictionaries, the keys of the outer dictionary describe valid section names by strings, that are interpreted as regular expressions. Therupon, the keys of the respective inner dictionaries describe valid parameter names as strings, that are also interpreted as regular expressions. Finally the values of the inner dictionaries define the type of the parameters by their own type, e.g. str, int, float etc. Accepted types can be found in the documentation of the function :func:`literal.decode <hup.base.literal.decode>`. autocast: If no scheme is given autocast determines, if the values are automatically converted to types, estimated by the function :func:`literal.estimate <hup.base.literal.estimate>` flat: Determines if the desired INI format structure contains sections or not. By default sections are used, if the first non blank, non comment line in the string identifies a section. Return: Structured configuration dictionary. """ # Check arguments check.has_type("first argument", text, str) # If the usage of sections is not defined by the argument 'flat' their # existence is determined from the given file scheme. If the file # scheme also is not given, it is determined by the first not blank and # non comment line in the text. If this line does not start with the # character '[', then the file scheme is considered to be flat. if flat is None: if isinstance(scheme, dict): flat = not any(isinstance(val, dict) for val in scheme.values()) else: flat = True with StringIO(text) as fh: line = '' for line in fh: content = line.strip() if content and not content.startswith('#'): break flat = not line.lstrip().startswith('[') # For flat structured files a temporary [root] section is created and the # scheme dictionary is embedded within the 'root' key of a wrapping # dictionary. if flat: text = '\n'.join(['[root]', text]) if isinstance(scheme, dict): scheme = cast(SecDict, {'root': scheme}) # Parse ini without literal decoding parser = ConfigParser() setattr(parser, 'optionxform', lambda key: key) parser.read_string(text) # Decode literals by using the scheme dictionary config = parse(parser, scheme=cast(SecDict, scheme), autocast=autocast) # If scheme is flat collapse the 'root' key return config.get('root') or {} if flat else config
def search(module: OptModule = None, pattern: OptStr = None, classinfo: ClassInfo = Function, key: OptStr = None, val: OptStr = None, groupby: OptStr = None, recursive: bool = True, rules: OptDictOfKeyOps = None, errors: bool = False, **kwds: Any) -> dict: """Recursively search for objects within submodules. Args: module: Optional reference to module, which is used to search objects. By default the current callers module is used. pattern: Only objects which names satisfy the wildcard pattern given by 'pattern' are returned. The format of the wildcard pattern is described in the standard library module :py:mod:`fnmatch`. If pattern is None, then all objects are returned. Default: None classinfo: Classinfo given as a class, a type or a tuple containing classes, types or other tuples. Only members, which are ether an instance or a subclass of classinfo are returned. By default all types are allowed. key: Name of function attribute which is used as the key for the returned dictionary. If 'key' is None, then the fully qualified function names are used as keys. Default: None val: Name of function attribute which is used as the value for the returned dictionary. If 'val' is None, then all attributes of the respective objects are returned. Default: None groupby: Name of function attribute which is used to group the results. If 'groupby' is None, then the results are not grouped. Default: None recursive: Boolean value which determines if the search is performed recursively within all submodules. Default: True rules: Dictionary with individual filter rules, used by the attribute filter. The form is {<attribute>: <lambda>, ...}, where: <attribute> is a string with the attribute name and <lambda> is a boolean valued lambda function, which specifies the comparison of the attribute value against the argument value. Example: {'tags': lambda arg, attr: set(arg) <= set(attr)} By default any attribute, which is not in the filter rules is compared to the argument value by equality. errors: Boolean value which determines if an error is raised, if the module could not be found. By default errors are not raised. **kwds: Keyword arguments, that define the attribute filter for the returned dictionary. For example if the argument "tags = ['test']" is given, then only objects are returned, which have the attribute 'tags' and the value of the attribute equals ['test']. If, however, the filter rule of the above example is given, then any function, with attribute 'tags' and a corresponding tag list, that comprises 'test' is returned. Returns: Dictionary with function information as specified in the arguments 'key' and 'val'. """ # Set default values module = module or stack.get_caller_module() # Check argument types check.has_type("'module'", module, Module) check.has_opt_type("'pattern'", pattern, str) # Get list with module names, including the given and the submodules submodules = get_submodules(parent=module, recursive=recursive) mnames = [module.__name__] + submodules # Create dictionary with member attributes fd = {} rules = rules or {} for mname in mnames: minst = get_module(mname, errors=errors) if minst is None: continue d = otree.get_members_dict(minst, classinfo=classinfo, pattern=pattern, rules=rules, **kwds) # Ignore members if any required attribute is not available for name, attr in d.items(): if key and not key in attr: continue if val and not val in attr: continue fd[name] = attr # Rename key for returned dictionary if key: d = {} for name, attr in fd.items(): if key not in attr: continue kval = attr[key] if kval in d: continue d[kval] = attr fd = d # Group results if groupby: fd = mapping.groupby(fd, key=groupby) # Set value for returned dictionary if val: if groupby: for gn, group in fd.items(): for name, attr in group.items(): fd[name] = attr[val] else: for name, attr in fd.items(): fd[name] = attr[val] return fd
def get_var(varname: str, *args: Any, pkgname: OptStr = None, **kwds: Any) -> OptStr: """Get environment or application variable. Environment variables comprise static and runtime properties of the operating system like 'username' or 'hostname'. Application variables in turn, are intended to describe the application distribution by authorship information, bibliographic information, status, formal conditions and notes or warnings. For mor information see :PEP:`345`. Args: varname: Name of environment variable. Typical application variable names are: 'name': The name of the distribution 'version': A string containing the distribution's version number 'status': Development status of the distributed application. Typical values are 'Prototype', 'Development', or 'Production' 'description': A longer description of the distribution that can run to several paragraphs. 'keywords': A list of additional keywords to be used to assist searching for the distribution in a larger catalog. 'url': A string containing the URL for the distribution's homepage. 'license': Text indicating the license covering the distribution 'copyright': Notice of statutorily prescribed form that informs users of the distribution to published copyright ownership. 'author': A string containing the author's name at a minimum; additional contact information may be provided. 'email': A string containing the author's e-mail address. It can contain a name and e-mail address, as described in :rfc:`822`. 'maintainer': A string containing the maintainer's name at a minimum; additional contact information may be provided. 'company': The company, which created or maintains the distribution. 'organization': The organization, twhich created or maintains the distribution. 'credits': list with strings, acknowledging further contributors, Teams or supporting organizations. *args: Optional arguments that specify the application, as required by the function :func:`~hup.base.env.update_vars`. pkgname: **kwds: Optional keyword arguments that specify the application, as required by the function :func:`~hup.base.env.update_vars`. Returns: String representing the value of the application variable. """ # Check type of 'varname' check.has_type("'varname'", varname, str) # Get package name from callers top level module pkgname = pkgname or pkg.get_root_name(stack.get_caller_module_name(-2)) # Update variables if not present or if optional arguments are given try: appvars = globals()['cache'][pkgname]['vars'] except KeyError: update_vars(*args, pkgname=pkgname, **kwds) appvars = globals()['cache'][pkgname]['vars'] return appvars.get(varname, None)
def as_dict(text: str, delim: str = ',') -> dict: """Convert text into dictionary. Args: text: String representing a dictionary. Valid representations are: Python format: Allows keys and values of arbitrary types: Example: "{'a': 2, 1: True}" Delimiter separated expressions: Allow string keys and values: Example (Variant A): "<key> = <value><delim> ..." Example (Variant B): "'<key>': <value><delim> ..." delim: A string, which is used as delimiter for the separatation of the text. This parameter is only used in the DSV format. Returns: Value of the text as dictionary. """ # Check types of Arguments check.has_type("first argument 'text'", text, str) check.has_type("argumnt 'delim'", delim, str) # Return empty dict if the string is blank if not text or not text.strip(): return dict() Num = pp.Word(pp.nums + '.') Str = pp.quotedString Bool = pp.Or(pp.Word("True") | pp.Word("False")) Key = pp.Word(pp.alphas + "_", pp.alphanums + "_.") Val = pp.Or(Num | Str | Bool) # Try dictionary format "<key> = <value><delim> ..." Term = pp.Group(Key + '=' + Val) Terms = Term + pp.ZeroOrMore(delim + Term) try: l = Terms.parseString(text.strip('{}')) except pp.ParseException: l = None # Try dictionary format "'<key>': <value><delim> ..." if not l: Term = pp.Group(Str + ':' + Val) Terms = Term + pp.ZeroOrMore(delim + Term) try: l = Terms.parseString(text.strip('{}')) except pp.ParseException: return {} # Create dictionary from list d = {} for item in l: if len(item) == 1: if item[0] == ',': continue d[item] = True continue try: key, val = item[0].strip('\'\"'), ast.literal_eval(item[2]) except (KeyError, NameError, TypeError, ValueError, SyntaxError, AttributeError): continue if isinstance(val, str): val = val.strip() d[key] = val return d