Module pdocs.extract
View Source
import importlib import os import pkgutil import typing import pdocs.doc class ExtractError(Exception): pass def split_module_spec(spec: str) -> typing.Tuple[str, str]: """ Splits a module specification into a base path (which may be empty), and a module name. Raises ExtactError if the spec is invalid. """ if not spec: raise ExtractError("Empty module spec.") if (os.sep in spec) or (os.altsep and os.altsep in spec): dirname, fname = os.path.split(spec) if fname.endswith(".py"): mname, _ = os.path.splitext(fname) return dirname, mname else: if "." in fname: raise ExtractError( "Invalid module name {fname}. Mixing path and module specifications " "is not supported.".format(fname=fname) ) return dirname, fname else: return "", spec def load_module(basedir: str, module: str) -> typing.Tuple[typing.Any, bool]: """ Returns a module object, and whether the module is a package or not. """ ispackage = False if basedir: mods = module.split(".") dirname = os.path.join(basedir, *mods[:-1]) modname = mods[-1] pkgloc = os.path.join(dirname, modname, "__init__.py") fileloc = os.path.join(dirname, modname + ".py") if os.path.exists(pkgloc): location, ispackage = pkgloc, True elif os.path.exists(fileloc): location, ispackage = fileloc, False else: raise ExtractError( "Module {module} not found in {basedir}".format(module=module, basedir=basedir) ) ispec = importlib.util.spec_from_file_location(modname, location) mobj = importlib.util.module_from_spec(ispec) try: # This can literally raise anything ispec.loader.exec_module(mobj) # type: ignore except Exception as e: raise ExtractError("Error importing {location}: {e}".format(location=location, e=e)) return mobj, ispackage else: try: # This can literally raise anything m = importlib.import_module(module) except ImportError: raise ExtractError("Module not found: {module}".format(module=module)) except Exception as e: raise ExtractError("Error importing {module}: {e}".format(module=module, e=e)) # This is the only case where we actually have to test whether we're a package if getattr(m, "__package__", False) and getattr(m, "__path__", False): ispackage = True return m, ispackage def submodules(dname: str, mname: str) -> typing.Sequence[str]: """ Returns a list of fully qualified submodules within a package, given a base directory and a fully qualified module name. """ loc = os.path.join(dname, *mname.split(".")) ret = [] for mi in pkgutil.iter_modules([loc], prefix=mname + "."): if isinstance(mi, tuple): # Python 3.5 compat ret.append(mi[1]) else: ret.append(mi.name) ret.sort() return ret def _extract_module(dname: str, mname: str, parent=None) -> typing.Any: m, pkg = load_module(dname, mname) mod = pdocs.doc.Module(mname, m, parent) if pkg: for submodule_full_name in submodules(dname, mname): if submodule_full_name.split(".")[-1].startswith("_"): continue mod.submodules.append(_extract_module(dname, submodule_full_name, parent=mod)) return mod def extract_module(spec: str): """ Extracts and returns a module object. The spec argument can have the following forms: Simple module: "foo.bar" Module path: "./path/to/module" File path: "./path/to/file.py" This function always invalidates caches to enable hot load and reload. May raise ExtactError. """ importlib.invalidate_caches() dname, mname = split_module_spec(spec) return _extract_module(dname, mname)
Functions
extract_module
def ( spec: str )
Extracts and returns a module object. The spec argument can have the following forms:
Simple module: "foo.bar" Module path: "./path/to/module" File path: "./path/to/file.py"
This function always invalidates caches to enable hot load and reload.
May raise ExtactError.
View Source
def extract_module(spec: str): """ Extracts and returns a module object. The spec argument can have the following forms: Simple module: "foo.bar" Module path: "./path/to/module" File path: "./path/to/file.py" This function always invalidates caches to enable hot load and reload. May raise ExtactError. """ importlib.invalidate_caches() dname, mname = split_module_spec(spec) return _extract_module(dname, mname)
load_module
def ( basedir: str, module: str ) -> Tuple[Any, bool]
Returns a module object, and whether the module is a package or not.
View Source
def load_module(basedir: str, module: str) -> typing.Tuple[typing.Any, bool]: """ Returns a module object, and whether the module is a package or not. """ ispackage = False if basedir: mods = module.split(".") dirname = os.path.join(basedir, *mods[:-1]) modname = mods[-1] pkgloc = os.path.join(dirname, modname, "__init__.py") fileloc = os.path.join(dirname, modname + ".py") if os.path.exists(pkgloc): location, ispackage = pkgloc, True elif os.path.exists(fileloc): location, ispackage = fileloc, False else: raise ExtractError( "Module {module} not found in {basedir}".format(module=module, basedir=basedir) ) ispec = importlib.util.spec_from_file_location(modname, location) mobj = importlib.util.module_from_spec(ispec) try: # This can literally raise anything ispec.loader.exec_module(mobj) # type: ignore except Exception as e: raise ExtractError("Error importing {location}: {e}".format(location=location, e=e)) return mobj, ispackage else: try: # This can literally raise anything m = importlib.import_module(module) except ImportError: raise ExtractError("Module not found: {module}".format(module=module)) except Exception as e: raise ExtractError("Error importing {module}: {e}".format(module=module, e=e)) # This is the only case where we actually have to test whether we're a package if getattr(m, "__package__", False) and getattr(m, "__path__", False): ispackage = True return m, ispackage
split_module_spec
def ( spec: str ) -> Tuple[str, str]
Splits a module specification into a base path (which may be empty), and a module name.
Raises ExtactError if the spec is invalid.
View Source
def split_module_spec(spec: str) -> typing.Tuple[str, str]: """ Splits a module specification into a base path (which may be empty), and a module name. Raises ExtactError if the spec is invalid. """ if not spec: raise ExtractError("Empty module spec.") if (os.sep in spec) or (os.altsep and os.altsep in spec): dirname, fname = os.path.split(spec) if fname.endswith(".py"): mname, _ = os.path.splitext(fname) return dirname, mname else: if "." in fname: raise ExtractError( "Invalid module name {fname}. Mixing path and module specifications " "is not supported.".format(fname=fname) ) return dirname, fname else: return "", spec
submodules
def ( dname: str, mname: str ) -> Sequence[str]
Returns a list of fully qualified submodules within a package, given a base directory and a fully qualified module name.
View Source
def submodules(dname: str, mname: str) -> typing.Sequence[str]: """ Returns a list of fully qualified submodules within a package, given a base directory and a fully qualified module name. """ loc = os.path.join(dname, *mname.split(".")) ret = [] for mi in pkgutil.iter_modules([loc], prefix=mname + "."): if isinstance(mi, tuple): # Python 3.5 compat ret.append(mi[1]) else: ret.append(mi.name) ret.sort() return ret
Classes
ExtractError
class ( /, *args, **kwargs )
Common base class for all non-exit exceptions.
View Source
class ExtractError(Exception): pass
Ancestors (in MRO)
- builtins.Exception
- builtins.BaseException
Class variables
args
Methods
with_traceback
def ( ... )
Exception.with_traceback(tb) -- set self.traceback to tb and return self.