Source code for irfpy.util.check

''' Misc functions related to discovering typical programming errors.

.. codeauthor:: Martin Wieser

.. autosummary::
    
    warn_globals    
    np_is_module
'''
import logging as _logging
_logger = _logging.getLogger(__name__)
import functools
import inspect
import sys

[docs]def warn_globals(func): """ Warns when accessing globals. Decorator that warns if a function accesses global "variables", i.e. objects that are not callable and that not other modules. >>> import irfpy.util.check as check >>> b = 1 # A global variable >>> >>> @check.warn_globals ... def myfunc(a): ... print(b) # b is a global variable, which has not passed as parameter to myfunc. Probably, the intention of the developer is to refer to ``a``. >>> myfunc(a=15) 1 When ``myfunc`` is called, ``myfunc`` referred to ``b``, a global variable. The decorator makes a warning as ``Warning: in 'myfunc': accessing global 'b'``. The warning is shown using the python logging system with the logger name of ``irfpy.util.check`` and the level of ``warning``. """ def warn_not_callable_globals(thing): thing = getattr(thing, 'im_func', thing) thing = getattr(thing, '__func__', thing) thing = getattr(thing, 'func_code', thing) thing = getattr(thing, '__code__', thing) frame = inspect.currentframe() try: callerglobals = getattr(frame.f_back.f_back, 'f_globals', {}) finally: del frame # avoid reference cycles: # https://docs.python.org/3/library/inspect.html#the-interpreter-stack for name in thing.co_names: if name in callerglobals: obj = callerglobals[name] if ((not callable(obj)) and \ (not inspect.ismodule(obj)) and \ (not inspect.isfunction(obj)) and \ (not inspect.isclass(obj))): _logger.warning("Warning: in '{}': accessing global '{}'\n".format(thing.co_name,name)) @functools.wraps(func) def wrapper_global_warn(*args, **kwargs): warn_not_callable_globals(func) return func(*args, **kwargs) return wrapper_global_warn
[docs]def np_is_module(): """ Verifies 'np' is a module. Verifies that the object 'np', if it exists, refers to a module (typically numpy) and is not the proton number density defined often as np in old (and odd) scripts. Call anywhere, e.g. at end of your script. Typical use case is as follows. The below function ``myfunc`` >>> import numpy as np # A typical use of numpy At a certain point, you (unintentionally) overwrite ``np`` to proton density. >>> np = 3.0 Later, you want to use ``np`` as numpy. AttributionError occurs. >>> zero_array = np.zeros(20) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... AttributeError: 'float' object has no attribute 'zeros' You can check if ``np`` is overwritten or not. Change the function as >>> from irfpy.util import check >>> check.np_is_module() You get the message :: irfpy.util.check: WARNING: Warning: 'np' is not a (numpy) module (anymore). The warning is shown using the python logging system with the logger name of ``irfpy.util.check`` and the level of ``warning``. """ frame = inspect.currentframe() try: callerglobals = getattr(frame.f_back, 'f_globals', {}) finally: del frame # avoid reference cycles: # https://docs.python.org/3/library/inspect.html#the-interpreter-stack if 'np' in callerglobals: obj = callerglobals['np'] if not inspect.ismodule(obj): _logger.warning("Warning: 'np' is not a (numpy) module (anymore).\n")