mirror of
https://github.com/davidhalter/jedi.git
synced 2026-01-30 21:15:23 +08:00
Merge pull request #641 from kelleyk/dev
Fix issues with the way lambdas are handled
This commit is contained in:
@@ -33,5 +33,6 @@ Savor d'Isavano (@KenetJervet) <newelevenken@163.com>
|
||||
Phillip Berndt (@phillipberndt) <phillip.berndt@gmail.com>
|
||||
Ian Lee (@IanLee1521) <IanLee1521@gmail.com>
|
||||
Farkhad Khatamov (@hatamov) <comsgn@gmail.com>
|
||||
Kevin Kelley (@kelleyk) <kelleyk@kelleyk.net>
|
||||
|
||||
Note: (@user) means a github user name.
|
||||
|
||||
@@ -336,7 +336,7 @@ class BaseDefinition(object):
|
||||
raise AttributeError()
|
||||
followed = followed[0] # only check the first one.
|
||||
|
||||
if followed.type == 'funcdef':
|
||||
if followed.type in ('funcdef', 'lambda'):
|
||||
if isinstance(followed, er.InstanceElement):
|
||||
params = followed.params[1:]
|
||||
else:
|
||||
|
||||
@@ -748,6 +748,15 @@ def _create_params(parent, argslist_list):
|
||||
class Function(ClassOrFunc):
|
||||
"""
|
||||
Used to store the parsed contents of a python function.
|
||||
|
||||
Children:
|
||||
0) <Keyword: def>
|
||||
1) <Name>
|
||||
2) parameter list (including open-paren and close-paren <Operator>s)
|
||||
3) <Operator: :>
|
||||
4) Node() representing function body
|
||||
5) ??
|
||||
6) annotation (if present)
|
||||
"""
|
||||
__slots__ = ('listeners',)
|
||||
type = 'funcdef'
|
||||
@@ -760,6 +769,7 @@ class Function(ClassOrFunc):
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
# Contents of parameter lit minus the leading <Operator: (> and the trailing <Operator: )>.
|
||||
return self.children[2].children[1:-1]
|
||||
|
||||
@property
|
||||
@@ -791,10 +801,13 @@ class Function(ClassOrFunc):
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
func_name = func_name or self.children[1]
|
||||
code = unicode(func_name) + self.children[2].get_code()
|
||||
func_name = func_name or self.name
|
||||
code = unicode(func_name) + self._get_paramlist_code()
|
||||
return '\n'.join(textwrap.wrap(code, width))
|
||||
|
||||
def _get_paramlist_code(self):
|
||||
return self.children[2].get_code()
|
||||
|
||||
@property
|
||||
def doc(self):
|
||||
""" Return a document string including call signature. """
|
||||
@@ -805,6 +818,12 @@ class Function(ClassOrFunc):
|
||||
class Lambda(Function):
|
||||
"""
|
||||
Lambdas are basically trimmed functions, so give it the same interface.
|
||||
|
||||
Children:
|
||||
0) <Keyword: lambda>
|
||||
*) <Param x> for each argument x
|
||||
-2) <Operator: :>
|
||||
-1) Node() representing body
|
||||
"""
|
||||
type = 'lambda'
|
||||
__slots__ = ()
|
||||
@@ -813,9 +832,17 @@ class Lambda(Function):
|
||||
# We don't want to call the Function constructor, call its parent.
|
||||
super(Function, self).__init__(children)
|
||||
self.listeners = set() # not used here, but in evaluation.
|
||||
lst = self.children[1:-2] # After `def foo`
|
||||
lst = self.children[1:-2] # Everything between `lambda` and the `:` operator is a parameter.
|
||||
self.children[1:-2] = _create_params(self, lst)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
# Borrow the position of the <Keyword: lambda> AST node.
|
||||
return Name(self.children[0].position_modifier, '<lambda>', self.children[0].start_pos)
|
||||
|
||||
def _get_paramlist_code(self):
|
||||
return '(' + ''.join(param.get_code() for param in self.params).strip() + ')'
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
return self.children[1:-2]
|
||||
@@ -823,6 +850,7 @@ class Lambda(Function):
|
||||
def is_generator(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def yields(self):
|
||||
return []
|
||||
|
||||
|
||||
64
test/test_parser/test_parser_tree.py
Normal file
64
test/test_parser/test_parser_tree.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 # This file contains Unicode characters.
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
from jedi._compatibility import u, unicode
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import tree as pt
|
||||
|
||||
|
||||
class TestsFunctionAndLambdaParsing(object):
|
||||
|
||||
FIXTURES = [
|
||||
('def my_function(x, y, z):\n return x + y * z\n', {
|
||||
'name': 'my_function',
|
||||
'call_sig': 'my_function(x, y, z)',
|
||||
'params': ['x', 'y', 'z'],
|
||||
}),
|
||||
('lambda x, y, z: x + y * z\n', {
|
||||
'name': '<lambda>',
|
||||
'call_sig': '<lambda>(x, y, z)',
|
||||
'params': ['x', 'y', 'z'],
|
||||
}),
|
||||
]
|
||||
|
||||
@pytest.fixture(params=FIXTURES)
|
||||
def node(self, request):
|
||||
parsed = Parser(load_grammar(), dedent(u(request.param[0])))
|
||||
request.keywords['expected'] = request.param[1]
|
||||
return parsed.module.subscopes[0]
|
||||
|
||||
@pytest.fixture()
|
||||
def expected(self, request, node):
|
||||
return request.keywords['expected']
|
||||
|
||||
def test_name(self, node, expected):
|
||||
assert isinstance(node.name, pt.Name)
|
||||
assert unicode(node.name) == u(expected['name'])
|
||||
|
||||
def test_params(self, node, expected):
|
||||
assert isinstance(node.params, list)
|
||||
assert all(isinstance(x, pt.Param) for x in node.params)
|
||||
assert [unicode(x.name) for x in node.params] == [u(x) for x in expected['params']]
|
||||
|
||||
def test_is_generator(self, node, expected):
|
||||
assert node.is_generator() is expected.get('is_generator', False)
|
||||
|
||||
def test_yields(self, node, expected):
|
||||
# TODO: There's a comment in the code noting that the current implementation is incorrect. This returns an
|
||||
# empty list at the moment (not e.g. False).
|
||||
if expected.get('yields', False):
|
||||
assert node.yields
|
||||
else:
|
||||
assert not node.yields
|
||||
|
||||
def test_annotation(self, node, expected):
|
||||
assert node.annotation() is expected.get('annotation', None)
|
||||
|
||||
def test_get_call_signature(self, node, expected):
|
||||
assert node.get_call_signature() == expected['call_sig']
|
||||
|
||||
def test_doc(self, node, expected):
|
||||
assert node.doc == expected.get('doc') or (expected['call_sig'] + '\n\n')
|
||||
Reference in New Issue
Block a user