1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00

testcrypt.py: use parameter names in diagnostics.

Making a virtue of the necessity of adding parameter names to
testcrypt.h a couple of commits ago, we can now use those names to
improve diagnostics, so that if you use the wrong type in a Python
function call the error message will tell you the name as well as the
index of the offending argument.

Also, the repr() text for the function itself will now print a full
prototype (albeit in a nasty hybrid of C, Python and testcrypt.h
syntax) which shows all the parameter names. That should be handy when
trying to remember the order of arguments at the REPL prompt.
This commit is contained in:
Simon Tatham 2021-11-21 13:21:01 +00:00
parent 3153f3ef39
commit aaaf11d7fb

View File

@ -149,7 +149,7 @@ def marshal_string(val):
else "%{:02x}".format(b) else "%{:02x}".format(b)
for b in val) for b in val)
def make_argword(arg, argtype, fnname, argindex, to_preserve): def make_argword(arg, argtype, fnname, argindex, argname, to_preserve):
typename, consumed = argtype typename, consumed = argtype
if typename.startswith("opt_"): if typename.startswith("opt_"):
if arg is None: if arg is None:
@ -166,8 +166,8 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
if isinstance(arg, Value): if isinstance(arg, Value):
if arg._typename != typename: if arg._typename != typename:
raise TypeError( raise TypeError(
"{}() argument {:d} should be {} ({} given)".format( "{}() argument #{:d} ({}) should be {} ({} given)".format(
fnname, argindex, typename, arg._typename)) fnname, argindex, argname, typename, arg._typename))
ident = arg._ident ident = arg._ident
if consumed: if consumed:
arg._consumed() arg._consumed()
@ -185,14 +185,14 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve):
return arg return arg
if typename == "mpint_list": if typename == "mpint_list":
sublist = [make_argword(len(arg), ("uint", False), sublist = [make_argword(len(arg), ("uint", False),
fnname, argindex, to_preserve)] fnname, argindex, argname, to_preserve)]
for val in arg: for val in arg:
sublist.append(make_argword(val, ("val_mpint", False), sublist.append(make_argword(val, ("val_mpint", False),
fnname, argindex, to_preserve)) fnname, argindex, argname, to_preserve))
return b" ".join(coerce_to_bytes(sub) for sub in sublist) return b" ".join(coerce_to_bytes(sub) for sub in sublist)
raise TypeError( raise TypeError(
"Can't convert {}() argument {:d} to {} (value was {!r})".format( "Can't convert {}() argument #{:d} ({}) to {} (value was {!r})".format(
fnname, argindex, typename, arg)) fnname, argindex, argname, typename, arg))
def unpack_string(identifier): def unpack_string(identifier):
retwords = childprocess.funcall("getstring", [identifier]) retwords = childprocess.funcall("getstring", [identifier])
@ -247,12 +247,20 @@ def make_retvals(rettypes, retwords, unpack_strings=True):
for rettype, word in zip(rettypes, retwords)] for rettype, word in zip(rettypes, retwords)]
class Function(object): class Function(object):
def __init__(self, fnname, rettypes, argtypes): def __init__(self, fnname, rettypes, retnames, argtypes, argnames):
self.fnname = fnname self.fnname = fnname
self.rettypes = rettypes self.rettypes = rettypes
self.retnames = retnames
self.argtypes = argtypes self.argtypes = argtypes
self.argnames = argnames
def __repr__(self): def __repr__(self):
return "<Function {}>".format(self.fnname) return "<Function {}({}) -> ({})>".format(
self.fnname,
", ".join(("consumed " if c else "")+t+" "+n
for (t,c),n in zip(self.argtypes, self.argnames)),
", ".join((t+" "+n if n is not None else t)
for t,n in zip(self.rettypes, self.retnames)),
)
def __call__(self, *args): def __call__(self, *args):
if len(args) != len(self.argtypes): if len(args) != len(self.argtypes):
raise TypeError( raise TypeError(
@ -261,7 +269,8 @@ class Function(object):
to_preserve = [] to_preserve = []
retwords = childprocess.funcall( retwords = childprocess.funcall(
self.fnname, [make_argword(args[i], self.argtypes[i], self.fnname, [make_argword(args[i], self.argtypes[i],
self.fnname, i, to_preserve) self.fnname, i, self.argnames[i],
to_preserve)
for i in range(len(args))]) for i in range(len(args))])
retvals = make_retvals(self.rettypes, retwords) retvals = make_retvals(self.rettypes, retwords)
if len(retvals) == 0: if len(retvals) == 0:
@ -380,14 +389,18 @@ def _setup(scope):
tokens = _lex_testcrypt_header(header) tokens = _lex_testcrypt_header(header)
for function, rettype, arglist in _parse_testcrypt_header(tokens): for function, rettype, arglist in _parse_testcrypt_header(tokens):
rettypes = [] rettypes = []
retnames = []
if rettype != "void": if rettype != "void":
rettypes.append(trim_argtype(rettype)) rettypes.append(trim_argtype(rettype))
retnames.append(None)
argtypes = [] argtypes = []
argnames = []
argsconsumed = [] argsconsumed = []
for arg, argname in arglist: for arg, argname in arglist:
if arg.startswith(outprefix): if arg.startswith(outprefix):
rettypes.append(trim_argtype(arg[len(outprefix):])) rettypes.append(trim_argtype(arg[len(outprefix):]))
retnames.append(argname)
else: else:
consumed = False consumed = False
if arg.startswith(consprefix): if arg.startswith(consprefix):
@ -395,7 +408,9 @@ def _setup(scope):
consumed = True consumed = True
arg = trim_argtype(arg) arg = trim_argtype(arg)
argtypes.append((arg, consumed)) argtypes.append((arg, consumed))
func = Function(function, rettypes, argtypes) argnames.append(argname)
func = Function(function, rettypes, retnames,
argtypes, argnames)
scope[function] = func scope[function] = func
if len(argtypes) > 0: if len(argtypes) > 0:
t = argtypes[0][0] t = argtypes[0][0]