Source code for minkit.base.core

########################################
# MIT License
#
# Copyright (c) 2020 Miguel Ramos Pernas
########################################
'''
Definition of the backend where to store the data and run the jobs.
'''
import contextlib
import importlib
import inspect
import logging
import math
import numpy as np
import os
import pkgutil
import time

__all__ = ['timer']


logger = logging.getLogger(__name__)

# Global random number generator (not used for sampling)
GLOBAL_RND_GEN = np.random.RandomState(4587)


class DocMeta(type):

    def __init__(cls, name, bases, namespace):
        '''
        Metaclass that allows to inherit the docstring from the first base
        with an available docstring for that method.
        '''
        super().__init__(name, bases, namespace)

        # Iterate over the instances in the class
        for attr, obj in namespace.items():

            if obj.__doc__:
                continue  # skip existing methods with docstrings

            for base in filter(lambda b: hasattr(b, attr), bases):

                f = getattr(base, attr)

                if f.__doc__:
                    obj.__doc__ = f.__doc__
                    break  # we set that from the first we find


def get_exposed_package_objects(path):
    '''
    Process a given path, taking all the exposed objects in it and returning
    a dictionary with their names and respective pointers.

    :param path: path to the package.
    :type path: str
    :returns: Names and objects that are exposed.
    :rtype: dict(str, object)
    '''
    pkg = os.path.normpath(path[path.rfind('minkit'):]).replace(os.sep, '.')

    dct = {}

    for loader, module_name, ispkg in pkgutil.walk_packages([path]):

        if module_name.endswith('setup') or module_name.endswith('__'):
            continue

        # Import all classes and functions
        mod = importlib.import_module('.' + module_name, package=pkg)

        for n, c in inspect.getmembers(mod):
            if n in mod.__all__:
                dct[n] = c

    return dct


# Allowed mathematical objects
MATH_OBJECTS = {n: f for n, f in inspect.getmembers(math, inspect.isbuiltin)}
MATH_OBJECTS['pi'] = math.pi
MATH_OBJECTS['max'] = max
MATH_OBJECTS['min'] = min


def eval_math_expression(expression):
    '''
    Evaluate a mathematical expression on the safest manner possible. Only
    functions defined in the "math" module are allowed.

    :param expression: input expression.
    :type expression: str
    :returns: evaluation of the expression.
    :raises NameError: If an identifier is found in the string that is not
       allowed as a mathematical expression.
    '''
    code = compile(expression, '', 'eval')

    for name in code.co_names:
        if name not in MATH_OBJECTS:
            raise NameError(
                f'Use of {name} not allowed in a mathematical expression; functions and constants allowed: {sorted(MATH_OBJECTS.keys())}')

    return eval(code, {'__builtins__': {}}, MATH_OBJECTS)


[docs]@contextlib.contextmanager def timer(): ''' Create an object that, on exit, displays the time elapsed. ''' start = time.time() yield end = time.time() hours, rem = divmod(end - start, 3600) minutes, seconds = divmod(rem, 60) logger.info(f'Time elapsed: {hours}h {minutes}m {seconds}s')