Get a lot of tests passing

This commit is contained in:
Dave Halter
2017-11-26 17:48:00 +01:00
parent c266fb301b
commit e71f0062dd
17 changed files with 120 additions and 79 deletions

View File

@@ -101,6 +101,7 @@ class Evaluator(object):
self.is_analysis = False self.is_analysis = False
self.python_version = sys.version_info[:2] self.python_version = sys.version_info[:2]
self.project = project self.project = project
self.access_cache = {}
project.add_evaluator(self) project.add_evaluator(self)
if compiled_sub_process is None: if compiled_sub_process is None:

View File

@@ -4,6 +4,7 @@ Module for statical analysis.
from jedi import debug from jedi import debug
from parso.python import tree from parso.python import tree
from jedi.evaluate.compiled import CompiledObject from jedi.evaluate.compiled import CompiledObject
from jedi.evaluate.helpers import is_string
CODES = { CODES = {
@@ -117,6 +118,7 @@ def add_attribute_error(name_context, lookup_context, name):
slot_names = lookup_context.get_function_slot_names('__getattr__') + \ slot_names = lookup_context.get_function_slot_names('__getattr__') + \
lookup_context.get_function_slot_names('__getattribute__') lookup_context.get_function_slot_names('__getattribute__')
for n in slot_names: for n in slot_names:
# TODO do we even get here?
if isinstance(name, CompiledInstanceName) and \ if isinstance(name, CompiledInstanceName) and \
n.parent_context.obj == object: n.parent_context.obj == object:
typ = Warning typ = Warning
@@ -139,7 +141,7 @@ def _check_for_exception_catch(node_context, jedi_name, exception, payload=None)
""" """
def check_match(cls, exception): def check_match(cls, exception):
try: try:
return isinstance(cls, CompiledObject) and issubclass(exception, cls.obj) return isinstance(cls, CompiledObject) and cls.is_super_class(exception)
except TypeError: except TypeError:
return False return False
@@ -189,8 +191,8 @@ def _check_for_exception_catch(node_context, jedi_name, exception, payload=None)
# Check name # Check name
key, lazy_context = args[1] key, lazy_context = args[1]
names = list(lazy_context.infer()) names = list(lazy_context.infer())
assert len(names) == 1 and isinstance(names[0], CompiledObject) assert len(names) == 1 and is_string(names[0])
assert names[0].obj == payload[1].value assert names[0].get_safe_value() == payload[1].value
# Check objects # Check objects
key, lazy_context = args[0] key, lazy_context = args[0]

View File

@@ -8,15 +8,13 @@ import os
import types import types
from functools import partial from functools import partial
from jedi._compatibility import builtins as _builtins, unicode, py_version from jedi._compatibility import builtins as _builtins
from jedi import debug from jedi import debug
from jedi.cache import underscore_memoization, memoize_method from jedi.cache import underscore_memoization, memoize_method
from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition, \ from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition, \
ContextNameMixin ContextNameMixin
from jedi.evaluate.base_context import Context, ContextSet from jedi.evaluate.base_context import Context, ContextSet
from jedi.evaluate.lazy_context import LazyKnownContext from jedi.evaluate.compiled.access import DirectObjectAccess, _sentinel, create_access
from jedi.evaluate.compiled.getattr_static import getattr_static
from jedi.evaluate.compiled.access import DirectObjectAccess, _sentinel
from . import fake from . import fake
@@ -75,7 +73,7 @@ class CompiledObject(Context):
@CheckAttribute @CheckAttribute
def py__class__(self): def py__class__(self):
return create(self.evaluator, self.obj.__class__) return create(self.evaluator, self.access.py__class__())
@CheckAttribute @CheckAttribute
def py__mro__(self): def py__mro__(self):
@@ -85,6 +83,7 @@ class CompiledObject(Context):
@CheckAttribute @CheckAttribute
def py__bases__(self): def py__bases__(self):
raise NotImplementedError
return tuple(create(self.evaluator, cls) for cls in self.obj.__bases__) return tuple(create(self.evaluator, cls) for cls in self.obj.__bases__)
def py__bool__(self): def py__bool__(self):
@@ -111,7 +110,7 @@ class CompiledObject(Context):
parts = p.strip().split('=') parts = p.strip().split('=')
yield UnresolvableParamName(self, parts[0]) yield UnresolvableParamName(self, parts[0])
else: else:
for signature_param in signature_params.values(): for signature_param in signature_params:
yield SignatureParamName(self, signature_param) yield SignatureParamName(self, signature_param)
def __repr__(self): def __repr__(self):
@@ -201,7 +200,7 @@ class CompiledObject(Context):
def dict_values(self): def dict_values(self):
return ContextSet.from_iterable( return ContextSet.from_iterable(
create(self.evaluator, v) for v in self.obj.values() create(self.evaluator, access) for access in self.access.dict_values()
) )
def get_safe_value(self, default=_sentinel): def get_safe_value(self, default=_sentinel):
@@ -213,6 +212,12 @@ class CompiledObject(Context):
self.access.execute_operation(other.access, operator) self.access.execute_operation(other.access, operator)
) )
def negate(self):
return create(self.evaluator, self.access.negate())
def is_super_class(self, exception):
return self.access.is_super_class(exception)
class CompiledName(AbstractNameDefinition): class CompiledName(AbstractNameDefinition):
def __init__(self, evaluator, parent_context, name): def __init__(self, evaluator, parent_context, name):
@@ -304,8 +309,11 @@ class CompiledObjectFilter(AbstractFilter):
@memoize_method @memoize_method
def get(self, name): def get(self, name):
name = str(name) name = str(name)
if not self._compiled_object.access.is_allowed_getattr(name): try:
return [EmptyCompiledName(self._evaluator, name)] if not self._compiled_object.access.is_allowed_getattr(name):
return [EmptyCompiledName(self._evaluator, name)]
except AttributeError:
return []
if self._is_instance and name not in self._compiled_object.access.dir(): if self._is_instance and name not in self._compiled_object.access.dir():
return [] return []
@@ -463,7 +471,6 @@ def _parse_function_doc(doc):
def _create_from_name(evaluator, compiled_object, name): def _create_from_name(evaluator, compiled_object, name):
faked = None faked = None
print(compiled_object.tree_node)
try: try:
faked = fake.get_faked_with_parent_context(compiled_object, name) faked = fake.get_faked_with_parent_context(compiled_object, name)
if faked.type == 'funcdef': if faked.type == 'funcdef':
@@ -472,8 +479,8 @@ def _create_from_name(evaluator, compiled_object, name):
except fake.FakeDoesNotExist: except fake.FakeDoesNotExist:
pass pass
obj = compiled_object.access.getattr(name, default=None) access = compiled_object.access.getattr(name, default=None)
return create(evaluator, obj, parent_context=compiled_object, faked=faked) return create(evaluator, access, parent_context=compiled_object, faked=faked)
def builtin_from_name(evaluator, string): def builtin_from_name(evaluator, string):
@@ -498,7 +505,11 @@ _SPECIAL_OBJECTS = {
def get_special_object(evaluator, identifier): def get_special_object(evaluator, identifier):
obj = _SPECIAL_OBJECTS[identifier] obj = _SPECIAL_OBJECTS[identifier]
return create(evaluator, obj, parent_context=create(evaluator, _builtins)) if identifier == 'BUILTINS':
parent_context = None
else:
parent_context = create(evaluator, _builtins)
return create(evaluator, obj, parent_context=parent_context)
def compiled_objects_cache(attribute_name): def compiled_objects_cache(attribute_name):
@@ -508,7 +519,7 @@ def compiled_objects_cache(attribute_name):
Caching the id has the advantage that an object doesn't need to be Caching the id has the advantage that an object doesn't need to be
hashable. hashable.
""" """
def wrapper(evaluator, obj, parent_context=None, module=None, faked=None): def wrapper(evaluator, obj, parent_context=None, faked=None):
cache = getattr(evaluator, attribute_name) cache = getattr(evaluator, attribute_name)
# Do a very cheap form of caching here. # Do a very cheap form of caching here.
key = id(obj), id(parent_context) key = id(obj), id(parent_context)
@@ -516,9 +527,9 @@ def compiled_objects_cache(attribute_name):
return cache[key][0] return cache[key][0]
except KeyError: except KeyError:
# TODO this whole decorator is way too ugly # TODO this whole decorator is way too ugly
result = func(evaluator, obj, parent_context, module, faked) result = func(evaluator, obj, parent_context, faked)
# Need to cache all of them, otherwise the id could be overwritten. # Need to cache all of them, otherwise the id could be overwritten.
cache[key] = result, obj, parent_context, module, faked cache[key] = result, obj, parent_context, faked
return result return result
return wrapper return wrapper
@@ -526,16 +537,17 @@ def compiled_objects_cache(attribute_name):
@compiled_objects_cache('compiled_cache') @compiled_objects_cache('compiled_cache')
def create(evaluator, obj, parent_context=None, module=None, faked=None): def create(evaluator, obj, parent_context=None, faked=None):
""" """
A very weird interface class to this module. The more options provided the A very weird interface class to this module. The more options provided the
more acurate loading compiled objects is. more acurate loading compiled objects is.
""" """
print('create', obj)
if isinstance(obj, DirectObjectAccess): if isinstance(obj, DirectObjectAccess):
access = obj access = obj
else: else:
access = DirectObjectAccess(obj) print('xxx', obj)
return create(evaluator, create_access(evaluator, obj), parent_context, faked)
if inspect.ismodule(obj): if inspect.ismodule(obj):
if parent_context is not None: if parent_context is not None:
# Modules don't have parents, be careful with caching: recurse. # Modules don't have parents, be careful with caching: recurse.
@@ -554,8 +566,7 @@ def create(evaluator, obj, parent_context=None, module=None, faked=None):
pass pass
else: else:
for access2, tree_node in zip(accesses, tree_nodes): for access2, tree_node in zip(accesses, tree_nodes):
parent_context = CompiledObject(evaluator, access2, parent_context, tree_node) parent_context = create(evaluator, access2, parent_context, faked=tree_node)
print('foo', obj, tree_nodes, parent_context)
# TODO this if is ugly. Please remove, it may make certain # TODO this if is ugly. Please remove, it may make certain
# properties of that function unusable. # properties of that function unusable.
@@ -563,13 +574,18 @@ def create(evaluator, obj, parent_context=None, module=None, faked=None):
from jedi.evaluate.context.function import FunctionContext from jedi.evaluate.context.function import FunctionContext
return FunctionContext(evaluator, parent_context.parent_context, tree_node) return FunctionContext(evaluator, parent_context.parent_context, tree_node)
return parent_context return parent_context
if parent_context is None: # TODO wow this is a mess....
if parent_context is None and not faked:
parent_context = create(evaluator, _builtins) parent_context = create(evaluator, _builtins)
return create(evaluator, obj, parent_context)
print('OOOOOOOOOO', obj)
if access._obj == _builtins and parent_context is not None:
raise 1
return CompiledObject(evaluator, access, parent_context, faked) return CompiledObject(evaluator, access, parent_context, faked)
def _create_from_access(evaluator, access, parent_context=None, faked=None): def _create_from_access(evaluator, access, parent_context=None, faked=None):
if parent_context is None: if parent_context is None:
parent_context = create(evaluator, _builtins) parent_context = create(evaluator, _builtins)
return CompiledObject(evaluator, access, parent_context, faked) return create(evaluator, access, parent_context, faked=faked)

View File

@@ -5,6 +5,7 @@ from collections import namedtuple
from jedi._compatibility import unicode, is_py3, is_py34, builtins, py_version from jedi._compatibility import unicode, is_py3, is_py34, builtins, py_version
from jedi.evaluate.compiled.getattr_static import getattr_static from jedi.evaluate.compiled.getattr_static import getattr_static
from jedi.evaluate.cache import evaluator_function_cache
MethodDescriptorType = type(str.replace) MethodDescriptorType = type(str.replace)
@@ -76,15 +77,22 @@ _OPERATORS.update(COMPARISON_OPERATORS)
SignatureParam = namedtuple('SignatureParam', 'name default empty annotation') SignatureParam = namedtuple('SignatureParam', 'name default empty annotation')
@evaluator_function_cache()
def create_access(evaluator, obj):
print('create', obj)
return DirectObjectAccess(evaluator, obj)
class DirectObjectAccess(object): class DirectObjectAccess(object):
def __init__(self, obj): def __init__(self, evaluator, obj):
self._evaluator = evaluator
self._obj = obj self._obj = obj
def __repr__(self): def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._obj) return '%s(%s)' % (self.__class__.__name__, self._obj)
def _create_access(self, obj): def _create_access(self, obj):
return DirectObjectAccess(obj) return create_access(self._evaluator, obj)
def py__bool__(self): def py__bool__(self):
return bool(self._obj) return bool(self._obj)
@@ -99,7 +107,7 @@ class DirectObjectAccess(object):
return inspect.getdoc(self._obj) or '' return inspect.getdoc(self._obj) or ''
def py__name__(self): def py__name__(self):
if not is_class_instance(self._obj) or \ if not _is_class_instance(self._obj) or \
inspect.ismethoddescriptor(self._obj): # slots inspect.ismethoddescriptor(self._obj): # slots
cls = self._obj cls = self._obj
else: else:
@@ -138,6 +146,9 @@ class DirectObjectAccess(object):
lst.append(self._create_access(part)) lst.append(self._create_access(part))
return lst return lst
def py__class__(self):
return self._create_access(self._obj.__class__)
def get_repr(self): def get_repr(self):
return repr(self._obj) return repr(self._obj)
@@ -161,7 +172,7 @@ class DirectObjectAccess(object):
try: try:
attr, is_get_descriptor = getattr_static(self._obj, name) attr, is_get_descriptor = getattr_static(self._obj, name)
except AttributeError: except AttributeError:
return [] raise
else: else:
if is_get_descriptor \ if is_get_descriptor \
and not type(attr) in ALLOWED_DESCRIPTOR_ACCESS: and not type(attr) in ALLOWED_DESCRIPTOR_ACCESS:
@@ -274,11 +285,20 @@ class DirectObjectAccess(object):
default=p.default, default=p.default,
empty=p.empty, empty=p.empty,
annotation=p.annotation, annotation=p.annotation,
) for p in signature.parameters ) for p in signature.parameters.values()
] ]
def negate(self):
return self._create_access(-self._obj)
def is_class_instance(obj): def dict_values(self):
return [self._create_access(v) for v in self._obj.values()]
def is_super_class(self, exception):
return issubclass(exception, self._obj)
def _is_class_instance(obj):
"""Like inspect.* methods.""" """Like inspect.* methods."""
try: try:
cls = obj.__class__ cls = obj.__class__

View File

@@ -37,7 +37,7 @@ class MixedObject(object):
self.parent_context = parent_context self.parent_context = parent_context
self.compiled_object = compiled_object self.compiled_object = compiled_object
self._context = tree_context self._context = tree_context
self.obj = compiled_object.obj self.access = compiled_object.access
# We have to overwrite everything that has to do with trailers, name # We have to overwrite everything that has to do with trailers, name
# lookups and filters to make it possible to route name lookups towards # lookups and filters to make it possible to route name lookups towards
@@ -49,7 +49,7 @@ class MixedObject(object):
yield MixedObjectFilter(self.evaluator, self) yield MixedObjectFilter(self.evaluator, self)
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (type(self).__name__, repr(self.obj)) return '<%s: %s>' % (type(self).__name__, repr(self.access))
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self._context, name) return getattr(self._context, name)
@@ -74,17 +74,11 @@ class MixedName(compiled.CompiledName):
@underscore_memoization @underscore_memoization
def infer(self): def infer(self):
obj = self.parent_context.obj access = self.parent_context.access
try: # TODO use logic from compiled.CompiledObjectFilter
# TODO use logic from compiled.CompiledObjectFilter access = access.getattr(self.string_name, default=None)
obj = getattr(obj, self.string_name)
except AttributeError:
# Happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
obj = None
return ContextSet( return ContextSet(
_create(self._evaluator, obj, parent_context=self.parent_context) _create(self._evaluator, access, parent_context=self.parent_context)
) )
@property @property
@@ -135,7 +129,10 @@ def _get_object_to_check(python_object):
raise TypeError # Prevents computation of `repr` within inspect. raise TypeError # Prevents computation of `repr` within inspect.
def find_syntax_node_name(evaluator, python_object): def _find_syntax_node_name(evaluator, access):
# TODO accessing this is bad, but it probably doesn't matter that much,
# because we're working with interpreteters only here.
python_object = access._obj
try: try:
python_object = _get_object_to_check(python_object) python_object = _get_object_to_check(python_object)
path = inspect.getsourcefile(python_object) path = inspect.getsourcefile(python_object)
@@ -195,11 +192,11 @@ def find_syntax_node_name(evaluator, python_object):
@compiled.compiled_objects_cache('mixed_cache') @compiled.compiled_objects_cache('mixed_cache')
def _create(evaluator, obj, parent_context=None, *args): def _create(evaluator, access, parent_context=None, *args):
tree_node, path = find_syntax_node_name(evaluator, obj) tree_node, path = _find_syntax_node_name(evaluator, access)
compiled_object = compiled.create( compiled_object = compiled.create(
evaluator, obj, parent_context=parent_context.compiled_object) evaluator, access, parent_context=parent_context.compiled_object)
if tree_node is None: if tree_node is None:
return compiled_object return compiled_object
@@ -218,7 +215,7 @@ def _create(evaluator, obj, parent_context=None, *args):
node_is_object=True node_is_object=True
) )
if tree_node.type == 'classdef': if tree_node.type == 'classdef':
if not inspect.isclass(obj): if not access.is_class():
# Is an instance, not a class. # Is an instance, not a class.
tree_context, = tree_context.execute_evaluated() tree_context, = tree_context.execute_evaluated()
@@ -228,4 +225,3 @@ def _create(evaluator, obj, parent_context=None, *args):
compiled_object, compiled_object,
tree_context=tree_context tree_context=tree_context
) )

View File

@@ -373,7 +373,7 @@ class SequenceLiteralContext(ArrayMixin, AbstractIterable):
for key_node, value in self._items(): for key_node, value in self._items():
for key in self._defining_context.eval_node(key_node): for key in self._defining_context.eval_node(key_node):
if is_string(key): if is_string(key):
yield key.obj, LazyTreeContext(self._defining_context, value) yield key.get_safe_value(), LazyTreeContext(self._defining_context, value)
def __repr__(self): def __repr__(self):
return "<%s of %s>" % (self.__class__.__name__, self.atom) return "<%s of %s>" % (self.__class__.__name__, self.atom)

View File

@@ -192,4 +192,3 @@ def _create_default_param(execution_context, param):
def create_default_params(execution_context, funcdef): def create_default_params(execution_context, funcdef):
return [_create_default_param(execution_context, p) return [_create_default_param(execution_context, p)
for p in funcdef.get_params()] for p in funcdef.get_params()]

View File

@@ -30,6 +30,7 @@ from jedi.evaluate import compiled
from jedi.evaluate.base_context import NO_CONTEXTS, ContextSet from jedi.evaluate.base_context import NO_CONTEXTS, ContextSet
from jedi.evaluate.lazy_context import LazyTreeContext from jedi.evaluate.lazy_context import LazyTreeContext
from jedi.evaluate.context import ModuleContext from jedi.evaluate.context import ModuleContext
from jedi.evaluate.helpers import is_string
from jedi import debug from jedi import debug
from jedi import _compatibility from jedi import _compatibility
from jedi import parser_utils from jedi import parser_utils
@@ -60,16 +61,15 @@ def _fix_forward_reference(context, node):
" not %s" % (node, evaled_nodes)) " not %s" % (node, evaled_nodes))
return node return node
evaled_node = list(evaled_nodes)[0] evaled_node = list(evaled_nodes)[0]
if isinstance(evaled_node, compiled.CompiledObject) and \ if is_string(evaled_node):
isinstance(evaled_node.obj, str):
try: try:
new_node = context.evaluator.grammar.parse( new_node = context.evaluator.grammar.parse(
_compatibility.unicode(evaled_node.obj), _compatibility.unicode(evaled_node.get_safe_value()),
start_symbol='eval_input', start_symbol='eval_input',
error_recovery=False error_recovery=False
) )
except ParserSyntaxError: except ParserSyntaxError:
debug.warning('Annotation not parsed: %s' % evaled_node.obj) debug.warning('Annotation not parsed: %s' % evaled_node)
return node return node
else: else:
module = node.get_root_node() module = node.get_root_node()

View File

@@ -157,7 +157,7 @@ def builtins_getattr(evaluator, objects, names, defaults=None):
for obj in objects: for obj in objects:
for name in names: for name in names:
if is_string(name): if is_string(name):
return obj.py__getattribute__(name.obj) return obj.py__getattribute__(name.get_safe_value())
else: else:
debug.warning('getattr called without str') debug.warning('getattr called without str')
continue continue
@@ -216,18 +216,21 @@ def builtins_reversed(evaluator, sequences, obj, arguments):
def builtins_isinstance(evaluator, objects, types, arguments): def builtins_isinstance(evaluator, objects, types, arguments):
bool_results = set() bool_results = set()
for o in objects: for o in objects:
cls = o.py__class__()
try: try:
mro_func = o.py__class__().py__mro__ mro_func = cls.py__mro__
except AttributeError: except AttributeError:
# This is temporary. Everything should have a class attribute in # This is temporary. Everything should have a class attribute in
# Python?! Maybe we'll leave it here, because some numpy objects or # Python?! Maybe we'll leave it here, because some numpy objects or
# whatever might not. # whatever might not.
return ContextSet(compiled.create(True), compiled.create(False)) return ContextSet(compiled.create(evaluator, True), compiled.create(evaluator, False))
mro = mro_func() mro = mro_func()
print(mro, types)
for cls_or_tup in types: for cls_or_tup in types:
if cls_or_tup.is_class(): if cls_or_tup.is_class():
print(id(mro[0]), mro[0], id(cls_or_tup), cls_or_tup)
bool_results.add(cls_or_tup in mro) bool_results.add(cls_or_tup in mro)
elif cls_or_tup.name.string_name == 'tuple' \ elif cls_or_tup.name.string_name == 'tuple' \
and cls_or_tup.get_root_context() == evaluator.BUILTINS: and cls_or_tup.get_root_context() == evaluator.BUILTINS:
@@ -246,6 +249,7 @@ def builtins_isinstance(evaluator, objects, types, arguments):
'not %s.' % cls_or_tup 'not %s.' % cls_or_tup
analysis.add(lazy_context._context, 'type-error-isinstance', node, message) analysis.add(lazy_context._context, 'type-error-isinstance', node, message)
print(objects, types, bool_results)
return ContextSet.from_iterable(compiled.create(evaluator, x) for x in bool_results) return ContextSet.from_iterable(compiled.create(evaluator, x) for x in bool_results)
@@ -265,15 +269,16 @@ def collections_namedtuple(evaluator, obj, arguments):
# Process arguments # Process arguments
# TODO here we only use one of the types, we should use all. # TODO here we only use one of the types, we should use all.
name = list(_follow_param(evaluator, arguments, 0))[0].obj # TODO this is buggy, doesn't need to be a string
name = list(_follow_param(evaluator, arguments, 0))[0].get_safe_value()
_fields = list(_follow_param(evaluator, arguments, 1))[0] _fields = list(_follow_param(evaluator, arguments, 1))[0]
if isinstance(_fields, compiled.CompiledObject): if isinstance(_fields, compiled.CompiledObject):
fields = _fields.obj.replace(',', ' ').split() fields = _fields.get_safe_value().replace(',', ' ').split()
elif isinstance(_fields, iterable.AbstractIterable): elif isinstance(_fields, iterable.AbstractIterable):
fields = [ fields = [
v.obj v.get_safe_value()
for lazy_context in _fields.py__iter__() for lazy_context in _fields.py__iter__()
for v in lazy_context.infer() if hasattr(v, 'obj') for v in lazy_context.infer() if is_string(v)
] ]
else: else:
return NO_CONTEXTS return NO_CONTEXTS

View File

@@ -2,7 +2,6 @@
Functions evaluating the syntax tree. Functions evaluating the syntax tree.
""" """
import copy import copy
import operator as op
from parso.python import tree from parso.python import tree
@@ -309,7 +308,7 @@ def eval_factor(context_set, operator):
for context in context_set: for context in context_set:
if operator == '-': if operator == '-':
if is_number(context): if is_number(context):
yield compiled.create(context.evaluator, -context.obj) yield context.negate()
elif operator == 'not': elif operator == 'not':
value = context.py__bool__() value = context.py__bool__()
if value is None: # Uncertainty. if value is None: # Uncertainty.
@@ -383,6 +382,7 @@ def _eval_comparison_part(evaluator, context, left, operator, right):
# `int() % float()`. # `int() % float()`.
return ContextSet(left) return ContextSet(left)
elif operator in COMPARISON_OPERATORS: elif operator in COMPARISON_OPERATORS:
print(operator, left, right)
if is_compiled(left) and is_compiled(right): if is_compiled(left) and is_compiled(right):
# Possible, because the return is not an option. Just compare. # Possible, because the return is not an option. Just compare.
try: try:
@@ -391,8 +391,10 @@ def _eval_comparison_part(evaluator, context, left, operator, right):
# Could be True or False. # Could be True or False.
pass pass
else: else:
raise NotImplementedError if operator in ('is', '!=', '==', 'is not'):
return ContextSet(compiled.create(evaluator, result)) operation = COMPARISON_OPERATORS[operator]
bool_ = operation(left, right)
return ContextSet(compiled.create(evaluator, bool_))
return ContextSet(compiled.create(evaluator, True), compiled.create(evaluator, False)) return ContextSet(compiled.create(evaluator, True), compiled.create(evaluator, False))
elif operator == 'in': elif operator == 'in':

View File

@@ -121,7 +121,7 @@ def _paths_from_assignment(module_context, expr_stmt):
for lazy_context in cn.infer().iterate(cn): for lazy_context in cn.infer().iterate(cn):
for context in lazy_context.infer(): for context in lazy_context.infer():
if is_string(context): if is_string(context):
abs_path = _abs_path(module_context, context.obj) abs_path = _abs_path(module_context, context.get_safe_value())
if abs_path is not None: if abs_path is not None:
yield abs_path yield abs_path
@@ -144,7 +144,7 @@ def _paths_from_list_modifications(module_context, trailer1, trailer2):
for context in module_context.create_context(arg).eval_node(arg): for context in module_context.create_context(arg).eval_node(arg):
if is_string(context): if is_string(context):
abs_path = _abs_path(module_context, context.obj) abs_path = _abs_path(module_context, context.get_safe_value())
if abs_path is not None: if abs_path is not None:
yield abs_path yield abs_path

View File

@@ -125,3 +125,8 @@ class StaticAnalysisCase(object):
def cwd_tmpdir(monkeypatch, tmpdir): def cwd_tmpdir(monkeypatch, tmpdir):
with helpers.set_cwd(tmpdir.dirpath): with helpers.set_cwd(tmpdir.dirpath):
yield tmpdir yield tmpdir
@pytest.fixture
def evaluator():
return jedi.Script('')._evaluator

View File

@@ -28,11 +28,6 @@ def test_versions(version):
assert any(executable in p for p in sys_path) assert any(executable in p for p in sys_path)
@pytest.fixture
def evaluator():
return jedi.Script('')._evaluator
def test_import_module(evaluator): def test_import_module(evaluator):
compiled_obj = evaluator.compiled_subprocess.import_module(name='math') compiled_obj = evaluator.compiled_subprocess.import_module(name='math')
assert compiled_obj.py__bool__() is True assert compiled_obj.py__bool__() is True

View File

@@ -50,12 +50,12 @@ def test_parse_function_doc_illegal_docstr():
assert ('', '') == compiled._parse_function_doc(docstr) assert ('', '') == compiled._parse_function_doc(docstr)
def test_doc(): def test_doc(evaluator):
""" """
Even CompiledObject docs always return empty docstrings - not None, that's Even CompiledObject docs always return empty docstrings - not None, that's
just a Jedi API definition. just a Jedi API definition.
""" """
obj = compiled.CompiledObject(_evaluator(), ''.__getnewargs__) obj = compiled.create(evaluator, ''.__getnewargs__)
assert obj.py__doc__() == '' assert obj.py__doc__() == ''

View File

@@ -6,7 +6,7 @@ from jedi._compatibility import py_version, unicode
def _eval_literal(code): def _eval_literal(code):
def_, = jedi.Script(code).goto_definitions() def_, = jedi.Script(code).goto_definitions()
return def_._name._context.obj return def_._name._context.access._obj
@pytest.mark.skipif('sys.version_info[:2] < (3, 6)') @pytest.mark.skipif('sys.version_info[:2] < (3, 6)')

View File

@@ -14,4 +14,4 @@ def test_equals(source):
script = Script(source) script = Script(source)
node = script._get_module_node().children[0] node = script._get_module_node().children[0]
first, = script._get_module().eval_node(node) first, = script._get_module().eval_node(node)
assert isinstance(first, CompiledObject) and first.obj is True assert isinstance(first, CompiledObject) and first.access._obj is True

View File

@@ -83,7 +83,7 @@ def test_add_to_end():
def test_tokenizer_with_string_literal_backslash(): def test_tokenizer_with_string_literal_backslash():
c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions() c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions()
assert c[0]._name._context.obj == 'foo' assert c[0]._name._context.get_safe_value() == 'foo'
def test_ellipsis(): def test_ellipsis():