def _populate_existing_modnames(self, names): # Populates any name that corresponds to a top-level module from xdoctest import static_analysis as static for n in names: if n not in self.known: if static.modname_to_modpath(n) is not None: self.known[n] = 'import {}'.format(n)
def _module_exists(modname): if modname not in _MODNAME_EXISTS_CACHE: from xdoctest import static_analysis as static modpath = static.modname_to_modpath(modname) exists_flag = modpath is not None _MODNAME_EXISTS_CACHE[modname] = exists_flag exists_flag = _MODNAME_EXISTS_CACHE[modname] return exists_flag
def _rectify_to_modpath(modpath_or_name): """ if modpath_or_name is a name, statically converts it to a path """ modpath = static.modname_to_modpath(modpath_or_name) if modpath is None: if exists(modpath_or_name): modpath = modpath_or_name else: raise ValueError('Cannot find module={}'.format(modpath_or_name)) return modpath
def _static_modname_to_modpath(modname, **kwargs): # Calls static.modname_to_modpath with checks had = modname in sys.modules try: modpath = static.modname_to_modpath(modname, **kwargs) except ValueError: modpath = None if not had: assert modname not in sys.modules, ( '{} should not be imported'.format(modname)) return modpath
def expand_module(path): # if True or filetype in {'py', 'pyx'}: # filetype = get_current_filetype() from xdoctest import static_analysis as static try: path = static.modname_to_modpath(path) # print('rectified module to path = {!r}'.format(path)) except Exception as ex: # if True or filetype in {'py', 'pyx'}: return None return path
def _static_parse_imports(modpath, imports=None, use_all=True): # from ubelt.meta import static_analysis as static # TODO: port some of this functionality over from xdoctest import static_analysis as static modname = static.modpath_to_modname(modpath) if imports is not None: import_paths = { m: static.modname_to_modpath(modname + '.' + m, hide_init=False) for m in imports } else: imports = [] import_paths = {} for sub_modpath in static.package_modpaths(modpath, with_pkg=True, recursive=False): # print('sub_modpath = {!r}'.format(sub_modpath)) sub_modname = static.modpath_to_modname(sub_modpath) rel_modname = sub_modname[len(modname) + 1:] if rel_modname.startswith('_'): continue if not rel_modname: continue import_paths[rel_modname] = sub_modpath imports.append(rel_modname) imports = sorted(imports) from_imports = [] for rel_modname in imports: sub_modpath = import_paths[rel_modname] with open(sub_modpath, 'r') as file: source = file.read() valid_callnames = None if use_all: try: valid_callnames = static.parse_static_value('__all__', source) except NameError: pass if valid_callnames is None: # The __all__ variable is not specified or we dont care top_level = static.TopLevelVisitor.parse(source) attrnames = list(top_level.assignments) + list( top_level.calldefs.keys()) # list of names we wont export by default invalid_callnames = dir(builtins) valid_callnames = [] for attr in attrnames: if '.' in attr or attr.startswith('_'): continue if attr in invalid_callnames: continue valid_callnames.append(attr) from_imports.append((rel_modname, sorted(valid_callnames))) return modname, imports, from_imports
def _rectify_to_modpath(modpath_or_name): """ if modpath_or_name is a name, statically converts it to a path """ modpath = static_analysis.modname_to_modpath(modpath_or_name) if modpath is None: if six.PY2: if modpath_or_name.endswith('.pyc'): modpath_or_name = modpath_or_name[:-1] if exists(modpath_or_name): modpath = modpath_or_name else: raise ValueError('Cannot find module={}'.format(modpath_or_name)) return modpath
def modname_to_modpath(modname, hide_init=True, hide_main=True): # nocover r""" Finds the path to a python module from its name. Determines the path to a python module without directly import it Converts the name of a module (__name__) to the path (__file__) where it is located without importing the module. Returns None if the module does not exist. Args: modname (str): module filepath hide_init (bool): if False, __init__.py will be returned for packages hide_main (bool): if False, and hide_init is True, __main__.py will be returned for packages, if it exists. Returns: str: modpath - path to the module, or None if it doesn't exist CommandLine: python -m ubelt.util_import modname_to_modpath Example: >>> from ubelt.util_import import * # NOQA >>> import sys >>> modname = 'ubelt.progiter' >>> already_exists = modname in sys.modules >>> modpath = modname_to_modpath(modname) >>> print('modpath = %r' % (modpath,)) >>> assert already_exists or modname not in sys.modules Example: >>> from ubelt.util_import import * # NOQA >>> import sys >>> modname = 'ubelt.__main__' >>> modpath = modname_to_modpath(modname, hide_main=False) >>> print('modpath = %r' % (modpath,)) >>> assert modpath.endswith('__main__.py') >>> modname = 'ubelt' >>> modpath = modname_to_modpath(modname, hide_init=False) >>> print('modpath = %r' % (modpath,)) >>> assert modpath.endswith('__init__.py') >>> modname = 'ubelt' >>> modpath = modname_to_modpath(modname, hide_init=False, hide_main=False) >>> print('modpath = %r' % (modpath,)) >>> assert modpath.endswith('__init__.py') """ from xdoctest import static_analysis as static return static.modname_to_modpath(modname, hide_init, hide_main)
def autogen_init(modpath_or_name, imports=None, attrs=True, use_all=True, dry=False): """ Autogenerates imports for a package __init__.py file. Args: modpath_or_name (str): path to or name of a package module. The path should reference the dirname not the __init__.py file. If specified by name, must be findable from the PYTHONPATH. imports (list): if specified, then only these specific submodules are used in package generation. Otherwise, all non underscore prefixed modules are used. attrs (bool): if False, then module attributes will not be imported. use_all (bool): if False the `__all__` attribute is ignored. dry (bool): if True, the autogenerated string is not written Notes: This will partially override the __init__ file. By default everything up to the last comment / __future__ import is preserved, and everything after is overriden. For more fine grained control, you can specify XML-like `# <AUTOGEN_INIT>` and `# </AUTOGEN_INIT>` comments around the volitle area. If specified only the area between these tags will be overwritten. To autogenerate a module on demand, its useful to keep a doctr comment in the __init__ file like this: python -c "import ubelt._internal as a; a.autogen_init('<your_module_path_or_name>')" """ # from ubelt import util_import from xdoctest import static_analysis as static if exists(modpath_or_name): modpath = modpath_or_name else: modpath = static.modname_to_modpath(modpath_or_name) if modpath is None: raise ValueError('Invalid module {}'.format(modpath_or_name)) if imports is None: # the __init__ file may have a variable describing the correct imports # should imports specify the name of this variable or should it always # be GLOBAL_MODULES? with open(join(modpath, '__init__.py'), 'r') as file: source = file.read() varname = 'GLOBAL_MODULES' try: imports = static.parse_static_value(varname, source) except NameError: pass modname, imports, from_imports = _static_parse_imports(modpath, imports=imports, use_all=use_all) if not attrs: from_imports = [] initstr = _initstr(modname, imports, from_imports, withheader=False) if dry: print(initstr) else: _autogen_init_write(modpath, initstr)