Source code for radical.utils.modules


import builtins
import inspect
import os
import pkgutil

import importlib.util as imputil

from typing import Any, Union, Optional

_id_cnt = 0


# ------------------------------------------------------------------------------
#
# to keep RU 2.6 compatible, we provide import_module which works around some
# quirks of __import__ when being used with dotted names. This is what the
# python docs recommend to use.  This basically steps down the module path and
# loads the respective submodule until arriving at the target.
#
[docs]def import_module(name): mod = __import__(name) for s in name.split('.')[1:]: mod = getattr(mod, s) return mod
# ------------------------------------------------------------------------------ # # as import_module, but without the import part :-P #
[docs]def find_module(name): package = pkgutil.get_loader(name) if not package: return None if '_NamespaceLoader' in str(package): # since Python 3.5, loaders differ between modules and namespaces return package._path._path[0] # pylint: disable=W0212 else: return os.path.dirname(package.get_filename())
# ------------------------------------------------------------------------------ # # a helper to load functions and classes from user provided source file which # are *not* installed as modules. All symbols from that file are loaded, and # returned is a dictionary with the following structure: # # symbols = {'classes' : {'Foo': <class 'mod_0001.Foo'>, # 'Bar': <class 'mod_0001.Bar'>, # ... # }, # 'functions': {'foo': <function foo at 0x7f532d241d40>, # 'bar': <function bar at 0x7f532d241d40>, # ... # } # } #
[docs]def import_file(path): global _id_cnt _id_cnt += 1 uid = 'mod_%d' % _id_cnt spec = imputil.spec_from_file_location(uid, path) mod = imputil.module_from_spec(spec) spec.loader.exec_module(mod) symbols = {'functions': dict(), 'classes' : dict()} for k,v in mod.__dict__.items(): if not k.startswith('__'): if inspect.isclass(v): symbols['classes' ][k] = v if inspect.isfunction(v): symbols['functions'][k] = v return symbols
# ------------------------------------------------------------------------------ #
[docs]def get_type(type_name: str) -> Optional[type]: ''' get a type object from a type name (str) ''' # check builtin types ret = getattr(builtins, type_name, None) if isinstance(ret, type): return ret # check global types ret = globals().get(type_name) if isinstance(ret, type): return ret # check local types of the calling frame ret = inspect.currentframe().f_back.f_locals.get(type_name) if isinstance(ret, type): return ret
# ------------------------------------------------------------------------------ #
[docs]def load_class(fpath: str, cname: str, ctype: Optional[Union[type,str]] = None) -> Optional[Any]: ''' load class `cname` from a source file at location `fpath` and return it (the class, not an instance). ''' if not os.path.isfile(fpath): raise ValueError('no source file at [%s]' % fpath) pname = os.path.splitext(os.path.basename(fpath))[0] spec = imputil.spec_from_file_location(pname, fpath) plugin = imputil.module_from_spec(spec) spec.loader.exec_module(plugin) ret = getattr(plugin, cname) if ctype: if isinstance(ctype, str): ctype_name = ctype ctype = get_type(ctype_name) if not ctype: raise ValueError('cannot type check %s' % ctype_name) if not issubclass(ret, ctype): return None return ret
# ------------------------------------------------------------------------------