mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-21 04:51:13 +08:00
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:
@@ -511,6 +511,9 @@ class FakeSequence(_FakeArray):
|
||||
def py__iter__(self):
|
||||
return self._lazy_context_list
|
||||
|
||||
def py__bool__(self):
|
||||
return bool(len(self._lazy_context_list))
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s of %s>" % (type(self).__name__, self._lazy_context_list)
|
||||
|
||||
|
||||
@@ -25,6 +25,23 @@ from jedi.evaluate import param
|
||||
from jedi.evaluate import analysis
|
||||
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):
|
||||
pass
|
||||
@@ -259,20 +276,24 @@ def collections_namedtuple(evaluator, obj, arguments):
|
||||
else:
|
||||
return set()
|
||||
|
||||
base = collections._class_template
|
||||
base += _NAMEDTUPLE_INIT
|
||||
# Build source
|
||||
source = collections._class_template.format(
|
||||
source = base.format(
|
||||
typename=name,
|
||||
field_names=fields,
|
||||
field_names=tuple(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),
|
||||
field_defs='\n'.join(collections._field_template.format(index=index, name=name)
|
||||
for index, name in enumerate(fields))
|
||||
)
|
||||
|
||||
# Parse source
|
||||
generated_class = next(evaluator.grammar.parse(source).iter_classdefs())
|
||||
return set([er.ClassContext(evaluator, generated_class, evaluator.BUILTINS)])
|
||||
module = evaluator.grammar.parse(source)
|
||||
generated_class = next(module.iter_classdefs())
|
||||
parent_context = er.ModuleContext(evaluator, module, '')
|
||||
return set([er.ClassContext(evaluator, generated_class, parent_context)])
|
||||
|
||||
|
||||
@argument_clinic('first, /')
|
||||
|
||||
@@ -43,3 +43,35 @@ def test_namedtuple_list():
|
||||
assert completions == set()
|
||||
else:
|
||||
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()]
|
||||
|
||||
Reference in New Issue
Block a user