1
0
forked from VimPlug/jedi

Merge branch 'master' into typeshed

This commit is contained in:
Dave Halter
2018-08-04 23:42:17 +02:00
23 changed files with 210 additions and 140 deletions

View File

@@ -5,7 +5,7 @@ from textwrap import dedent
import operator as op
from collections import namedtuple
from jedi._compatibility import unicode, is_py3, is_py34, builtins, \
from jedi._compatibility import unicode, is_py3, builtins, \
py_version, force_unicode, print_to_stderr
from jedi.evaluate.compiled.getattr_static import getattr_static
@@ -31,10 +31,9 @@ NOT_CLASS_TYPES = (
if is_py3:
NOT_CLASS_TYPES += (
types.MappingProxyType,
types.SimpleNamespace
types.SimpleNamespace,
types.DynamicClassAttribute,
)
if is_py34:
NOT_CLASS_TYPES += (types.DynamicClassAttribute,)
# Those types don't exist in typing.
@@ -141,7 +140,7 @@ def load_module(evaluator, dotted_name, sys_path):
__import__(dotted_name)
except ImportError:
# If a module is "corrupt" or not really a Python module or whatever.
print_to_stderr('Module %s not importable.' % dotted_name)
print_to_stderr('Module %s not importable in path %s.' % (dotted_name, sys_path))
return None
except Exception:
# Since __import__ pretty much makes code execution possible, just

View File

@@ -447,8 +447,11 @@ def create_from_name(evaluator, compiled_object, name):
pass
access = compiled_object.access_handle.getattr(name, default=None)
parent_context = compiled_object
if parent_context.is_class():
parent_context = parent_context.parent_context
return create_cached_compiled_object(
evaluator, access, parent_context=compiled_object, faked=faked
evaluator, access, parent_context=parent_context, faked=faked
)

View File

@@ -15,9 +15,15 @@ import errno
import weakref
import traceback
from functools import partial
from threading import Thread
try:
from queue import Queue, Empty
except ImportError:
from Queue import Queue, Empty # python 2.7
from jedi._compatibility import queue, is_py3, force_unicode, \
pickle_dump, pickle_load, GeneralizedPopen
from jedi import debug
from jedi.cache import memoize_method
from jedi.evaluate.compiled.subprocess import functions
from jedi.evaluate.compiled.access import DirectObjectAccess, AccessPath, \
@@ -28,6 +34,12 @@ from jedi.api.exceptions import InternalError
_MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py')
def _enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
def _get_function(name):
return getattr(functions, name)
@@ -135,6 +147,7 @@ class CompiledSubprocess(object):
@property
@memoize_method
def _process(self):
debug.dbg('Start environment subprocess %s', self._executable)
parso_path = sys.modules['parso'].__file__
args = (
self._executable,
@@ -142,7 +155,7 @@ class CompiledSubprocess(object):
os.path.dirname(os.path.dirname(parso_path)),
'.'.join(str(x) for x in sys.version_info[:3]),
)
return GeneralizedPopen(
process = GeneralizedPopen(
args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -151,6 +164,14 @@ class CompiledSubprocess(object):
# (this is already the case on Python 3).
bufsize=-1
)
self._stderr_queue = Queue()
self._stderr_thread = t = Thread(
target=_enqueue_output,
args=(process.stderr, self._stderr_queue)
)
t.daemon = True
t.start()
return process
def run(self, evaluator, function, args=(), kwargs={}):
# Delete old evaluators.
@@ -219,6 +240,16 @@ class CompiledSubprocess(object):
stderr,
))
while True:
# Try to do some error reporting from the subprocess and print its
# stderr contents.
try:
line = self._stderr_queue.get_nowait()
line = line.decode('utf-8', 'replace')
debug.warning('stderr output: %s' % line.rstrip('\n'))
except Empty:
break
if is_exception:
# Replace the attribute error message with a the traceback. It's
# way more informative.
@@ -282,11 +313,9 @@ class Listener(object):
def listen(self):
stdout = sys.stdout
# Mute stdout/stderr. Nobody should actually be able to write to those,
# because stdout is used for IPC and stderr will just be annoying if it
# leaks (on module imports).
# Mute stdout. Nobody should actually be able to write to it,
# because stdout is used for IPC.
sys.stdout = open(os.devnull, 'w')
sys.stderr = open(os.devnull, 'w')
stdin = sys.stdin
if sys.version_info[0] > 2:
stdout = stdout.buffer

View File

@@ -11,7 +11,7 @@ from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments
from jedi.cache import memoize_method
from jedi.evaluate.context.function import FunctionExecutionContext, \
FunctionContext, AbstractFunction
from jedi.evaluate.context.klass import ClassContext, apply_py__get__
from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter
from jedi.evaluate.context import iterable
from jedi.parser_utils import get_parent_scope
@@ -87,7 +87,7 @@ class AbstractInstanceContext(Context):
def execute_function_slots(self, names, *evaluated_args):
return ContextSet.from_sets(
name.execute_evaluated(*evaluated_args)
name.infer().execute_evaluated(*evaluated_args)
for name in names
)
@@ -169,7 +169,7 @@ class AbstractInstanceContext(Context):
def create_init_executions(self):
for name in self.get_function_slot_names(u'__init__'):
if isinstance(name, SelfName):
if isinstance(name, LazyInstanceClassName):
function = FunctionContext.from_context(
self.parent_context,
name.tree_name.parent
@@ -265,17 +265,23 @@ class AnonymousInstance(TreeInstance):
class CompiledInstanceName(compiled.CompiledName):
def __init__(self, evaluator, instance, parent_context, name):
super(CompiledInstanceName, self).__init__(evaluator, parent_context, name)
def __init__(self, evaluator, instance, klass, name):
super(CompiledInstanceName, self).__init__(
evaluator,
klass.parent_context,
name.string_name
)
self._instance = instance
self._class = klass
self._class_member_name = name
@iterator_to_context_set
def infer(self):
for result_context in super(CompiledInstanceName, self).infer():
for result_context in self._class_member_name.infer():
is_function = result_context.api_type == 'function'
if result_context.tree_node is not None and is_function:
yield BoundMethod(self._instance, self.parent_context, result_context)
yield BoundMethod(self._instance, self._class, result_context)
else:
if is_function:
yield CompiledBoundMethod(result_context)
@@ -283,20 +289,26 @@ class CompiledInstanceName(compiled.CompiledName):
yield result_context
class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
class CompiledInstanceClassFilter(filters.AbstractFilter):
name_class = CompiledInstanceName
def __init__(self, evaluator, instance, compiled_object):
super(CompiledInstanceClassFilter, self).__init__(
evaluator,
compiled_object,
is_instance=True,
)
def __init__(self, evaluator, instance, klass):
self._evaluator = evaluator
self._instance = instance
self._class = klass
self._class_filter = next(klass.get_filters(is_instance=True))
def _create_name(self, name):
return self.name_class(
self._evaluator, self._instance, self._compiled_object, name)
def get(self, name):
return self._convert(self._class_filter.get(name))
def values(self):
return self._convert(self._class_filter.values())
def _convert(self, names):
return [
CompiledInstanceName(self._evaluator, self._instance, self._class, n)
for n in names
]
class BoundMethod(AbstractFunction):
@@ -332,11 +344,6 @@ class CompiledBoundMethod(compiled.CompiledObject):
return list(super(CompiledBoundMethod, self).get_param_names())[1:]
class InstanceNameDefinition(filters.TreeNameDefinition):
def infer(self):
return super(InstanceNameDefinition, self).infer()
class SelfName(filters.TreeNameDefinition):
"""
This name calculates the parent_context lazily.
@@ -351,10 +358,15 @@ class SelfName(filters.TreeNameDefinition):
return self._instance.create_instance_context(self.class_context, self.tree_name)
class LazyInstanceClassName(SelfName):
class LazyInstanceClassName(object):
def __init__(self, instance, class_context, class_member_name):
self._instance = instance
self.class_context = class_context
self._class_member_name = class_member_name
@iterator_to_context_set
def infer(self):
for result_context in super(LazyInstanceClassName, self).infer():
for result_context in self._class_member_name.infer():
if isinstance(result_context, FunctionContext):
# Classes are never used to resolve anything within the
# functions. Only other functions and modules will resolve
@@ -364,45 +376,51 @@ class LazyInstanceClassName(SelfName):
for c in apply_py__get__(result_context, self._instance):
yield c
def __getattr__(self, name):
return getattr(self._class_member_name, name)
class InstanceClassFilter(filters.ParserTreeFilter):
name_class = LazyInstanceClassName
class InstanceClassFilter(filters.AbstractFilter):
"""
This filter is special in that it uses the class filter and wraps the
resulting names in LazyINstanceClassName. The idea is that the class name
filtering can be very flexible and always be reflected in instances.
"""
def __init__(self, evaluator, context, class_context, origin_scope):
super(InstanceClassFilter, self).__init__(
evaluator=evaluator,
context=context,
node_context=class_context,
origin_scope=origin_scope
)
self._instance = context
self._class_context = class_context
self._class_filter = next(class_context.get_filters(
search_global=False,
origin_scope=origin_scope,
is_instance=True,
))
def _equals_origin_scope(self):
node = self._origin_scope
while node is not None:
if node == self._parser_scope or node == self.context:
return True
node = get_parent_scope(node)
return False
def get(self, name):
return self._convert(self._class_filter.get(name))
def _access_possible(self, name):
return not name.value.startswith('__') or name.value.endswith('__') \
or self._equals_origin_scope()
def values(self):
return self._convert(self._class_filter.values())
def _filter(self, names):
names = super(InstanceClassFilter, self)._filter(names)
return [name for name in names if self._access_possible(name)]
def _convert_names(self, names):
return [self.name_class(self.context, self._class_context, name) for name in names]
def _convert(self, names):
return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names]
class SelfAttributeFilter(InstanceClassFilter):
class SelfAttributeFilter(ClassFilter):
"""
This class basically filters all the use cases where `self.*` was assigned.
"""
name_class = SelfName
def __init__(self, evaluator, context, class_context, origin_scope):
super(SelfAttributeFilter, self).__init__(
evaluator=evaluator,
context=context,
node_context=class_context,
origin_scope=origin_scope,
is_instance=True,
)
self._class_context = class_context
def _filter(self, names):
names = self._filter_self_names(names)
if isinstance(self._parser_scope, compiled.CompiledObject) and False:
@@ -421,6 +439,9 @@ class SelfAttributeFilter(InstanceClassFilter):
if name.is_definition() and self._access_possible(name):
yield name
def _convert_names(self, names):
return [self.name_class(self.context, self._class_context, name) for name in names]
def _check_flows(self, names):
return names

View File

@@ -38,6 +38,7 @@ py__doc__(include_call_signature: Returns the docstring for a context.
"""
from jedi._compatibility import use_metaclass
from jedi.parser_utils import get_parent_scope
from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass
from jedi.evaluate import compiled
from jedi.evaluate.lazy_context import LazyKnownContext
@@ -58,9 +59,10 @@ def apply_py__get__(context, base_context):
class ClassName(TreeNameDefinition):
def __init__(self, parent_context, tree_name, name_context):
def __init__(self, parent_context, tree_name, name_context, apply_decorators):
super(ClassName, self).__init__(parent_context, tree_name)
self._name_context = name_context
self._apply_decorators = apply_decorators
@iterator_to_context_set
def infer(self):
@@ -70,16 +72,45 @@ class ClassName(TreeNameDefinition):
self.parent_context.evaluator, self._name_context, self.tree_name)
for result_context in inferred:
for c in apply_py__get__(result_context, self.parent_context):
yield c
if self._apply_decorators:
for c in apply_py__get__(result_context, self.parent_context):
yield c
else:
yield result_context
class ClassFilter(ParserTreeFilter):
name_class = ClassName
def __init__(self, *args, **kwargs):
self._is_instance = kwargs.pop('is_instance') # Python 2 :/
super(ClassFilter, self).__init__(*args, **kwargs)
def _convert_names(self, names):
return [self.name_class(self.context, name, self._node_context)
for name in names]
return [
self.name_class(
parent_context=self.context,
tree_name=name,
name_context=self._node_context,
apply_decorators=not self._is_instance,
) for name in names
]
def _equals_origin_scope(self):
node = self._origin_scope
while node is not None:
if node == self._parser_scope or node == self.context:
return True
node = get_parent_scope(node)
return False
def _access_possible(self, name):
return not name.value.startswith('__') or name.value.endswith('__') \
or self._equals_origin_scope()
def _filter(self, names):
names = super(ClassFilter, self)._filter(names)
return [name for name in names if self._access_possible(name)]
class ClassContext(use_metaclass(CachedMetaClass, TreeContext)):
@@ -163,7 +194,9 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)):
else:
yield ClassFilter(
self.evaluator, self, node_context=cls,
origin_scope=origin_scope)
origin_scope=origin_scope,
is_instance=is_instance
)
def is_class(self):
return True

View File

@@ -38,12 +38,6 @@ class AbstractNameDefinition(object):
return '<%s: %s>' % (self.__class__.__name__, self.string_name)
return '<%s: %s@%s>' % (self.__class__.__name__, self.string_name, self.start_pos)
def execute(self, arguments):
return self.infer().execute(arguments)
def execute_evaluated(self, *args, **kwargs):
return self.infer().execute_evaluated(*args, **kwargs)
def is_import(self):
return False