diff --git a/test/bytecode_3.1/04_def_annotate.pyc b/test/bytecode_3.1/04_def_annotate.pyc index 4529a695..607ee87a 100644 Binary files a/test/bytecode_3.1/04_def_annotate.pyc and b/test/bytecode_3.1/04_def_annotate.pyc differ diff --git a/test/bytecode_3.4/04_def_annotate.pyc b/test/bytecode_3.4/04_def_annotate.pyc index 0acbc4ab..376e2575 100644 Binary files a/test/bytecode_3.4/04_def_annotate.pyc and b/test/bytecode_3.4/04_def_annotate.pyc differ diff --git a/test/bytecode_3.5/04_def_annotate.pyc b/test/bytecode_3.5/04_def_annotate.pyc index 6501af8e..784db926 100644 Binary files a/test/bytecode_3.5/04_def_annotate.pyc and b/test/bytecode_3.5/04_def_annotate.pyc differ diff --git a/test/simple_source/bug31/04_def_annotate.py b/test/simple_source/bug31/04_def_annotate.py index a397f73c..52942dd5 100644 --- a/test/simple_source/bug31/04_def_annotate.py +++ b/test/simple_source/bug31/04_def_annotate.py @@ -18,6 +18,11 @@ def div(a: dict(type=float, help='the dividend'), """Divide a by b""" return a / b +class TestSignatureObject(unittest.TestCase): + def test_signature_on_wkwonly(self): + def test(*, a:float, b:str) -> int: + pass + class SupportsInt(_Protocol): @abstractmethod diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index aaa9cb8d..48cd3cbf 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -79,7 +79,9 @@ from uncompyle6.semantics.consts import ( TABLE_DIRECT, escape, minint, MAP ) -from uncompyle6.semantics.make_function import find_all_globals, find_none +from uncompyle6.semantics.make_function import ( + find_all_globals, find_none, code_has_star_arg, code_has_star_star_arg +) if PYTHON3: from itertools import zip_longest @@ -1659,10 +1661,10 @@ class FragmentsWalker(pysource.SourceWalker, object): params.reverse() # back to correct order - if 4 & code.co_flags: # flag 2 -> variable number of args + if code_has_star_arg(code): params.append('*%s' % code.co_varnames[argc]) argc += 1 - if 8 & code.co_flags: # flag 3 -> keyword args + if code_has_star_star_arg(code): params.append('**%s' % code.co_varnames[argc]) argc += 1 diff --git a/uncompyle6/semantics/make_function.py b/uncompyle6/semantics/make_function.py index 402b9f35..0b545426 100644 --- a/uncompyle6/semantics/make_function.py +++ b/uncompyle6/semantics/make_function.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, 2016 by Rocky Bernstein +# Copyright (c) 2015-2017 by Rocky Bernstein # Copyright (c) 2000-2002 by hartmut Goebel """ All the crazy things we have to do to handle Python functions @@ -45,6 +45,17 @@ def find_none(node): return True return False +# FIXME: put this in xdis +def code_has_star_arg(code): + """Return True iff + the code object has a variable positional parameter (*args-like)""" + return (code.co_flags & 4) != 0 + +def code_has_star_star_arg(code): + """Return True iff + The code object has a variable keyword parameter (**kwargs-like).""" + return (code.co_flags & 8) != 0 + # FIXME: DRY the below code... def make_function3_annotate(self, node, isLambda, nested=1, @@ -87,7 +98,7 @@ def make_function3_annotate(self, node, isLambda, nested=1, l = -len(node) while j >= l and node[j].type in ('annotate_arg' 'annotate_tuple'): annotate_args[annotate_tup[i]] = (node[j][0].attr, - node[j][0] == 'LOAD_CONST') + node[j][0] in ('LOAD_CONST', 'LOAD_GLOBAL')) i -= 1 j -= 1 @@ -96,9 +107,12 @@ def make_function3_annotate(self, node, isLambda, nested=1, # positional args are before kwargs defparams = node[:args_node.attr[0]] pos_args, kw_args, annotate_argc = args_node.attr + if 'return' in annotate_args.keys(): + annotate_argc = len(annotate_args) - 1 else: defparams = node[:args_node.attr] kw_args = 0 + annotate_argc = 0 pass if 3.0 <= self.version <= 3.2: @@ -144,7 +158,7 @@ def make_function3_annotate(self, node, isLambda, nested=1, indent = ' ' * l line_number = self.line_number - if 4 & code.co_flags: # flag 2 -> variable number of args + if code_has_star_arg(code): self.write('*%s' % code.co_varnames[argc + kw_pairs]) argc += 1 @@ -186,10 +200,12 @@ def make_function3_annotate(self, node, isLambda, nested=1, else: suffix = ', ' + # self.println(indent, '#flags:\t', int(code.co_flags)) - if kw_args > 0: - if not (4 & code.co_flags): + if kw_args + annotate_argc > 0: + if not code_has_star_arg(code): if argc > 0: + self.write(", *, ") else: self.write("*, ") @@ -211,10 +227,32 @@ def make_function3_annotate(self, node, isLambda, nested=1, self.write(', ') i += 1 pass + elif n == 'annotate_arg': + from trepan.api import debug; debug() + pass + pass + annotate_args = [] + for n in node: + if n == 'annotate_arg': + annotate_args.append(n[0].attr) + elif n == 'annotate_tuple': + t = n[0].attr + if t[-1] == 'return': + t = t[0:-1] + annotate_args = annotate_args[:-1] + pass + last = len(annotate_args) - 1 + for i in range(len(annotate_args)): + self.write("%s: %s" % (annotate_args[i], t[i])) + if i < last: + self.write(', ') + pass + pass + break pass pass - if 8 & code.co_flags: # flag 3 -> keyword args + if code_has_star_star_arg(code): if argc > 0: self.write(', ') self.write('**%s' % code.co_varnames[argc + kw_pairs]) @@ -297,6 +335,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None): else: defparams = node[:args_node.attr] kw_args = 0 + annotate_argc = 0 pass lambda_index = None @@ -335,7 +374,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None): name, default in zip_longest(paramnames, defparams, fillvalue=None)] params.reverse() # back to correct order - if 4 & code.co_flags: # flag 2 -> variable number of args + if code_has_star_arg(code): params.append('*%s' % code.co_varnames[argc]) argc += 1 @@ -363,7 +402,7 @@ def make_function2(self, node, isLambda, nested=1, codeNode=None): break pass - if 8 & code.co_flags: # flag 3 -> keyword args + if code_has_star_star_arg(code): if argc > 0: self.write(', ') self.write('**%s' % code.co_varnames[argc + kw_pairs]) @@ -472,7 +511,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None): name, default in zip_longest(paramnames, defparams, fillvalue=None)] params.reverse() # back to correct order - if 4 & code.co_flags: # flag 2 -> variable number of args + if code_has_star_arg(code): if self.version > 3.0: params.append('*%s' % code.co_varnames[argc + kw_pairs]) else: @@ -498,7 +537,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None): indent = ' ' * l line_number = self.line_number - if 4 & code.co_flags: # flag 2 -> variable number of args + if code_has_star_arg(code): self.write('*%s' % code.co_varnames[argc + kw_pairs]) argc += 1 @@ -552,7 +591,7 @@ def make_function3(self, node, isLambda, nested=1, codeNode=None): pass pass - if 8 & code.co_flags: # flag 3 -> keyword args + if code_has_star_star_arg(code): if argc > 0: self.write(', ') self.write('**%s' % code.co_varnames[argc + kw_pairs])