lewis.core.utils

This module contains some useful helper classes and functions that are not specific to a certain module contained in the Core API.

Members

FromOptionalDependency

This is a utility class for importing classes from a module or replacing them with dummy types if the module can not be loaded.

check_limits

This decorator helps to make sure that the parameter of a property setter (or any other method with one argument) is within certain numerical limits.

dict_strict_update

This function updates base_dict with update_dict if and only if update_dict does not contain keys that are not already in base_dict.

extract_module_name

This function tries to extract a valid module name from the basename of the supplied path.

format_doc_text

A very thin wrapper around textwrap.fill to consistently wrap documentation text for display in a command line environment.

get_members

Returns all members of an object for which the supplied predicate is true and that do not begin with __.

get_submodules

This function imports all sub-modules of the supplied module and returns a dictionary with module names as keys and the sub-module objects as values.

seconds_since

This is a small helper function that returns the elapsed seconds since start using datetime.datetime.now().

class lewis.core.utils.FromOptionalDependency(module, exception=None)[source]

Bases: object

This is a utility class for importing classes from a module or replacing them with dummy types if the module can not be loaded.

Assume module ‘a’ that does:

from b import C, D

and module ‘e’ which does:

from a import F

where ‘b’ is a hard to install dependency which is thus optional. To still be able to do:

import e

without raising an error, for example for inspection purposes, this class can be used as a workaround in module ‘a’:

C, D = FromOptionalDependency("b").do_import("C", "D")

which is not as pretty as the actual syntax, but at least it can be read in a similar way. If the module ‘b’ can not be imported, stub-types are created that are called ‘C’ and ‘D’. Everything depending on these types will work until any of those are instantiated - in that case an exception is raised.

The exception can be controlled via the exception-parameter. If it is a string, a LewisException is constructed from it. Alternatively it can be an instance of an exception-type. If not provided, a LewisException with a standard message is constructed. If it is anything else, a RuntimeError is raised.

Essentially, this class helps deferring ImportErrors until anything from the module that was attempted to load is actually used.

Parameters:
  • module – Module from that symbols should be imported.

  • exception – Text for LewisException or custom exception object.

do_import(*names)[source]

Tries to import names from the module specified on initialization of the FromOptionalDependency-object. In case an ImportError occurs, the requested names are replaced with stub objects.

Parameters:

names – List of strings that are used as type names.

Returns:

Tuple of actual symbols or stub types with provided names. If there is only one element in the tuple, that element is returned.

class lewis.core.utils.check_limits(lower=None, upper=None, silent=False)[source]

Bases: object

This decorator helps to make sure that the parameter of a property setter (or any other method with one argument) is within certain numerical limits.

It’s possible to set static limits using floats or ints:

class Foo:
    _bar = 0

    @property
    def bar(self):
        return self._bar

    @bar.setter
    @check_limits(0, 15)
    def bar(self, new_value):
        self._bar = new_value

But sometimes this is not flexible enough, so it’s also possible to supply strings, which are the names of attributes of the object the decorated method belongs with:

class Foo:
    _bar = 0

    bar_min = 0
    bar_max = 24

    @property
    def bar(self):
        return self._bar

    @bar.setter
    @check_limits("bar_min", "bar_max")
    def bar(self, new_value):
        self._bar = new_value

This will make sure that the new value is always between bar_min and bar_max, even if they change at runtime. If the limit is None (default), the value will not be limited in that direction.

Upper and lower limit can also be used exclusively, for example for a property that has a lower bound but not an upper, say a temperature:

class Foo:
    _temp = 273.15

    @check_limits(lower=0)
    def set_temperature(self, t_in_kelvin):
        self._temp = t_in_kelvin

If the value is outside the specified limits, the decorated function is not called and a LimitViolationException is raised if the silent- parameter is False (default). If that option is active, the call is simply silently ignored.

Parameters:
  • lower – Numerical lower limit or name of attribute that contains limit.

  • upper – Numerical upper limit or name of attribute that contains limit.

  • silent – A limit violation will not raise an exception if this option is True.

lewis.core.utils.dict_strict_update(base_dict, update_dict) None[source]

This function updates base_dict with update_dict if and only if update_dict does not contain keys that are not already in base_dict. It is essentially a more strict interpretation of the term “updating” the dict.

If update_dict contains keys that are not in base_dict, a RuntimeError is raised.

Parameters:
  • base_dict – The dict that is to be updated. This dict is modified.

  • update_dict – The dict containing the new values.

lewis.core.utils.extract_module_name(absolute_path)[source]

This function tries to extract a valid module name from the basename of the supplied path. If it’s a directory, the directory name is returned, if it’s a file, the file name without extension is returned. If the basename starts with _ or . or it’s a file with an ending different from .py, the function returns None

Parameters:

absolute_path – Absolute path of something that might be a module.

Returns:

Module name or None.

lewis.core.utils.format_doc_text(text)[source]

A very thin wrapper around textwrap.fill to consistently wrap documentation text for display in a command line environment. The text is wrapped to 99 characters with an indentation depth of 4 spaces. Each line is wrapped independently in order to preserve manually added line breaks.

Parameters:

text – The text to format, it is cleaned by inspect.cleandoc.

Returns:

The formatted doc text.

lewis.core.utils.get_members(obj, predicate=None)[source]

Returns all members of an object for which the supplied predicate is true and that do not begin with __. Keep in mind that the supplied function must accept a potentially very broad range of inputs, because the members of an object can be of any type. The function puts those members into a dict with the member names as keys and returns it. If no predicate is supplied, all members are put into the dict.

Parameters:
  • obj – Object from which to get the members.

  • predicate – Filter function for the members, only members for which True is returned are part of the resulting dict.

Returns:

Dict with name-object pairs of members of obj for which predicate returns true.

lewis.core.utils.get_submodules(module) dict[str, ModuleType][source]

This function imports all sub-modules of the supplied module and returns a dictionary with module names as keys and the sub-module objects as values. If the supplied parameter is not a module object, a RuntimeError is raised.

Parameters:

module – Module object from which to import sub-modules.

Returns:

Dict with name-module pairs.

lewis.core.utils.seconds_since(start)[source]

This is a small helper function that returns the elapsed seconds since start using datetime.datetime.now().

Parameters:

start – Start time.

Returns:

Elapsed seconds since start time.