Skip to content

Commit 84a5467

Browse files
committed
Turn ArgSpec into a dataclass
1 parent ba1dac7 commit 84a5467

File tree

3 files changed

+33
-46
lines changed

3 files changed

+33
-46
lines changed

bpython/autocomplete.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,11 +570,11 @@ def matches(
570570

571571
matches = {
572572
f"{name}="
573-
for name in argspec.argspec[0]
573+
for name in argspec.argspec.args
574574
if isinstance(name, str) and name.startswith(r.word)
575575
}
576576
matches.update(
577-
name + "=" for name in argspec.argspec[4] if name.startswith(r.word)
577+
name + "=" for name in argspec.argspec.kwonly if name.startswith(r.word)
578578
)
579579
return matches if matches else None
580580

bpython/inspection.py

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import keyword
2626
import pydoc
2727
import re
28-
from collections import namedtuple
2928
from dataclasses import dataclass
3029
from typing import Any, Callable, Optional, Type, Dict, List
3130
from types import MemberDescriptorType, TracebackType
@@ -37,18 +36,15 @@
3736
from .lazyre import LazyReCompile
3837

3938

40-
ArgSpec = namedtuple(
41-
"ArgSpec",
42-
[
43-
"args",
44-
"varargs",
45-
"varkwargs",
46-
"defaults",
47-
"kwonly",
48-
"kwonly_defaults",
49-
"annotations",
50-
],
51-
)
39+
@dataclass
40+
class ArgSpec:
41+
args: List[str]
42+
varargs: Optional[str]
43+
varkwargs: Optional[str]
44+
defaults: Optional[List[Any]]
45+
kwonly: List[str]
46+
kwonly_defaults: Optional[Dict[str, Any]]
47+
annotations: Optional[Dict[str, Any]]
5248

5349

5450
@dataclass
@@ -171,32 +167,33 @@ def parsekeywordpairs(signature: str) -> Dict[str, str]:
171167
return {item[0]: "".join(item[2:]) for item in stack if len(item) >= 3}
172168

173169

174-
def fixlongargs(f, argspec):
170+
def fixlongargs(f: Callable, argspec: ArgSpec) -> ArgSpec:
175171
"""Functions taking default arguments that are references to other objects
176172
whose str() is too big will cause breakage, so we swap out the object
177173
itself with the name it was referenced with in the source by parsing the
178174
source itself !"""
179-
if argspec[3] is None:
175+
if argspec.defaults is None:
180176
# No keyword args, no need to do anything
181-
return
182-
values = list(argspec[3])
177+
return argspec
178+
values = list(argspec.defaults)
183179
if not values:
184-
return
185-
keys = argspec[0][-len(values) :]
180+
return argspec
181+
keys = argspec.args[-len(values) :]
186182
try:
187183
src = inspect.getsourcelines(f)
188184
except (OSError, IndexError):
189185
# IndexError is raised in inspect.findsource(), can happen in
190186
# some situations. See issue #94.
191-
return
187+
return argspec
192188
signature = "".join(src[0])
193189
kwparsed = parsekeywordpairs(signature)
194190

195191
for i, (key, value) in enumerate(zip(keys, values)):
196192
if len(repr(value)) != len(kwparsed[key]):
197193
values[i] = _Repr(kwparsed[key])
198194

199-
argspec[3] = values
195+
argspec.defaults = values
196+
return argspec
200197

201198

202199
getpydocspec_re = LazyReCompile(
@@ -247,7 +244,7 @@ def getpydocspec(f, func):
247244
)
248245

249246

250-
def getfuncprops(func, f):
247+
def getfuncprops(func: str, f: Callable) -> Optional[FuncProps]:
251248
# Check if it's a real bound method or if it's implicitly calling __init__
252249
# (i.e. FooClass(...) and not FooClass.__init__(...) -- the former would
253250
# not take 'self', the latter would:
@@ -268,9 +265,8 @@ def getfuncprops(func, f):
268265
# '__init__' throws xmlrpclib.Fault (see #202)
269266
return None
270267
try:
271-
argspec = get_argspec_from_signature(f)
272-
fixlongargs(f, argspec)
273-
argspec = ArgSpec(*argspec)
268+
argspec = _get_argspec_from_signature(f)
269+
argspec = fixlongargs(f, argspec)
274270
fprops = FuncProps(func, argspec, is_bound_method)
275271
except (TypeError, KeyError, ValueError):
276272
argspec = getpydocspec(f, func)
@@ -289,7 +285,7 @@ def is_eval_safe_name(string: str) -> bool:
289285
)
290286

291287

292-
def get_argspec_from_signature(f):
288+
def _get_argspec_from_signature(f: Callable) -> ArgSpec:
293289
"""Get callable signature from inspect.signature in argspec format.
294290
295291
inspect.signature is a Python 3 only function that returns the signature of
@@ -324,26 +320,15 @@ def get_argspec_from_signature(f):
324320
elif parameter.kind == inspect.Parameter.VAR_KEYWORD:
325321
varkwargs = parameter.name
326322

327-
# inspect.getfullargspec returns None for 'defaults', 'kwonly_defaults' and
328-
# 'annotations' if there are no values for them.
329-
if not defaults:
330-
defaults = None
331-
332-
if not kwonly_defaults:
333-
kwonly_defaults = None
334-
335-
if not annotations:
336-
annotations = None
337-
338-
return [
323+
return ArgSpec(
339324
args,
340325
varargs,
341326
varkwargs,
342-
defaults,
327+
defaults if defaults else None,
343328
kwonly,
344-
kwonly_defaults,
345-
annotations,
346-
]
329+
kwonly_defaults if kwonly_defaults else None,
330+
annotations if annotations else None,
331+
)
347332

348333

349334
get_encoding_line_re = LazyReCompile(r"^.*coding[:=]\s*([-\w.]+).*$")

bpython/urwid.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -763,10 +763,12 @@ def _populate_completion(self):
763763
if self.funcprops:
764764
# This is mostly just stolen from the cli module.
765765
func_name = self.funcprops.func
766-
args = self.funcprops.argspec
766+
args = self.funcprops.argspec.args
767767
is_bound = self.funcprops.is_bound_method
768768
in_arg = self.arg_pos
769-
args, varargs, varkw, defaults = args[:4]
769+
varargs = self.funcprops.argspec.varargs
770+
varkw = self.funcprops.argspec.varkwargs
771+
defaults = self.funcprops.argspec.defaults
770772
kwonly = self.funcprops.argspec.kwonly
771773
kwonly_defaults = self.funcprops.argspec.kwonly_defaults or {}
772774
markup = [("bold name", func_name), ("name", ": (")]

0 commit comments

Comments
 (0)