2023-09-05 11:04:27 +10:00

181 lines
5.3 KiB
Python

# SPDX-FileCopyrightText: 2009-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2009 Fernando Perez, https://www.stani.be
# Original copyright (see doc-string):
# ****************************************************************************
# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
# ****************************************************************************
"""Completer for import statements
Original code was from IPython/Extensions/ipy_completers.py. The following
changes have been made:
- ported to python3
- pep8 polishing
- limit list of modules to prefix in case of "from w"
- sorted modules
- added sphinx documentation
- complete() returns a blank list of the module isn't found
"""
import os
import sys
TIMEOUT_STORAGE = 3 # Time in secs after which the root-modules will be stored
TIMEOUT_GIVEUP = 20 # Time in secs after which we give up
ROOT_MODULES = None
def get_root_modules():
"""
Returns a list containing the names of all the modules available in the
folders of the python-path.
:returns: modules
:rtype: list
"""
global ROOT_MODULES
modules = []
if not (ROOT_MODULES is None):
return ROOT_MODULES
from time import time
t = time()
store = False
for path in sys.path:
modules += module_list(path)
if time() - t >= TIMEOUT_STORAGE and not store:
# Caching the list of root modules, please wait!
store = True
if time() - t > TIMEOUT_GIVEUP:
# This is taking too long, we give up.
ROOT_MODULES = []
return []
modules += sys.builtin_module_names
# needed for modules defined in C
modules += sys.modules.keys()
modules = set(modules)
modules.discard("__init__")
modules = sorted(list(modules))
if store:
ROOT_MODULES = modules
return modules
def module_list(path):
"""
Return the list containing the names of the modules available in
the given folder.
:arg path: folder path
:type path: str
:returns: modules
:rtype: list
"""
if os.path.isdir(path):
folder_list = os.listdir(path)
elif path.endswith('.egg'):
from zipimport import zipimporter
try:
folder_list = [f for f in zipimporter(path)._files]
except:
folder_list = []
else:
folder_list = []
# folder_list = glob.glob(os.path.join(path,'*'))
folder_list = [
p for p in folder_list
if (os.path.exists(os.path.join(path, p, '__init__.py')) or
p[-3:] in {'.py', '.so'} or
p[-4:] in {'.pyc', '.pyo', '.pyd'})]
folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
return folder_list
def complete(line):
"""
Returns a list containing the completion possibilities for an import line.
:arg line:
incomplete line which contains an import statement::
import xml.d
from xml.dom import
:type line: str
:returns: list of completion possibilities
:rtype: list
>>> complete('import weak')
['weakref']
>>> complete('from weakref import C')
['CallableProxyType']
"""
import inspect
def try_import(mod, *, only_modules=False):
def is_importable(module, attr):
if only_modules:
return inspect.ismodule(getattr(module, attr))
else:
return not (attr[:2] == '__' and attr[-2:] == '__')
try:
m = __import__(mod)
except:
return []
mods = mod.split('.')
for module in mods[1:]:
m = getattr(m, module)
if (not hasattr(m, '__file__')) or (not only_modules) or\
(hasattr(m, '__file__') and '__init__' in m.__file__):
completion_list = [attr for attr in dir(m)
if is_importable(m, attr)]
else:
completion_list = []
completion_list.extend(getattr(m, '__all__', []))
if hasattr(m, '__file__') and '__init__' in m.__file__:
completion_list.extend(module_list(os.path.dirname(m.__file__)))
completion_list = list(set(completion_list))
if '__init__' in completion_list:
completion_list.remove('__init__')
return completion_list
def filter_prefix(names, prefix):
return [name for name in names if name.startswith(prefix)]
words = line.split(' ')
if len(words) == 3 and words[0] == 'from':
return ['import ']
if len(words) < 3 and (words[0] in {'import', 'from'}):
if len(words) == 1:
return get_root_modules()
mod = words[1].split('.')
if len(mod) < 2:
return filter_prefix(get_root_modules(), words[-1])
completion_list = try_import('.'.join(mod[:-1]), only_modules=True)
completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
return filter_prefix(completion_list, words[-1])
if len(words) >= 3 and words[0] == 'from':
mod = words[1]
return filter_prefix(try_import(mod), words[-1])
# get here if the import is not found
# import invalid_module
# ^, in this case return nothing
return []