Skip to content

Commit 3a808fa

Browse files
function parser finds function expressions
1 parent 134fb1e commit 3a808fa

File tree

2 files changed

+61
-19
lines changed

2 files changed

+61
-19
lines changed

bpython/repl.py

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -479,41 +479,59 @@ def get_object(self, name):
479479

480480
@classmethod
481481
def _funcname_and_argnum(cls, line):
482-
"""Name of the current function and where we are in args
482+
# each list in stack:
483+
# [full_expr, function_expr, arg_number, opening]
484+
# empty string in num_commas means we've encountered a kwarg
485+
# so we're done counting
483486

484-
Returns (None, None) if can't be found."""
485-
stack = [['', 0, '']]
487+
# new plan: do a full parse, then combine things
488+
stack = [['', '', 0, '']]
486489
try:
487490
for (token, value) in PythonLexer().get_tokens(line):
488491
if token is Token.Punctuation:
489492
if value in '([{':
490-
stack.append(['', 0, value])
493+
stack.append(['', '', 0, value])
491494
elif value in ')]}':
492-
stack.pop()
495+
full, _, _, start = stack.pop()
496+
expr = start + full + value
497+
stack[-1][1] += expr
498+
stack[-1][0] += expr
493499
elif value == ',':
494500
try:
495-
stack[-1][1] += 1
501+
stack[-1][2] += 1
496502
except TypeError:
497-
stack[-1][1] = ''
498-
stack[-1][0] = ''
499-
elif value == ':' and stack[-1][2] == 'lambda':
500-
stack.pop()
503+
stack[-1][2] = ''
504+
stack[-1][1] = ''
505+
stack[-1][0] += value
506+
elif value == ':' and stack[-1][3] == 'lambda':
507+
expr = stack.pop()[0] + ':'
508+
stack[-1][1] += expr
509+
stack[-1][0] += expr
501510
else:
502-
stack[-1][0] = ''
503-
elif (token is Token.Name or token in Token.Name.subtypes or
511+
stack[-1][1] = ''
512+
stack[-1][0] += value
513+
elif (token is Token.Number or
514+
token in Token.Number.subtypes or
515+
token is Token.Name or token in Token.Name.subtypes or
504516
token is Token.Operator and value == '.'):
517+
stack[-1][1] += value
505518
stack[-1][0] += value
506519
elif token is Token.Operator and value == '=':
507-
stack[-1][1] = stack[-1][0]
508-
stack[-1][0] = ''
520+
stack[-1][2] = stack[-1][1]
521+
stack[-1][1] = ''
522+
stack[-1][0] += value
523+
elif token is Token.Number or token in Token.Number.subtypes:
524+
stack[-1][1] = value
525+
stack[-1][0] += value
509526
elif token is Token.Keyword and value == 'lambda':
510-
stack.append(['', 0, value])
527+
stack.append([value, '', 0, value])
511528
else:
512-
stack[-1][0] = ''
513-
while stack[-1][2] in '[{':
529+
stack[-1][1] = ''
530+
stack[-1][0] += value
531+
while stack[-1][3] in '[{':
514532
stack.pop()
515-
_, arg_number, _ = stack.pop()
516-
func, _, _ = stack.pop()
533+
_, _, arg_number, _ = stack.pop()
534+
_, func, _, _ = stack.pop()
517535
return func, arg_number
518536
except IndexError:
519537
return None, None

bpython/test/test_repl.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,30 @@ def test_methods_of_expressions(self):
238238
self.assertTrue(self.repl.get_args())
239239

240240

241+
class TestArgspecInternal(unittest.TestCase):
242+
def test_function_expressions(self):
243+
te = self.assertTupleEqual
244+
fa = lambda line: repl.Repl._funcname_and_argnum(line)
245+
for line, (func, argnum) in [
246+
('spam(', ('spam', 0)),
247+
('spam((), ', ('spam', 1)),
248+
('spam.eggs((), ', ('spam.eggs', 1)),
249+
('spam[abc].eggs((), ', ('spam[abc].eggs', 1)),
250+
('spam[0].eggs((), ', ('spam[0].eggs', 1)),
251+
('spam[a + b]eggs((), ', ('spam[a + b]eggs', 1)),
252+
('spam().eggs((), ', ('spam().eggs', 1)),
253+
('spam(1, 2).eggs((), ', ('spam(1, 2).eggs', 1)),
254+
('spam(1, f(1)).eggs((), ', ('spam(1, f(1)).eggs', 1)),
255+
('[0].eggs((), ', ('[0].eggs', 1)),
256+
('[0][0]((), {}).eggs((), ', ('[0][0]((), {}).eggs', 1)),
257+
('a + spam[0].eggs((), ', ('spam[0].eggs', 1)),
258+
("spam(", ("spam", 0)),
259+
("spam(map([]", ("map", 0)),
260+
("spam((), ", ("spam", 1))
261+
]:
262+
te(fa(line), (func, argnum))
263+
264+
241265
class TestGetSource(unittest.TestCase):
242266
def setUp(self):
243267
self.repl = FakeRepl()

0 commit comments

Comments
 (0)