"""Import statement completions.Contains modified code from the IPython project (at core/completerlib.py).# Copyright (c) IPython Development Team.# Distributed under the terms of the Modified BSD License."""importinspectimportosimportreimportsysfromimportlibimportimport_modulefromimportlib.machineryimportall_suffixesfromtimeimporttimefromzipimportimportzipimporterfromxonsh.built_insimportXSHfromxonsh.completers.toolsimport(RichCompletion,contextual_completer,get_filter_function,)fromxonsh.lib.lazyasdimportlazyobjectfromxonsh.parsers.completion_contextimportCompletionContext_suffixes=all_suffixes()# Time in seconds after which we give upTIMEOUT_GIVEUP=2@lazyobjectdefIMPORT_RE():# Regular expression for the python import statementsuffixes=r"|".join(re.escape(s)forsin_suffixes)returnre.compile(r"(?P<name>[^\W\d]\w*?)"r"(?P<package>[/\\]__init__)?"rf"(?P<suffix>{suffixes})$")
[docs]defmodule_list(path):""" Return the list containing the names of the modules available in the given folder. """# sys.path has the cwd as an empty string, but isdir/listdir need it as '.'ifpath=="":path="."# A few local constants to be used in loops belowpjoin=os.path.joinifos.path.isdir(path):# Build a list of all files in the directory and all files# in its subdirectories. For performance reasons, do not# recurse more than one level into subdirectories.files=[]forroot,dirs,nondirsinos.walk(path,followlinks=True):subdir=root[len(path)+1:]ifsubdir:files.extend(pjoin(subdir,f)forfinnondirs)dirs[:]=[]# Do not recurse into additional subdirectories.else:files.extend(nondirs)else:try:files=list(zipimporter(path)._files.keys())except:# noqafiles=[]# Build a list of modules which match the import_re regex.modules=[]forfinfiles:m=IMPORT_RE.match(f)ifm:modules.append(m.group("name"))returnlist(set(modules))
[docs]defget_root_modules():""" Returns a list containing the names of all the modules available in the folders of the pythonpath. """rootmodules_cache=XSH.modules_cacherootmodules=list(sys.builtin_module_names)start_time=time()forpathinsys.path:try:modules=rootmodules_cache[path]exceptKeyError:modules=module_list(path)try:modules.remove("__init__")exceptValueError:passifpathnotin("","."):# cwd modules should not be cachedrootmodules_cache[path]=modulesiftime()-start_time>TIMEOUT_GIVEUP:print("\nwarning: Getting root modules is taking too long, we give up")return[]rootmodules.extend(modules)rootmodules=list(set(rootmodules))returnrootmodules
[docs]defis_possible_submodule(module,attr):try:obj=getattr(module,attr)exceptAttributeError:# Is possilby an unimported submodulereturnTrueexceptTypeError:# https://github.com/ipython/ipython/issues/9678returnFalsereturninspect.ismodule(obj)
[docs]deftry_import(mod:str,only_modules=False)->list[str]:""" Try to import given module and return list of potential completions. """mod=mod.rstrip(".")try:m=import_module(mod)exceptException:return[]m_is_init="__init__"in(getattr(m,"__file__","")or"")completions=[]if(nothasattr(m,"__file__"))or(notonly_modules)orm_is_init:completions.extend([attrforattrindir(m)ifis_importable(m,attr,only_modules)])m_all=getattr(m,"__all__",[])ifonly_modules:completions.extend(attrforattrinm_allifis_possible_submodule(m,attr))else:completions.extend(m_all)ifm_is_init:ifm.__file__:completions.extend(module_list(os.path.dirname(m.__file__)))completions_set={cforcincompletionsifisinstance(c,str)}completions_set.discard("__init__")returnlist(completions_set)
[docs]@contextual_completerdefcomplete_import(context:CompletionContext):""" Completes module names and objects for "import ..." and "from ... import ...". """ifnot(context.commandandcontext.python):# Imports are only possible in independent lines (not in `$()` or `@()`).# This means it's python code, but also can be a command as far as the parser is concerned.returnNonecommand=context.commandifcommand.opening_quote:# can't have a quoted importreturnNonearg_index=command.arg_indexprefix=command.prefixargs=command.argsifarg_index==1andargs[0].value=="from":# completing module to importreturncomplete_module(prefix)ifarg_index>=1andargs[0].value=="import":# completing module to import, might be multiple modulesprefix=prefix.rsplit(",",1)[-1]returncomplete_module(prefix),len(prefix)ifarg_index==2andargs[0].value=="from":return{RichCompletion("import",append_space=True)}ifarg_index>2andargs[0].value=="from"andargs[2].value=="import":# complete thing inside a module, might be multiple objectsmodule=args[1].valueprefix=prefix.rsplit(",",1)[-1]returnfilter_completions(prefix,try_import(module)),len(prefix)returnset()