mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Finally fixing the Python 2 issues with static_getattr.
This commit is contained in:
@@ -8,7 +8,7 @@ from jedi.evaluate.compiled import mixed
|
||||
from jedi.evaluate.context import Context
|
||||
|
||||
|
||||
class NamespaceObject():
|
||||
class NamespaceObject(object):
|
||||
def __init__(self, dct):
|
||||
self.__dict__ = dct
|
||||
|
||||
|
||||
@@ -6,10 +6,9 @@ information returned to enable Jedi to make decisions.
|
||||
|
||||
import types
|
||||
|
||||
_sentinel = object()
|
||||
from jedi._compatibility import py_version
|
||||
|
||||
def _static_getmro(klass):
|
||||
return type.__dict__['__mro__'].__get__(klass)
|
||||
_sentinel = object()
|
||||
|
||||
def _check_instance(obj, attr):
|
||||
instance_dict = {}
|
||||
@@ -37,7 +36,7 @@ def _is_type(obj):
|
||||
return True
|
||||
|
||||
|
||||
def _shadowed_dict(klass):
|
||||
def _shadowed_dict_newstyle(klass):
|
||||
dict_attr = type.__dict__["__dict__"]
|
||||
for entry in _static_getmro(klass):
|
||||
try:
|
||||
@@ -52,8 +51,74 @@ def _shadowed_dict(klass):
|
||||
return _sentinel
|
||||
|
||||
|
||||
def _static_getmro_newstyle(klass):
|
||||
return type.__dict__['__mro__'].__get__(klass)
|
||||
|
||||
|
||||
if py_version >= 30:
|
||||
_shadowed_dict = _shadowed_dict_newstyle
|
||||
_get_type = type
|
||||
_static_getmro = _static_getmro_newstyle
|
||||
else:
|
||||
def _shadowed_dict(klass):
|
||||
"""
|
||||
In Python 2 __dict__ is not overwritable:
|
||||
|
||||
class Foo(object): pass
|
||||
setattr(Foo, '__dict__', 4)
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
TypeError: __dict__ must be a dictionary object
|
||||
|
||||
It applies to both newstyle and oldstyle classes:
|
||||
|
||||
class Foo(object): pass
|
||||
setattr(Foo, '__dict__', 4)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
AttributeError: attribute '__dict__' of 'type' objects is not writable
|
||||
|
||||
It also applies to instances of those objects. However to keep things
|
||||
straight forward, newstyle classes always use the complicated way of
|
||||
accessing it while oldstyle classes just use getattr.
|
||||
"""
|
||||
if type(klass) is _oldstyle_class_type:
|
||||
return getattr(klass, '__dict__', _sentinel)
|
||||
return _shadowed_dict_newstyle(klass)
|
||||
|
||||
class _OldStyleClass():
|
||||
pass
|
||||
|
||||
_oldstyle_instance_type = type(_OldStyleClass())
|
||||
_oldstyle_class_type = type(_OldStyleClass)
|
||||
|
||||
def _get_type(obj):
|
||||
type_ = object.__getattribute__(obj, '__class__')
|
||||
if type_ is _oldstyle_instance_type:
|
||||
# Somehow for old style classes we need to access it directly.
|
||||
return obj.__class__
|
||||
return type_
|
||||
|
||||
def _static_getmro(klass):
|
||||
if type(klass) is _oldstyle_class_type:
|
||||
def oldstyle_mro(klass):
|
||||
"""
|
||||
Oldstyle mro is a really simplistic way of look up mro:
|
||||
https://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python
|
||||
"""
|
||||
yield klass
|
||||
for base in klass.__bases__:
|
||||
for yield_from in oldstyle_mro(base):
|
||||
yield yield_from
|
||||
|
||||
return oldstyle_mro(klass)
|
||||
|
||||
return _static_getmro_newstyle(klass)
|
||||
|
||||
|
||||
def _safe_hasattr(obj, name):
|
||||
return _check_class(type(obj), name) is not _sentinel
|
||||
return _check_class(_get_type(obj), name) is not _sentinel
|
||||
|
||||
|
||||
def _safe_is_data_descriptor(obj):
|
||||
@@ -76,7 +141,7 @@ def getattr_static(obj, attr, default=_sentinel):
|
||||
"""
|
||||
instance_result = _sentinel
|
||||
if not _is_type(obj):
|
||||
klass = type(obj)
|
||||
klass = _get_type(obj)
|
||||
dict_attr = _shadowed_dict(klass)
|
||||
if (dict_attr is _sentinel or
|
||||
type(dict_attr) is types.MemberDescriptorType):
|
||||
|
||||
@@ -177,7 +177,7 @@ def test_getitem_side_effects():
|
||||
_assert_interpreter_complete('foo[0].', locals(), [])
|
||||
|
||||
|
||||
def test_property_error():
|
||||
def test_property_error_oldstyle():
|
||||
lst = []
|
||||
class Foo3():
|
||||
@property
|
||||
@@ -193,6 +193,22 @@ def test_property_error():
|
||||
assert lst == []
|
||||
|
||||
|
||||
def test_property_error_newstyle():
|
||||
lst = []
|
||||
class Foo3(object):
|
||||
@property
|
||||
def bar(self):
|
||||
lst.append(1)
|
||||
raise ValueError
|
||||
|
||||
foo = Foo3()
|
||||
_assert_interpreter_complete('foo.bar', locals(), ['bar'])
|
||||
_assert_interpreter_complete('foo.bar.baz', locals(), [])
|
||||
|
||||
# There should not be side effects
|
||||
assert lst == []
|
||||
|
||||
|
||||
def test_param_completion():
|
||||
def foo(bar):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user