Skip to content

Commit e906df7

Browse files
committed
Refactor _funcname_and_argnum to avoid Exception-based control flow
1 parent 0ada13d commit e906df7

File tree

1 file changed

+49
-37
lines changed

1 file changed

+49
-37
lines changed

bpython/repl.py

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import time
3636
import traceback
3737
from abc import abstractmethod
38+
from dataclasses import dataclass
3839
from itertools import takewhile
3940
from pathlib import Path
4041
from types import ModuleType, TracebackType
@@ -380,6 +381,17 @@ class SourceNotFound(Exception):
380381
"""Exception raised when the requested source could not be found."""
381382

382383

384+
@dataclass
385+
class _FuncExpr:
386+
"""Stack element in Repl._funcname_and_argnum"""
387+
388+
full_expr: str
389+
function_expr: str
390+
arg_number: int
391+
opening: str
392+
keyword: Optional[str] = None
393+
394+
383395
class Repl:
384396
"""Implements the necessary guff for a Python-repl-alike interface
385397
@@ -564,37 +576,37 @@ def get_object(self, name):
564576
return obj
565577

566578
@classmethod
567-
def _funcname_and_argnum(cls, line):
579+
def _funcname_and_argnum(
580+
cls, line: str
581+
) -> Tuple[Optional[str], Optional[Union[str, int]]]:
568582
"""Parse out the current function name and arg from a line of code."""
569-
# each list in stack:
570-
# [full_expr, function_expr, arg_number, opening]
571-
# arg_number may be a string if we've encountered a keyword
572-
# argument so we're done counting
573-
stack = [["", "", 0, ""]]
583+
# each element in stack is a _FuncExpr instance
584+
# if keyword is not None, we've encountered a keyword and so we're done counting
585+
stack = [_FuncExpr("", "", 0, "")]
574586
try:
575587
for (token, value) in Python3Lexer().get_tokens(line):
576588
if token is Token.Punctuation:
577589
if value in "([{":
578-
stack.append(["", "", 0, value])
590+
stack.append(_FuncExpr("", "", 0, value))
579591
elif value in ")]}":
580-
full, _, _, start = stack.pop()
581-
expr = start + full + value
582-
stack[-1][1] += expr
583-
stack[-1][0] += expr
592+
element = stack.pop()
593+
expr = element.opening + element.full_expr + value
594+
stack[-1].function_expr += expr
595+
stack[-1].full_expr += expr
584596
elif value == ",":
585-
try:
586-
stack[-1][2] += 1
587-
except TypeError:
588-
stack[-1][2] = ""
589-
stack[-1][1] = ""
590-
stack[-1][0] += value
591-
elif value == ":" and stack[-1][3] == "lambda":
592-
expr = stack.pop()[0] + ":"
593-
stack[-1][1] += expr
594-
stack[-1][0] += expr
597+
if stack[-1].keyword is None:
598+
stack[-1].arg_number += 1
599+
else:
600+
stack[-1].keyword = ""
601+
stack[-1].function_expr = ""
602+
stack[-1].full_expr += value
603+
elif value == ":" and stack[-1].opening == "lambda":
604+
expr = stack.pop().full_expr + ":"
605+
stack[-1].function_expr += expr
606+
stack[-1].full_expr += expr
595607
else:
596-
stack[-1][1] = ""
597-
stack[-1][0] += value
608+
stack[-1].function_expr = ""
609+
stack[-1].full_expr += value
598610
elif (
599611
token is Token.Number
600612
or token in Token.Number.subtypes
@@ -603,25 +615,25 @@ def _funcname_and_argnum(cls, line):
603615
or token is Token.Operator
604616
and value == "."
605617
):
606-
stack[-1][1] += value
607-
stack[-1][0] += value
618+
stack[-1].function_expr += value
619+
stack[-1].full_expr += value
608620
elif token is Token.Operator and value == "=":
609-
stack[-1][2] = stack[-1][1]
610-
stack[-1][1] = ""
611-
stack[-1][0] += value
621+
stack[-1].keyword = stack[-1].function_expr
622+
stack[-1].function_expr = ""
623+
stack[-1].full_expr += value
612624
elif token is Token.Number or token in Token.Number.subtypes:
613-
stack[-1][1] = value
614-
stack[-1][0] += value
625+
stack[-1].function_expr = value
626+
stack[-1].full_expr += value
615627
elif token is Token.Keyword and value == "lambda":
616-
stack.append([value, "", 0, value])
628+
stack.append(_FuncExpr(value, "", 0, value))
617629
else:
618-
stack[-1][1] = ""
619-
stack[-1][0] += value
620-
while stack[-1][3] in "[{":
630+
stack[-1].function_expr = ""
631+
stack[-1].full_expr += value
632+
while stack[-1].opening in "[{":
621633
stack.pop()
622-
_, _, arg_number, _ = stack.pop()
623-
_, func, _, _ = stack.pop()
624-
return func, arg_number
634+
elem1 = stack.pop()
635+
elem2 = stack.pop()
636+
return elem2.function_expr, elem1.keyword or elem1.arg_number
625637
except IndexError:
626638
return None, None
627639

0 commit comments

Comments
 (0)