1
0
forked from VimPlug/jedi

Fix namedtuple support

There were a couple issues:
 - namedtuple with one member didn't work
 - namedtuple content access was never possible
 - operator.itemgetter didn't work properly. Corrected py__bool__ for FakeSequence

Fixes #730.
This commit is contained in:
Dave Halter
2017-09-12 11:06:16 +02:00
parent 4a544c29ea
commit a8a15114ac
3 changed files with 61 additions and 5 deletions

View File

@@ -511,6 +511,9 @@ class FakeSequence(_FakeArray):
def py__iter__(self): def py__iter__(self):
return self._lazy_context_list return self._lazy_context_list
def py__bool__(self):
return bool(len(self._lazy_context_list))
def __repr__(self): def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self._lazy_context_list) return "<%s of %s>" % (type(self).__name__, self._lazy_context_list)

View File

@@ -25,6 +25,23 @@ from jedi.evaluate import param
from jedi.evaluate import analysis from jedi.evaluate import analysis
from jedi.evaluate.context import LazyTreeContext, ContextualizedNode from jedi.evaluate.context import LazyTreeContext, ContextualizedNode
# Now this is all part of fake tuples in Jedi. However super doesn't work on
# __init__ and __new__ doesn't work at all. So adding this to nametuples is
# just the easiest way.
_NAMEDTUPLE_INIT = """
def __init__(_cls, {arg_list}):
'A helper function for namedtuple.'
self.__iterable = ({arg_list})
def __iter__(self):
for i in self.__iterable:
yield i
def __getitem__(self, y):
return self.__iterable[y]
"""
class NotInStdLib(LookupError): class NotInStdLib(LookupError):
pass pass
@@ -259,20 +276,24 @@ def collections_namedtuple(evaluator, obj, arguments):
else: else:
return set() return set()
base = collections._class_template
base += _NAMEDTUPLE_INIT
# Build source # Build source
source = collections._class_template.format( source = base.format(
typename=name, typename=name,
field_names=fields, field_names=tuple(fields),
num_fields=len(fields), num_fields=len(fields),
arg_list=', '.join(fields), arg_list = repr(tuple(fields)).replace("'", "")[1:-1],
repr_fmt=', '.join(collections._repr_template.format(name=name) for name in fields), repr_fmt=', '.join(collections._repr_template.format(name=name) for name in fields),
field_defs='\n'.join(collections._field_template.format(index=index, name=name) field_defs='\n'.join(collections._field_template.format(index=index, name=name)
for index, name in enumerate(fields)) for index, name in enumerate(fields))
) )
# Parse source # Parse source
generated_class = next(evaluator.grammar.parse(source).iter_classdefs()) module = evaluator.grammar.parse(source)
return set([er.ClassContext(evaluator, generated_class, evaluator.BUILTINS)]) generated_class = next(module.iter_classdefs())
parent_context = er.ModuleContext(evaluator, module, '')
return set([er.ClassContext(evaluator, generated_class, parent_context)])
@argument_clinic('first, /') @argument_clinic('first, /')

View File

@@ -43,3 +43,35 @@ def test_namedtuple_list():
assert completions == set() assert completions == set()
else: else:
assert completions == set(['legs', 'length', 'large']) assert completions == set(['legs', 'length', 'large'])
def test_namedtuple_content():
source = dedent("""\
import collections
Foo = collections.namedtuple('Foo', ['bar', 'baz'])
named = Foo(baz=4, bar=3.0)
unnamed = Foo(4, '')
""")
def d(source):
x, = Script(source).goto_definitions()
return x.name
assert d(source + 'unnamed.bar') == 'int'
assert d(source + 'unnamed.baz') == 'str'
assert d(source + 'named.bar') == 'float'
assert d(source + 'named.baz') == 'int'
def test_nested_namedtuples():
"""
From issue #730.
"""
s = Script(dedent('''
import collections
Dataset = collections.namedtuple('Dataset', ['data'])
Datasets = collections.namedtuple('Datasets', ['train'])
train_x = Datasets(train=Dataset('data_value'))
train_x.train.'''
))
assert 'data' in [c.name for c in s.completions()]