mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-09 23:34:45 +08:00
getattr() / __getattribute__ / __getattr__ implementation
This commit is contained in:
@@ -47,6 +47,7 @@ Jedi supports many of the widely used Python features:
|
||||
- support for list.append, set.add, list.extend, etc.
|
||||
- (nested) list comprehensions / ternary expressions
|
||||
- relative imports
|
||||
- `getattr()` / `__getattr__` / `__getattribute__`
|
||||
- function annotations (py3k feature, are ignored right now, but being parsed.
|
||||
I don't know what to do with them.)
|
||||
- class decorators (py3k feature, are being ignored too, until I find a use
|
||||
@@ -55,13 +56,11 @@ Jedi supports many of the widely used Python features:
|
||||
However, it does not yet support (and probably will in future versions, because
|
||||
they are on my todo list):
|
||||
|
||||
- `getattr()` / `__getattr__` / `__getattribute__`
|
||||
- `sys.path` modifications
|
||||
- assert / isinstance
|
||||
- manipulations of instances outside the instance variables, without using
|
||||
functions
|
||||
- mro
|
||||
- operation support -> \_\_mul\_\_, \_\_add\_\_, etc.
|
||||
- assert / isinstance
|
||||
|
||||
It does not support (and most probably will not in future versions):
|
||||
- metaclasses (how could an auto-completion ever support this)
|
||||
@@ -72,7 +71,7 @@ Caveats
|
||||
-------
|
||||
|
||||
This framework should work for both Python 2/3. However, some things were just
|
||||
not as *pythonic* Python 2 as things should be. To keep things simple, some
|
||||
not as *pythonic* in Python 2 as things should be. To keep things simple, some
|
||||
things have been held back:
|
||||
|
||||
- Classes: Always Python 3 like, therefore all classes inherit from `object`.
|
||||
|
||||
70
evaluate.py
70
evaluate.py
@@ -216,10 +216,8 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
|
||||
return names
|
||||
|
||||
def get_subscope_by_name(self, name):
|
||||
for sub in reversed(self.base.subscopes):
|
||||
if sub.name.get_code() == name:
|
||||
sub = self.base.get_subscope_by_name(name)
|
||||
return InstanceElement(self, sub, True)
|
||||
raise KeyError("Couldn't find subscope.")
|
||||
|
||||
def execute_subscope_by_name(self, name, args=None):
|
||||
if args is None:
|
||||
@@ -383,6 +381,12 @@ class Class(use_metaclass(CachedMetaClass, parsing.Base)):
|
||||
result += super_result
|
||||
return result
|
||||
|
||||
def get_subscope_by_name(self, name):
|
||||
for sub in reversed(self.subscopes):
|
||||
if sub.name.get_code() == name:
|
||||
return sub
|
||||
raise KeyError("Couldn't find subscope.")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.base.name
|
||||
@@ -480,6 +484,37 @@ class Execution(Executable):
|
||||
def get_return_types(self, evaluate_generator=False):
|
||||
""" Get the return types of a function. """
|
||||
stmts = []
|
||||
if self.base.parent() == builtin.builtin_scope \
|
||||
and not isinstance(self.base, (Generator, Array)):
|
||||
func_name = str(self.base.name)
|
||||
|
||||
# some implementations of builtins:
|
||||
if func_name == 'getattr':
|
||||
# follow the first param
|
||||
try:
|
||||
objects = follow_call_list([self.var_args[0]])
|
||||
names = follow_call_list([self.var_args[1]])
|
||||
except IndexError:
|
||||
debug.warning('getattr() called with to few args.')
|
||||
return []
|
||||
|
||||
for obj in objects:
|
||||
if not isinstance(obj, (Instance, Class)):
|
||||
debug.warning('getattr called without instance')
|
||||
return []
|
||||
|
||||
for name in names:
|
||||
key = name.var_args.get_only_subelement()
|
||||
try:
|
||||
stmts.append(obj.get_subscope_by_name(key))
|
||||
except KeyError:
|
||||
debug.warning('called getattr() without string')
|
||||
#if not (isinstance(name, Instance) \
|
||||
#and name.var_args:
|
||||
#debug.warning('getattr called without instance')
|
||||
#return []
|
||||
return stmts
|
||||
|
||||
if self.base.isinstance(Class):
|
||||
# There maybe executions of executions.
|
||||
stmts = [Instance(self.base, self.var_args)]
|
||||
@@ -1119,6 +1154,10 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
|
||||
# if there are results, ignore the other scopes
|
||||
if result:
|
||||
break
|
||||
|
||||
if not result and isinstance(scope, Instance):
|
||||
# getattr() / __getattr__ / __getattribute__
|
||||
result += check_getattr(scope, name_str)
|
||||
debug.dbg('sfn filter "%s" in %s: %s' % (name_str, scope, result))
|
||||
return result
|
||||
|
||||
@@ -1142,12 +1181,37 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
|
||||
else:
|
||||
if isinstance(scope, Instance):
|
||||
scope_generator = scope.scope_generator()
|
||||
else:
|
||||
if isinstance(scope, Class):
|
||||
# classes are only available directly via chaining?
|
||||
# strange stuff...
|
||||
names = scope.get_defined_names()
|
||||
else:
|
||||
names = get_defined_names_for_position(scope, position)
|
||||
scope_generator = iter([(scope, names)])
|
||||
|
||||
return descriptor_check(remove_statements(filter_name(scope_generator)))
|
||||
|
||||
def check_getattr(inst, name_str):
|
||||
result = []
|
||||
# str is important to lose the NamePart!
|
||||
name = parsing.Call(str(name_str), parsing.Call.STRING, (0, 0), inst)
|
||||
args = helpers.generate_param_array([name])
|
||||
try:
|
||||
result = inst.execute_subscope_by_name('__getattr__', args)
|
||||
except KeyError:
|
||||
pass
|
||||
if not result:
|
||||
# this is a little bit special. `__getattribute__` is executed
|
||||
# before anything else. But: I know no use case, where this
|
||||
# could be practical and the jedi would return wrong types. If
|
||||
# you ever have something, let me know!
|
||||
try:
|
||||
result = inst.execute_subscope_by_name('__getattribute__', args)
|
||||
except KeyError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
def get_iterator_types(inputs):
|
||||
""" Returns the types of any iterator (arrays, yields, __iter__, etc). """
|
||||
|
||||
@@ -464,3 +464,44 @@ a[1]
|
||||
WithoutMethod.blub(WithoutMethod())
|
||||
#? str()
|
||||
WithoutMethod.blub(B())
|
||||
|
||||
# -----------------
|
||||
# __getattr__ / getattr() / __getattribute__
|
||||
# -----------------
|
||||
|
||||
#? str().upper
|
||||
getattr(str(), 'upper')
|
||||
#? str.upper
|
||||
getattr(str, 'upper')
|
||||
|
||||
# some strange getattr calls
|
||||
#?
|
||||
getattr(str, 1)
|
||||
#?
|
||||
getattr()
|
||||
#?
|
||||
getattr(str)
|
||||
#?
|
||||
getattr(getattr, 1)
|
||||
|
||||
|
||||
class Base():
|
||||
def ret(self, b):
|
||||
return b
|
||||
|
||||
class Wrapper():
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.obj, name)
|
||||
|
||||
class Wrapper2():
|
||||
def __getattribute__(self, name):
|
||||
return getattr(Base(), name)
|
||||
|
||||
#? int()
|
||||
Wrapper(Base()).ret(3)
|
||||
|
||||
#? int()
|
||||
Wrapper2(Base()).ret(3)
|
||||
|
||||
Reference in New Issue
Block a user