From 16914feb1217bbe0f0df64affd1a909a24e23ec6 Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 22 Dec 2022 22:47:14 -0500 Subject: [PATCH 01/22] Get ready for release 3.9.0 --- NEWS.md | 24 +++++++++++++++++++++++- admin-tools/check-3.3-3.5-versions.sh | 27 +++++++++++++++++++++++++++ admin-tools/pyenv-newest-versions | 2 +- pytest/Makefile | 2 +- uncompyle6/version.py | 2 +- 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 admin-tools/check-3.3-3.5-versions.sh diff --git a/NEWS.md b/NEWS.md index 5125ccf7..9f33e295 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,26 @@ -3.8.0: 2020-10-29 +3.9.0: 2022-12-22 +================= + +* deparse generator expressions for Python 3.0 .. 3.2 +* Python 3.0 list comprehension. +* Fix Issues #310, #344, #377, #391, #409, #414 +* Limited support for 3.8+ f-string "=" specifier +* Correct 2.5-7 relative import formatting +* Miscellaneous bug fixing +* remove \n in lambda +* Python 2.6 gramar cleanup +* Correct some Python 2.6 chain compare decompilation +* Ensure no parenthesis subscript slices +* Correct 2.x formatting "slice2" nonterminal +* Correct 3.7 imports +* Improve "async for" parsing +* Handle BUILD_MAP opcode +* match Python AT better +* Correct 3.7 positional args +* PyPy 3.7 and PyPy 3.8 support +* Miscellaneous linting, isorting, blacking + +3.8.0: 2021-10-29 ================= * Better handling of invalid bytecode magic diff --git a/admin-tools/check-3.3-3.5-versions.sh b/admin-tools/check-3.3-3.5-versions.sh new file mode 100644 index 00000000..5b451577 --- /dev/null +++ b/admin-tools/check-3.3-3.5-versions.sh @@ -0,0 +1,27 @@ +#!/bin/bash +function finish { + cd $owd +} +owd=$(pwd) +trap finish EXIT + +cd $(dirname ${BASH_SOURCE[0]}) +if ! source ./pyenv-3.3-3.5-versions ; then + exit $? +fi +if ! source ./setup-python-3.3.sh ; then + exit $? +fi + +cd .. +for version in $PYVERSIONS; do + echo --- $version --- + if ! pyenv local $version ; then + exit $? + fi + make clean && python setup.py develop + if ! make check ; then + exit $? + fi + echo === $version === +done diff --git a/admin-tools/pyenv-newest-versions b/admin-tools/pyenv-newest-versions index 4350d1bc..f5911863 100644 --- a/admin-tools/pyenv-newest-versions +++ b/admin-tools/pyenv-newest-versions @@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then echo "This script should be *sourced* rather than run directly through bash" exit 1 fi -export PYVERSIONS='3.6.15 pypy3.6-7.3.1 3.7.16 pypy-3.7-3.9 pypy3.8-7.3.10 pyston-2.3.5 3.8.14' +export PYVERSIONS='3.6.15 pypy3.6-7.3.1 3.7.16 pypy3.7-7.3.9 pypy3.8-7.3.10 pyston-2.3.5 3.8.16' diff --git a/pytest/Makefile b/pytest/Makefile index cb4c0caa..16ab89a6 100644 --- a/pytest/Makefile +++ b/pytest/Makefile @@ -7,5 +7,5 @@ PYTHON ?= python test check pytest: @PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \ if [[ $$PYTHON_VERSION > 3.2 ]] || [[ $$PYTHON_VERSION == 2.7 ]] || [[ $$PYTHON_VERSION == 2.6 ]]; then \ - py.test; \ + $(PYTHON) -m pytest .; \ fi diff --git a/uncompyle6/version.py b/uncompyle6/version.py index 44c88a7d..e6a3e3c7 100644 --- a/uncompyle6/version.py +++ b/uncompyle6/version.py @@ -14,4 +14,4 @@ # This file is suitable for sourcing inside POSIX shell as # well as importing into Python # fmt: off -__version__="3.9.0a1" # noqa +__version__="3.9.0" # noqa From 090570cd34fad588a116816cfaf609044390f0aa Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 02:20:59 -0500 Subject: [PATCH 02/22] 3.4-3.5 MAKE_CLOSURE with annotate Docs lie about annnotation args. Slight adjustment here. More is probably needed. --- uncompyle6/parser.py | 2 +- uncompyle6/scanners/scanner3.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index 016b22de..3ef8a5a0 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -199,7 +199,7 @@ class PythonParser(GenericASTBuilder): if instructions[finish].linestart: break pass - if start > 0: + if start >= 0: err_token = instructions[index] print("Instruction context:") for i in range(start, finish): diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 127f739e..d723a060 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -626,10 +626,19 @@ class Scanner3(Scanner): pos_args, name_pair_args, annotate_args = parse_fn_counts( inst.argval ) + + correct_annotate_args = annotate_args + if opname == "MAKE_CLOSURE" and ((3, 4) <= self.version < (3, 6)) and annotate_args > 0: + # For some reason that I don't understand annotate_args is off by one + # here from what is documented in + # https://docs.python.org/3.4/library/dis.html#opcode-MAKE_CLOSURE + # However in parsing rule, we have already adjusted for the one-fewer annotate arg + correct_annotate_args -= 1 + pattr = "%d positional, %d keyword only, %d annotated" % ( pos_args, name_pair_args, - annotate_args, + correct_annotate_args, ) if name_pair_args > 0: # FIXME: this should probably be K_ From 92830c2eaea8ebfb9ed3243fba3208689434efa5 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 02:22:53 -0500 Subject: [PATCH 03/22] Newer setuptools --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 40a86898..fba9e512 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,4 @@ pytest Click~=7.0 xdis>=6.0.4 configobj~=5.0.6 -setuptools~=65.3.0 +setuptools~=65.5.1 From 73a4c0be78f840c42a7b049a5800214adcdf5f70 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 02:49:43 -0500 Subject: [PATCH 04/22] Use 3.7.16 for master version --- admin-tools/setup-master.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin-tools/setup-master.sh b/admin-tools/setup-master.sh index f0d39adb..9e00863c 100755 --- a/admin-tools/setup-master.sh +++ b/admin-tools/setup-master.sh @@ -1,5 +1,5 @@ #!/bin/bash -PYTHON_VERSION=3.7.14 +PYTHON_VERSION=3.7.16 function checkout_version { local repo=$1 From 42d26ccbd75ede8ccede3129caa6240a7f356621 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 06:21:12 -0500 Subject: [PATCH 05/22] Bump version --- uncompyle6/scanners/scanner3.py | 6 +++--- uncompyle6/version.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index d723a060..0cde31a1 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -628,9 +628,9 @@ class Scanner3(Scanner): ) correct_annotate_args = annotate_args - if opname == "MAKE_CLOSURE" and ((3, 4) <= self.version < (3, 6)) and annotate_args > 0: - # For some reason that I don't understand annotate_args is off by one - # here from what is documented in + if opname in ("MAKE_CLOSURE", "MAKE_FUNCTION") and ((3, 4) <= self.version < (3, 6)) and annotate_args > 0: + # For some reason that I don't understand, annotate_args is off by one + # when there is an EXENDED_ARG instruction from what is documented in # https://docs.python.org/3.4/library/dis.html#opcode-MAKE_CLOSURE # However in parsing rule, we have already adjusted for the one-fewer annotate arg correct_annotate_args -= 1 diff --git a/uncompyle6/version.py b/uncompyle6/version.py index e6a3e3c7..03e22cc7 100644 --- a/uncompyle6/version.py +++ b/uncompyle6/version.py @@ -14,4 +14,4 @@ # This file is suitable for sourcing inside POSIX shell as # well as importing into Python # fmt: off -__version__="3.9.0" # noqa +__version__="3.9.1.dev0" # noqa From 26b60f6fb899f70f6ee7339c2ba6247d91ca98ed Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 09:42:16 -0500 Subject: [PATCH 06/22] Handle Python 3.4 MAKE_CLOSURE fns ... Is done just like Python 3.3 --- test/bytecode_3.3_run/02_make_closure.pyc | Bin 0 -> 850 bytes test/bytecode_3.4_run/02_make_closure.pyc | Bin 0 -> 580 bytes test/simple_source/bug34/02_make_closure.py | 18 ++++++++++++++++++ uncompyle6/parsers/parse3.py | 16 ++++++++-------- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 test/bytecode_3.3_run/02_make_closure.pyc create mode 100644 test/bytecode_3.4_run/02_make_closure.pyc create mode 100644 test/simple_source/bug34/02_make_closure.py diff --git a/test/bytecode_3.3_run/02_make_closure.pyc b/test/bytecode_3.3_run/02_make_closure.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0200d741f3315c22bb36499439d9c54743e07e0a GIT binary patch literal 850 zcmb7CO;5r=5S?uc0)b%S#iJ+Fg8_pcjWIFClU(4$WmC3`skRim9is_1j3%D^1^yE+ z{xWB_5UuHjh0f0G$9r#P_Osn-9ejPOcLAW_&v41VpwAqU2>~Vm*P(#WfftG$SUE5e zplQX0ux3MYJQ(?Px)9aKD8jUZXI^#aGXpY9fGbUybepUL+6CniMmo`%}C?09nqv8?vwwoKD~`v+n4qI zd5~z*-=gDECTWPoEU|6F2Mpr%zO7rH*>t998kg9MYzl}tRYAN-ry|X5GOXzF4hb{d x$X%!E32dr792c*I8y8d_R`j$QkfOq(_6!CpBw?U8rbO8=C0hxq*@+I6! z=n60tQIJK!m5{}7qH7;9m84|9t2SeLmB9;DNF+2Tye5 zmyePQbC4IVvgXvdQcJHgbLB-Z zj&*xChyOKTKEQHnkUX7zQEVI8w|&}L042ZHJx5{ zw!PEY+`=KvPRGLV?eUG}r)ZG5Isu3!(V*{k_1_iSKU3%$Pc%w~2GKVWhecLV7xXQ} z%PPrd8Rbgrqh3+up@D6Zb^t9bce9HO{6Y(4j!8=fKiuf_SS!n!5c1-BE-+ 0: rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE LOAD_STR %s" @@ -1196,7 +1196,7 @@ class Python3Parser(PythonParser): opname, ) - elif self.version >= (3, 4): + elif self.version >= (3, 5): if not self.is_pypy: load_op = "LOAD_STR" else: From 154dabfcef84b7ebcaed5f13beb084b075ba2276 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 09:42:16 -0500 Subject: [PATCH 07/22] Handle Python 3.4 MAKE_CLOSURE fns ... Is done just like Python 3.3 --- test/bytecode_3.3_run/02_make_closure.pyc | Bin 0 -> 1005 bytes test/bytecode_3.4_run/02_make_closure.pyc | Bin 0 -> 722 bytes test/simple_source/bug34/02_make_closure.py | 18 ++++++++++++++++ uncompyle6/parsers/parse3.py | 22 ++++++++++++-------- 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 test/bytecode_3.3_run/02_make_closure.pyc create mode 100644 test/bytecode_3.4_run/02_make_closure.pyc create mode 100644 test/simple_source/bug34/02_make_closure.py diff --git a/test/bytecode_3.3_run/02_make_closure.pyc b/test/bytecode_3.3_run/02_make_closure.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0828c6cb8020d4ff21ce11758064a83270af1072 GIT binary patch literal 1005 zcmb7CO>Yx15FPJswjXLK5<=pH$_4EKT2we91ga`d^wJzqF4o5DrrYessl9_h%84S7 zIQ19sSCIH+ym1oIpj_%*kH_Qj%zHEbwX?f@^ZSqasfY~S8Y%R*nBfYdA{sy{8Yp^S zB~+#K4f~X?6B;CBVace<<2ECk(Pd98=U`Gx}08Ir4 z^8Fw>hcPm_N$ERIdty8*GF;<5h!5r19yczqw|yJBLV~hXrLvz`G7XLFWXLMVvW906 z9nm$>r4p%TN^t0eX856`)MZft63HmBP75KzA_7mX=S^r@*OR)_dNpgzT^5py1*{Zg z)gwmatr)>HWe8dq*bqMz_qv@nRtLx4*ux=@UcNefaim+Y)n-ojVESbxzeEZGh}5py z!pD!G|5u-bCE7wdu(&@dTMPW#uxQ=7iji6KPTXE`*(}au-8vuX44rFMvd`JfLp08m z&63U3+J@pXZUwRhQ<^TWy literal 0 HcmV?d00001 diff --git a/test/bytecode_3.4_run/02_make_closure.pyc b/test/bytecode_3.4_run/02_make_closure.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cfb04469d177b07815a77b21234f70173b344fd GIT binary patch literal 722 zcmZ8dJ#W-N5S{Vbz7H;>KuA<5G)R`VApa@ZsCRdrI zH2_7h=8uVjHV%YKO9?#45}&wkrLFH1Rm-X+vXaxr3?B1L`B<8~5;ODzqN2{7Bl-%z z@4Kp5)ta2?Y@v5&-TcYk?xV-luy=kI%#T;^t&C#8$MqQiw{XDw?9P8Jj{myIQgO7* z8%^YgM2#ssu0no9blT^J`gmqc9SYbX=MIpem1_2efJ~^z#|arlAr2i)4zix_ z5tXLk#^p)4^Y|`Y` 0: rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE LOAD_STR %s" @@ -1195,8 +1197,10 @@ class Python3Parser(PythonParser): "pos_arg " * args_pos, opname, ) + self.add_unique_rule(rule, opname, token.attr, customize) - elif self.version >= (3, 4): + + if self.version >= (3, 4): if not self.is_pypy: load_op = "LOAD_STR" else: @@ -1221,7 +1225,7 @@ class Python3Parser(PythonParser): opname, ) - self.add_unique_rule(rule, opname, token.attr, customize) + self.add_unique_rule(rule, opname, token.attr, customize) if args_kw == 0: rule = "mkfunc ::= %sload_closure load_genexpr %s" % ( From 5102e5f6e01d33416efb856ef455882694c54b34 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 14 Jan 2023 10:02:01 -0500 Subject: [PATCH 08/22] Merge hell --- test/bytecode_3.3_run/02_make_closure.pyc | Bin 850 -> 1005 bytes test/bytecode_3.4_run/02_make_closure.pyc | Bin 580 -> 722 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/bytecode_3.3_run/02_make_closure.pyc b/test/bytecode_3.3_run/02_make_closure.pyc index 0200d741f3315c22bb36499439d9c54743e07e0a..0828c6cb8020d4ff21ce11758064a83270af1072 100644 GIT binary patch delta 292 zcmcb__LjYV9uF_qg#Cw-yBHZ5k{OTyGmz~7#Kna`B87n=3P>_CL@_eBr!X<3Ff+6O z`OFN>Obk&>45>g77KRiyFqefPm4zXT6(o|)#8B+aP{PL0#0VB-2dM!GM!*CefJzxb z27^R%p`t0Q48a=gKx<3ofJ8_}X0bv+QGR++Vy*&^S)7`arkk9Rnw*`Pm#(-myn;!L z1EdzDAwE7OKRG_W6zCa-#K{wxL?*vw;#VvM`6VT_tdtK(I~EtG7L{b?=eZUYHt>L<$2#6p&{W6h8JWcj1x5MkMTxlzKxT1jPMU6VMrv|)W?s7D#*nRyhU~Z4 z8Go-M9xl9ZxtPH`LY#VoOW#nTnVh3t0VgeGk7%L{vXOiP*0~rS**cgR5 G_?ZEwPZW0m From 8479e66adddd68cc39dc76020e2645eca10ef7c9 Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 16 Jan 2023 01:22:39 -0500 Subject: [PATCH 09/22] Annotate arg parsing --- uncompyle6/scanner.py | 25 ++++++++++++++++---- uncompyle6/scanners/scanner3.py | 41 ++++++++++++++++----------------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index 7955496d..da94d6b1 100644 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016, 2018-2022 by Rocky Bernstein +# Copyright (c) 2016, 2018-2023 by Rocky Bernstein # Copyright (c) 2005 by Dan Pascu # Copyright (c) 2000-2002 by hartmut Goebel # Copyright (c) 1999 John Aycock @@ -32,8 +32,8 @@ from xdis import ( Bytecode, canonic_python_version, code2num, - instruction_size, extended_arg_val, + instruction_size, next_offset, ) @@ -600,8 +600,25 @@ class Scanner(object): return self.Token -def parse_fn_counts(argc): - return ((argc & 0xFF), (argc >> 8) & 0xFF, (argc >> 16) & 0x7FFF) +# TODO: after the next xdis release, use from there instead. +def parse_fn_counts_30_35(argc): + """ + In Python 3.0 to 3.5 MAKE_CLOSURE and MAKE_FUNCTION encode + arguments counts of positional, default + named, and annotation + arguments a particular kind of encoding where each of + the entry a a packe byted value of the lower 24 bits + of ``argc``. The high bits of argc may have come from + an EXTENDED_ARG instruction. Here, we unpack the values + from the ``argc`` int and return a triple of the + positional args, named_args, and annotation args. + """ + annotate_count = (argc >> 16) & 0x7FFF + # For some reason that I don't understand, annotate_args is off by one + # when there is an EXENDED_ARG instruction from what is documented in + # https://docs.python.org/3.4/library/dis.html#opcode-MAKE_CLOSURE + if annotate_count > 1: + annotate_count -= 1 + return ((argc & 0xFF), (argc >> 8) & 0xFF, annotate_count) def get_scanner(version, is_pypy=False, show_asm=None): diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 8d3b91a0..1868ada9 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2019, 2021-2022 by Rocky Bernstein +# Copyright (c) 2015-2019, 2021-2023 by Rocky Bernstein # Copyright (c) 2005 by Dan Pascu # Copyright (c) 2000-2002 by hartmut Goebel # @@ -39,7 +39,7 @@ from xdis import iscode, instruction_size, Instruction from xdis.bytecode import _get_const_info from uncompyle6.scanners.tok import Token -from uncompyle6.scanner import parse_fn_counts +from uncompyle6.scanner import parse_fn_counts_30_35 import xdis # Get all the opcodes into globals @@ -621,32 +621,31 @@ class Scanner3(Scanner): flags >>= 1 attr = attr[:4] # remove last value: attr[5] == False else: - pos_args, name_pair_args, annotate_args = parse_fn_counts( + pos_args, name_pair_args, annotate_args = parse_fn_counts_30_35( inst.argval + ) + + pattr = "%s positional, %s keyword only, %s annotated" % ( + pos_args, name_pair_args, annotate_args ) - correct_annotate_args = annotate_args - if opname in ("MAKE_CLOSURE", "MAKE_FUNCTION") and ((3, 4) <= self.version < (3, 6)) and annotate_args > 0: - # For some reason that I don't understand, annotate_args is off by one - # when there is an EXENDED_ARG instruction from what is documented in - # https://docs.python.org/3.4/library/dis.html#opcode-MAKE_CLOSURE - # However in parsing rule, we have already adjusted for the one-fewer annotate arg - correct_annotate_args -= 1 - - pattr = "%d positional, %d keyword only, %d annotated" % ( - pos_args, - name_pair_args, - correct_annotate_args, - ) - if name_pair_args > 0: + if name_pair_args > 0 and annotate_args > 0: # FIXME: this should probably be K_ - opname = "%s_N%d" % (opname, name_pair_args) + opname += "_N%s_A%s" % (name_pair_args, annotate_args) pass - if annotate_args > 0: - opname = "%s_A_%d" % (opname, annotate_args) + elif annotate_args > 0: + opname += "_A_%s" % annotate_args pass - opname = "%s_%d" % (opname, pos_args) + elif name_pair_args > 0: + opname += "_N_%s" % name_pair_args + pass + else: + # Rule customization mathics, MAKE_FUNCTION_... + # so make sure to add the "_" + opname += "_0" + attr = (pos_args, name_pair_args, annotate_args) + new_tokens.append( Token( opname=opname, From 9a7eb0ad0a969147b8af1349e2deca204f6255a8 Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 15 Jan 2023 22:48:07 -0500 Subject: [PATCH 10/22] try to be more honest about MAKE_{FUNCTION,CLOSURE} --- uncompyle6/parser.py | 10 ++- uncompyle6/parsers/parse3.py | 148 ++++++++++++++++---------------- uncompyle6/parsers/parse32.py | 2 +- uncompyle6/scanner.py | 23 ++++- uncompyle6/scanners/scanner3.py | 41 ++++----- 5 files changed, 122 insertions(+), 102 deletions(-) diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index 3ef8a5a0..e694f944 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -213,10 +213,16 @@ class PythonParser(GenericASTBuilder): raise ParserError(None, -1, self.debug["reduce"]) def get_pos_kw(self, token): - """Return then the number of positional parameters and - represented by the attr field of token""" + """ + Return then the number of positional parameters and keyword + parfameters represented by the attr (operand) field of + token. + + This appears in CALL_FUNCTION or CALL_METHOD (PyPy) tokens + """ # Low byte indicates number of positional paramters, # high byte number of keyword parameters + assert token.kind.startswith("CALL_FUNCTION") or token.kind.startswith("CALL_METHOD") args_pos = token.attr & 0xFF args_kw = (token.attr >> 8) & 0xFF return args_pos, args_kw diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index bb26495c..e71d3100 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -541,9 +541,9 @@ class Python3Parser(PythonParser): # token found, while this one doesn't. if self.version < (3, 6): call_function = self.call_fn_name(call_fn_tok) - args_pos, args_kw = self.get_pos_kw(call_fn_tok) + pos_args_count, kw_args_count = self.get_pos_kw(call_fn_tok) rule = "build_class ::= LOAD_BUILD_CLASS mkfunc %s" "%s" % ( - ("expr " * (args_pos - 1) + ("kwarg " * args_kw)), + ("expr " * (pos_args_count - 1) + ("kwarg " * kw_args_count)), call_function, ) else: @@ -552,10 +552,10 @@ class Python3Parser(PythonParser): if call_function.startswith("CALL_FUNCTION_KW"): self.addRule("classdef ::= build_class_kw store", nop_func) if is_pypy: - args_pos, args_kw = self.get_pos_kw(call_fn_tok) + pos_args_count, kw_args_count = self.get_pos_kw(call_fn_tok) rule = "build_class_kw ::= LOAD_BUILD_CLASS mkfunc %s%s%s" % ( - "expr " * (args_pos - 1), - "kwarg " * (args_kw), + "expr " * (pos_args_count - 1), + "kwarg " * (kw_args_count), call_function, ) else: @@ -581,7 +581,7 @@ class Python3Parser(PythonParser): classdefdeco2 ::= LOAD_BUILD_CLASS mkfunc {expr}^n-1 CALL_FUNCTION_n """ - args_pos, args_kw = self.get_pos_kw(token) + pos_args_count, kw_args_count = self.get_pos_kw(token) # Additional exprs for * and ** args: # 0 if neither @@ -590,7 +590,7 @@ class Python3Parser(PythonParser): # Yes, this computation based on instruction name is a little bit hoaky. nak = (len(opname) - len("CALL_FUNCTION")) // 3 - uniq_param = args_kw + args_pos + uniq_param = kw_args_count + pos_args_count # Note: 3.5+ have subclassed this method; so we don't handle # 'CALL_FUNCTION_VAR' or 'CALL_FUNCTION_EX' here. @@ -599,16 +599,16 @@ class Python3Parser(PythonParser): token.kind = self.call_fn_name(token) rule = ( "call ::= expr " - + ("pos_arg " * args_pos) - + ("kwarg " * args_kw) + + ("pos_arg " * pos_args_count) + + ("kwarg " * kw_args_count) + token.kind ) else: token.kind = self.call_fn_name(token) rule = ( "call ::= expr " - + ("pos_arg " * args_pos) - + ("kwarg " * args_kw) + + ("pos_arg " * pos_args_count) + + ("kwarg " * kw_args_count) + "expr " * nak + token.kind ) @@ -616,11 +616,11 @@ class Python3Parser(PythonParser): self.add_unique_rule(rule, token.kind, uniq_param, customize) if "LOAD_BUILD_CLASS" in self.seen_ops: - if next_token == "CALL_FUNCTION" and next_token.attr == 1 and args_pos > 1: + if next_token == "CALL_FUNCTION" and next_token.attr == 1 and pos_args_count > 1: rule = "classdefdeco2 ::= LOAD_BUILD_CLASS mkfunc %s%s_%d" % ( - ("expr " * (args_pos - 1)), + ("expr " * (pos_args_count - 1)), opname, - args_pos, + pos_args_count, ) self.add_unique_rule(rule, token.kind, uniq_param, customize) @@ -955,14 +955,14 @@ class Python3Parser(PythonParser): elif opname_base == "CALL_METHOD": # PyPy and Python 3.7+ only - DRY with parse2 - args_pos, args_kw = self.get_pos_kw(token) + pos_args_count, kw_args_count = self.get_pos_kw(token) # number of apply equiv arguments: nak = (len(opname_base) - len("CALL_METHOD")) // 3 rule = ( "call ::= expr " - + ("pos_arg " * args_pos) - + ("kwarg " * args_kw) + + ("pos_arg " * pos_args_count) + + ("kwarg " * kw_args_count) + "expr " * nak + opname ) @@ -1096,7 +1096,7 @@ class Python3Parser(PythonParser): """ self.addRule(rule, nop_func) - args_pos, args_kw, annotate_args = token.attr + pos_args_count, kw_args_count, annotate_args = token.attr # FIXME: Fold test into add_make_function_rule if self.version < (3, 3): @@ -1105,7 +1105,7 @@ class Python3Parser(PythonParser): j = 2 if self.is_pypy or (i >= j and tokens[i - j] == "LOAD_LAMBDA"): rule_pat = "lambda_body ::= %sload_closure LOAD_LAMBDA %%s%s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, opname, ) self.add_make_function_rule(rule_pat, opname, token.attr, customize) @@ -1113,7 +1113,7 @@ class Python3Parser(PythonParser): if has_get_iter_call_function1: rule_pat = ( "generator_exp ::= %sload_closure load_genexpr %%s%s expr " - "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * args_pos, opname) + "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * pos_args_count, opname) ) self.add_make_function_rule(rule_pat, opname, token.attr, customize) @@ -1129,7 +1129,7 @@ class Python3Parser(PythonParser): rule_pat = ( "listcomp ::= %sload_closure LOAD_LISTCOMP %%s%s expr " "GET_ITER CALL_FUNCTION_1" - % ("pos_arg " * args_pos, opname) + % ("pos_arg " * pos_args_count, opname) ) self.add_make_function_rule( rule_pat, opname, token.attr, customize @@ -1138,7 +1138,7 @@ class Python3Parser(PythonParser): rule_pat = ( "set_comp ::= %sload_closure LOAD_SETCOMP %%s%s expr " "GET_ITER CALL_FUNCTION_1" - % ("pos_arg " * args_pos, opname) + % ("pos_arg " * pos_args_count, opname) ) self.add_make_function_rule( rule_pat, opname, token.attr, customize @@ -1149,13 +1149,13 @@ class Python3Parser(PythonParser): self.add_unique_rule( "dict_comp ::= %sload_closure LOAD_DICTCOMP %s " "expr GET_ITER CALL_FUNCTION_1" - % ("pos_arg " * args_pos, opname), + % ("pos_arg " * pos_args_count, opname), opname, token.attr, customize, ) - if args_kw > 0: + if kw_args_count > 0: kwargs_str = "kwargs " else: kwargs_str = "" @@ -1167,34 +1167,34 @@ class Python3Parser(PythonParser): "mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE %s" % ( kwargs_str, - "pos_arg " * args_pos, - "annotate_arg " * (annotate_args - 1), + "pos_arg " * pos_args_count, + "annotate_arg " * (annotate_args), opname, ) ) else: rule = "mkfunc ::= %s%sload_closure LOAD_CODE %s" % ( kwargs_str, - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, opname, ) self.add_unique_rule(rule, opname, token.attr, customize) - elif (3, 3) <= self.version < (3, 5): + elif (3, 3) <= self.version < (3, 6): if annotate_args > 0: rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE LOAD_STR %s" % ( kwargs_str, - "pos_arg " * args_pos, - "annotate_arg " * (annotate_args - 1), + "pos_arg " * pos_args_count, + "annotate_arg " * (annotate_args), opname, ) ) else: rule = "mkfunc ::= %s%sload_closure LOAD_CODE LOAD_STR %s" % ( kwargs_str, - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, opname, ) self.add_unique_rule(rule, opname, token.attr, customize) @@ -1210,16 +1210,16 @@ class Python3Parser(PythonParser): rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple load_closure %s %s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, kwargs_str, - "annotate_arg " * (annotate_args - 1), + "annotate_arg " * (annotate_args), load_op, opname, ) ) else: rule = "mkfunc ::= %s%s load_closure LOAD_CODE %s %s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, kwargs_str, load_op, opname, @@ -1227,16 +1227,16 @@ class Python3Parser(PythonParser): self.add_unique_rule(rule, opname, token.attr, customize) - if args_kw == 0: + if kw_args_count == 0: rule = "mkfunc ::= %sload_closure load_genexpr %s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, opname, ) self.add_unique_rule(rule, opname, token.attr, customize) if self.version < (3, 4): rule = "mkfunc ::= %sload_closure LOAD_CODE %s" % ( - "expr " * args_pos, + "expr " * pos_args_count, opname, ) self.add_unique_rule(rule, opname, token.attr, customize) @@ -1247,10 +1247,10 @@ class Python3Parser(PythonParser): if self.version >= (3, 6): # The semantics of MAKE_FUNCTION in 3.6 are totally different from # before. - args_pos, args_kw, annotate_args, closure = token.attr - stack_count = args_pos + args_kw + annotate_args + pos_args_count, kw_args_count, annotate_args, closure = token.attr + stack_count = pos_args_count + kw_args_count + annotate_args if closure: - if args_pos: + if pos_args_count: rule = "lambda_body ::= %s%s%s%s" % ( "expr " * stack_count, "load_closure " * closure, @@ -1283,14 +1283,14 @@ class Python3Parser(PythonParser): if has_get_iter_call_function1: rule_pat = ( "generator_exp ::= %sload_genexpr %%s%s expr " - "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * args_pos, opname) + "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * pos_args_count, opname) ) self.add_make_function_rule( rule_pat, opname, token.attr, customize ) rule_pat = ( "generator_exp ::= %sload_closure load_genexpr %%s%s expr " - "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * args_pos, opname) + "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * pos_args_count, opname) ) self.add_make_function_rule( rule_pat, opname, token.attr, customize @@ -1312,7 +1312,7 @@ class Python3Parser(PythonParser): rule_pat = ( "listcomp ::= %sLOAD_LISTCOMP %%s%s expr " "GET_ITER CALL_FUNCTION_1" - % ("expr " * args_pos, opname) + % ("expr " * pos_args_count, opname) ) self.add_make_function_rule( rule_pat, opname, token.attr, customize @@ -1320,8 +1320,8 @@ class Python3Parser(PythonParser): if self.is_pypy or (i >= 2 and tokens[i - 2] == "LOAD_LAMBDA"): rule_pat = "lambda_body ::= %s%sLOAD_LAMBDA %%s%s" % ( - ("pos_arg " * args_pos), - ("kwarg " * args_kw), + ("pos_arg " * pos_args_count), + ("kwarg " * kw_args_count), opname, ) self.add_make_function_rule( @@ -1330,9 +1330,9 @@ class Python3Parser(PythonParser): continue if self.version < (3, 6): - args_pos, args_kw, annotate_args = token.attr + pos_args_count, kw_args_count, annotate_args = token.attr else: - args_pos, args_kw, annotate_args, closure = token.attr + pos_args_count, kw_args_count, annotate_args, closure = token.attr if self.version < (3, 3): j = 1 @@ -1342,7 +1342,7 @@ class Python3Parser(PythonParser): if has_get_iter_call_function1: rule_pat = ( "generator_exp ::= %sload_genexpr %%s%s expr " - "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * args_pos, opname) + "GET_ITER CALL_FUNCTION_1" % ("pos_arg " * pos_args_count, opname) ) self.add_make_function_rule(rule_pat, opname, token.attr, customize) @@ -1354,7 +1354,7 @@ class Python3Parser(PythonParser): # Todo: For Pypy we need to modify this slightly rule_pat = ( "listcomp ::= %sLOAD_LISTCOMP %%s%s expr " - "GET_ITER CALL_FUNCTION_1" % ("expr " * args_pos, opname) + "GET_ITER CALL_FUNCTION_1" % ("expr " * pos_args_count, opname) ) self.add_make_function_rule( rule_pat, opname, token.attr, customize @@ -1363,13 +1363,13 @@ class Python3Parser(PythonParser): # FIXME: Fold test into add_make_function_rule if self.is_pypy or (i >= j and tokens[i - j] == "LOAD_LAMBDA"): rule_pat = "lambda_body ::= %s%sLOAD_LAMBDA %%s%s" % ( - ("pos_arg " * args_pos), - ("kwarg " * args_kw), + ("pos_arg " * pos_args_count), + ("kwarg " * kw_args_count), opname, ) self.add_make_function_rule(rule_pat, opname, token.attr, customize) - if args_kw == 0: + if kw_args_count == 0: kwargs = "no_kwargs" self.add_unique_rule("no_kwargs ::=", opname, token.attr, customize) else: @@ -1379,13 +1379,13 @@ class Python3Parser(PythonParser): # positional args after keyword args rule = "mkfunc ::= %s %s%s%s" % ( kwargs, - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, "LOAD_CODE ", opname, ) self.add_unique_rule(rule, opname, token.attr, customize) rule = "mkfunc ::= %s%s%s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, "LOAD_CODE ", opname, ) @@ -1393,14 +1393,14 @@ class Python3Parser(PythonParser): # positional args after keyword args rule = "mkfunc ::= %s %s%s%s" % ( kwargs, - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, "LOAD_CODE LOAD_STR ", opname, ) elif self.version >= (3, 6): # positional args before keyword args rule = "mkfunc ::= %s%s %s%s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, kwargs, "LOAD_CODE LOAD_STR ", opname, @@ -1408,7 +1408,7 @@ class Python3Parser(PythonParser): elif self.version >= (3, 4): # positional args before keyword args rule = "mkfunc ::= %s%s %s%s" % ( - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, kwargs, "LOAD_CODE LOAD_STR ", opname, @@ -1416,7 +1416,7 @@ class Python3Parser(PythonParser): else: rule = "mkfunc ::= %s%sexpr %s" % ( kwargs, - "pos_arg " * args_pos, + "pos_arg " * pos_args_count, opname, ) self.add_unique_rule(rule, opname, token.attr, customize) @@ -1426,8 +1426,8 @@ class Python3Parser(PythonParser): rule = ( "mkfunc_annotate ::= %s%sannotate_tuple LOAD_CODE LOAD_STR %s" % ( - ("pos_arg " * (args_pos)), - ("call " * (annotate_args - 1)), + ("pos_arg " * pos_args_count), + ("call " * annotate_args), opname, ) ) @@ -1435,8 +1435,8 @@ class Python3Parser(PythonParser): rule = ( "mkfunc_annotate ::= %s%sannotate_tuple LOAD_CODE LOAD_STR %s" % ( - ("pos_arg " * (args_pos)), - ("annotate_arg " * (annotate_args - 1)), + ("pos_arg " * pos_args_count), + ("annotate_arg " * annotate_args), opname, ) ) @@ -1447,21 +1447,21 @@ class Python3Parser(PythonParser): if self.version == (3, 3): # 3.3 puts kwargs before pos_arg pos_kw_tuple = ( - ("kwargs " * args_kw), - ("pos_arg " * (args_pos)), + ("kwargs " * kw_args_count), + ("pos_arg " * pos_args_count), ) else: # 3.4 and 3.5puts pos_arg before kwargs pos_kw_tuple = ( - "pos_arg " * (args_pos), - ("kwargs " * args_kw), + "pos_arg " * (pos_args_count), + ("kwargs " * kw_args_count), ) rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple LOAD_CODE LOAD_STR EXTENDED_ARG %s" % ( pos_kw_tuple[0], pos_kw_tuple[1], - ("call " * (annotate_args - 1)), + ("call " * annotate_args), opname, ) ) @@ -1471,7 +1471,7 @@ class Python3Parser(PythonParser): % ( pos_kw_tuple[0], pos_kw_tuple[1], - ("annotate_arg " * (annotate_args - 1)), + ("annotate_arg " * annotate_args), opname, ) ) @@ -1480,9 +1480,9 @@ class Python3Parser(PythonParser): rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple LOAD_CODE EXTENDED_ARG %s" % ( - ("kwargs " * args_kw), - ("pos_arg " * (args_pos)), - ("annotate_arg " * (annotate_args - 1)), + ("kwargs " * kw_args_count), + ("pos_arg " * (pos_args_count)), + ("annotate_arg " * annotate_args), opname, ) ) @@ -1490,9 +1490,9 @@ class Python3Parser(PythonParser): rule = ( "mkfunc_annotate ::= %s%s%sannotate_tuple LOAD_CODE EXTENDED_ARG %s" % ( - ("kwargs " * args_kw), - ("pos_arg " * (args_pos)), - ("call " * (annotate_args - 1)), + ("kwargs " * kw_args_count), + ("pos_arg " * pos_args_count), + ("call " * annotate_args), opname, ) ) diff --git a/uncompyle6/parsers/parse32.py b/uncompyle6/parsers/parse32.py index 0c3d4d6d..f0706798 100644 --- a/uncompyle6/parsers/parse32.py +++ b/uncompyle6/parsers/parse32.py @@ -92,7 +92,7 @@ class Python32Parser(Python3Parser): "LOAD_CONST LOAD_CODE EXTENDED_ARG %s" ) % ( ("pos_arg " * args_pos), - ("annotate_arg " * (annotate_args - 1)), + ("annotate_arg " * (annotate_args)), opname, ) self.add_unique_rule(rule, opname, token.attr, customize) diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index b8e621e7..18e885e4 100644 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -21,7 +21,7 @@ scanner/ingestion module. From here we call various version-specific scanners, e.g. for Python 2.7 or 3.4. """ -from typing import Optional +from typing import Optional, Tuple from array import array from collections import namedtuple @@ -600,8 +600,25 @@ class Scanner(object): return self.Token -def parse_fn_counts(argc): - return ((argc & 0xFF), (argc >> 8) & 0xFF, (argc >> 16) & 0x7FFF) +# TODO: after the next xdis release, use from there instead. +def parse_fn_counts_30_35(argc: int) -> Tuple[int, int, int]: + """ + In Python 3.0 to 3.5 MAKE_CLOSURE and MAKE_FUNCTION encode + arguments counts of positional, default + named, and annotation + arguments a particular kind of encoding where each of + the entry a a packe byted value of the lower 24 bits + of ``argc``. The high bits of argc may have come from + an EXTENDED_ARG instruction. Here, we unpack the values + from the ``argc`` int and return a triple of the + positional args, named_args, and annotation args. + """ + annotate_count = (argc >> 16) & 0x7FFF + # For some reason that I don't understand, annotate_args is off by one + # when there is an EXENDED_ARG instruction from what is documented in + # https://docs.python.org/3.4/library/dis.html#opcode-MAKE_CLOSURE + if annotate_count > 1: + annotate_count -= 1 + return ((argc & 0xFF), (argc >> 8) & 0xFF, annotate_count) def get_scanner(version, is_pypy=False, show_asm=None): diff --git a/uncompyle6/scanners/scanner3.py b/uncompyle6/scanners/scanner3.py index 0cde31a1..1cf060e0 100644 --- a/uncompyle6/scanners/scanner3.py +++ b/uncompyle6/scanners/scanner3.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2019, 2021-2022 by Rocky Bernstein +# Copyright (c) 2015-2019, 2021-2023 by Rocky Bernstein # Copyright (c) 2005 by Dan Pascu # Copyright (c) 2000-2002 by hartmut Goebel # @@ -41,7 +41,7 @@ from xdis import iscode, instruction_size, Instruction from xdis.bytecode import _get_const_info from uncompyle6.scanners.tok import Token -from uncompyle6.scanner import parse_fn_counts +from uncompyle6.scanner import parse_fn_counts_30_35 import xdis # Get all the opcodes into globals @@ -363,7 +363,7 @@ class Scanner3(Scanner): ) new_tokens.append( Token( - opname=f"BUILD_DICT_OLDER", + opname="BUILD_DICT_OLDER", attr=t.attr, pattr=t.pattr, offset=t.offset, @@ -623,32 +623,29 @@ class Scanner3(Scanner): flags >>= 1 attr = attr[:4] # remove last value: attr[5] == False else: - pos_args, name_pair_args, annotate_args = parse_fn_counts( + pos_args, name_pair_args, annotate_args = parse_fn_counts_30_35( inst.argval - ) + ) - correct_annotate_args = annotate_args - if opname in ("MAKE_CLOSURE", "MAKE_FUNCTION") and ((3, 4) <= self.version < (3, 6)) and annotate_args > 0: - # For some reason that I don't understand, annotate_args is off by one - # when there is an EXENDED_ARG instruction from what is documented in - # https://docs.python.org/3.4/library/dis.html#opcode-MAKE_CLOSURE - # However in parsing rule, we have already adjusted for the one-fewer annotate arg - correct_annotate_args -= 1 + pattr = f"{pos_args} positional, {name_pair_args} keyword only, {annotate_args} annotated" - pattr = "%d positional, %d keyword only, %d annotated" % ( - pos_args, - name_pair_args, - correct_annotate_args, - ) - if name_pair_args > 0: + if name_pair_args > 0 and annotate_args > 0: # FIXME: this should probably be K_ - opname = "%s_N%d" % (opname, name_pair_args) + opname += f"_N{name_pair_args}_A{annotate_args}" pass - if annotate_args > 0: - opname = "%s_A_%d" % (opname, annotate_args) + elif annotate_args > 0: + opname += f"_A_{annotate_args}" pass - opname = "%s_%d" % (opname, pos_args) + elif name_pair_args > 0: + opname += f"_N_{name_pair_args}" + pass + else: + # Rule customization mathics, MAKE_FUNCTION_... + # so make sure to add the "_" + opname += "_0" + attr = (pos_args, name_pair_args, annotate_args) + new_tokens.append( Token( opname=opname, From 50697bb79ecbc710663af2714cb13b13aaefd198 Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 16 Jan 2023 03:40:55 -0500 Subject: [PATCH 11/22] Improve set comprehension for Python 3.0 --- uncompyle6/parsers/parse30.py | 4 ++-- uncompyle6/semantics/gencomp.py | 2 +- uncompyle6/semantics/n_actions.py | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/uncompyle6/parsers/parse30.py b/uncompyle6/parsers/parse30.py index d4896259..9537d1ba 100644 --- a/uncompyle6/parsers/parse30.py +++ b/uncompyle6/parsers/parse30.py @@ -74,8 +74,8 @@ class Python30Parser(Python31Parser): # Need to keep LOAD_FAST as index 1 set_comp_header ::= BUILD_SET_0 DUP_TOP STORE_FAST set_comp_func ::= set_comp_header - LOAD_FAST FOR_ITER store comp_iter - JUMP_BACK POP_TOP JUMP_BACK RETURN_VALUE RETURN_LAST + LOAD_ARG FOR_ITER store comp_iter + JUMP_BACK COME_FROM POP_TOP JUMP_BACK RETURN_VALUE RETURN_LAST list_comp_header ::= BUILD_LIST_0 DUP_TOP STORE_FAST list_comp ::= list_comp_header diff --git a/uncompyle6/semantics/gencomp.py b/uncompyle6/semantics/gencomp.py index 1115d316..78d8677d 100644 --- a/uncompyle6/semantics/gencomp.py +++ b/uncompyle6/semantics/gencomp.py @@ -465,7 +465,7 @@ class ComprehensionMixin: self.write(": ") self.preorder(n[1]) else: - if self.version == (3, 0): + if self.version == (3, 0) and len(n) > 1: body = n[1] else: body = n[0] diff --git a/uncompyle6/semantics/n_actions.py b/uncompyle6/semantics/n_actions.py index aed3d366..fbbc326b 100644 --- a/uncompyle6/semantics/n_actions.py +++ b/uncompyle6/semantics/n_actions.py @@ -1093,7 +1093,12 @@ class NonterminalActions: self.write("{") if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]: if self.version == (3, 0): - iter_index = 6 + if len(node) >= 6: + iter_index = 6 + else: + assert node[1].kind.startswith("MAKE_FUNCTION") + iter_index = 2 + pass else: iter_index = 1 self.comprehension_walk_newer(node, iter_index=iter_index, code_index=0) From 2b3cd696db0fb9d7e2540c78c284ee39322bd0bb Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 16 Jan 2023 03:50:50 -0500 Subject: [PATCH 12/22] 3.0 set comprehension bug --- uncompyle6/semantics/gencomp.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/uncompyle6/semantics/gencomp.py b/uncompyle6/semantics/gencomp.py index 78d8677d..c1018c90 100644 --- a/uncompyle6/semantics/gencomp.py +++ b/uncompyle6/semantics/gencomp.py @@ -465,8 +465,13 @@ class ComprehensionMixin: self.write(": ") self.preorder(n[1]) else: - if self.version == (3, 0) and len(n) > 1: - body = n[1] + if self.version == (3, 0): + if isinstance(n, Token): + body = store + elif len(n) > 1: + body = n[1] + else: + body = n[0] else: body = n[0] self.preorder(body) From ee7fda286966f40000d494c05a7742d076b0a7a0 Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 16 Jan 2023 08:56:34 -0500 Subject: [PATCH 13/22] Remove a CircleCI test for 3.0-3.2... until we can find an image that might run this --- .circleci/config.yml | 76 -------------------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 8e02f975..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,76 +0,0 @@ -version: 2 -filters: - branches: - only: python-3.3-to-3.5 -jobs: - build: - working_directory: ~/rocky/python-uncompyle6 - parallelism: 1 - shell: /bin/bash --login - # CircleCI 2.0 does not support environment variables that refer to each other the same way as 1.0 did. - # If any of these refer to each other, rewrite them so that they don't or see https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables . - environment: - CIRCLE_ARTIFACTS: /tmp/circleci-artifacts - CIRCLE_TEST_REPORTS: /tmp/circleci-test-results - COMPILE: --compile - # To see the list of pre-built images that CircleCI provides for most common languages see - # https://circleci.com/docs/2.0/circleci-images/ - docker: - - image: circleci/python:3.5 - steps: - # Machine Setup - # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each - # The following `checkout` command checks out your code to your working directory. In 1.0 we did this implicitly. In 2.0 you can choose where in the course of a job your code should be checked out. - - checkout - # Prepare for artifact and test results collection equivalent to how it was done on 1.0. - # In many cases you can simplify this from what is generated here. - # 'See docs on artifact collection here https://circleci.com/docs/2.0/artifacts/' - - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS - # This is based on your 1.0 configuration file or project settings - - run: - working_directory: ~/rocky/python-uncompyle6 - command: pip install --user virtualenv && pip install --user nose && pip install --user pep8 - # Dependencies - # This would typically go in either a build or a build-and-test job when using workflows - # Restore the dependency cache - - restore_cache: - keys: - - v2-dependencies-{{ .Branch }}- - # fallback to using the latest cache if no exact match is found - - v2-dependencies- - - - run: - command: | # Use pip to install dependengcies - pip install --user --upgrade setuptools - # Until the next release - pip install git+https://github.com/rocky/python-xdis@python-3.3-to-3.5#egg=xdis - pip install --user -e . - pip install --user -r requirements-dev.txt - - # Save dependency cache - - save_cache: - key: v2-dependencies-{{ .Branch }}-{{ epoch }} - paths: - # This is a broad list of cache paths to include many possible development environments - # You can probably delete some of these entries - - vendor/bundle - - ~/virtualenvs - - ~/.m2 - - ~/.ivy2 - - ~/.bundle - - ~/.cache/bower - - # Test - # This would typically be a build job when using workflows, possibly combined with build - # This is based on your 1.0 configuration file or project settings - - run: sudo python ./setup.py develop && make check - # Teardown - # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each - # Save test results - - store_test_results: - path: /tmp/circleci-test-results - # Save artifacts - - store_artifacts: - path: /tmp/circleci-artifacts - - store_artifacts: - path: /tmp/circleci-test-results From 931abc57268b158d24e5d4b1a281311e0ce91d97 Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 18 Jan 2023 17:08:39 -0500 Subject: [PATCH 14/22] self.opc.version -> self.opc.version_tuple The next release of xdis will no longer support self.opc.version (a float value which doesn't work in the presense of 3.10 and above) --- uncompyle6/scanners/tok.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uncompyle6/scanners/tok.py b/uncompyle6/scanners/tok.py index def45346..58147cde 100644 --- a/uncompyle6/scanners/tok.py +++ b/uncompyle6/scanners/tok.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2021 by Rocky Bernstein +# Copyright (c) 2016-2021, 2023 by Rocky Bernstein # Copyright (c) 2000-2002 by hartmut Goebel # Copyright (c) 1999 John Aycock # @@ -180,7 +180,7 @@ class Token: elif name == "LOAD_ASSERT": return "%s%s %s" % (prefix, offset_opname, pattr) elif self.op in self.opc.NAME_OPS: - if self.opc.version >= 3.0: + if self.opc.version_tuple >= (3, 0): return "%s%s%s %s" % (prefix, offset_opname, argstr, self.attr) elif name == "EXTENDED_ARG": return "%s%s%s 0x%x << %s = %s" % ( From 7c9956464095d990461d9797c0b8b585a26eecdf Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 18 Jan 2023 21:01:11 -0500 Subject: [PATCH 15/22] Reinstate pos_args in CALL_METHOD --- uncompyle6/parsers/parse37base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uncompyle6/parsers/parse37base.py b/uncompyle6/parsers/parse37base.py index ef307cc5..3456934e 100644 --- a/uncompyle6/parsers/parse37base.py +++ b/uncompyle6/parsers/parse37base.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017, 2019-2020, 2022 Rocky Bernstein +# Copyright (c) 2016-2017, 2019-2020, 2022-2023 Rocky Bernstein """ Python 3.7 base code. We keep non-custom-generated grammar rules out of this file. """ @@ -558,7 +558,7 @@ class Python37BaseParser(PythonParser): nak = (len(opname_base) - len("CALL_METHOD")) // 3 rule = ( "call ::= expr " - + ("expr " * args_pos) + + ("pos_arg " * args_pos) + ("kwarg " * args_kw) + "expr " * nak + opname From e03f4cfe430bdc302fa6c1743bde3a148d66edce Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 18 Jan 2023 22:23:06 -0500 Subject: [PATCH 16/22] Try adding no blank templates --- .github/ISSUE_TEMPLATE/config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..4abc80f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: False From 41d1ba31f332aa15f189c7327d0b37fcb4473f8a Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 19 Jan 2023 04:31:19 -0500 Subject: [PATCH 17/22] Synch with decompyle3 code a little bit --- uncompyle6/parsers/parse37base.py | 49 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/uncompyle6/parsers/parse37base.py b/uncompyle6/parsers/parse37base.py index 3456934e..39940afe 100644 --- a/uncompyle6/parsers/parse37base.py +++ b/uncompyle6/parsers/parse37base.py @@ -431,35 +431,39 @@ class Python37BaseParser(PythonParser): "BUILD_TUPLE", "BUILD_TUPLE_UNPACK", ): - v = token.attr + collection_size = token.attr is_LOAD_CLOSURE = False if opname_base == "BUILD_TUPLE": # If is part of a "load_closure", then it is not part of a # "list". is_LOAD_CLOSURE = True - for j in range(v): + for j in range(collection_size): if tokens[i - j - 1].kind != "LOAD_CLOSURE": is_LOAD_CLOSURE = False break if is_LOAD_CLOSURE: - rule = "load_closure ::= %s%s" % (("LOAD_CLOSURE " * v), opname) + rule = "load_closure ::= %s%s" % ( + ("LOAD_CLOSURE " * collection_size), + opname, + ) self.add_unique_rule(rule, opname, token.attr, customize) - if not is_LOAD_CLOSURE or v == 0: + if not is_LOAD_CLOSURE or collection_size == 0: # We do this complicated test to speed up parsing of # pathelogically long literals, especially those over 1024. - build_count = token.attr - thousands = build_count // 1024 - thirty32s = (build_count // 32) % 32 + thousands = collection_size // 1024 + thirty32s = (collection_size // 32) % 32 if thirty32s > 0: rule = "expr32 ::=%s" % (" expr" * 32) - self.add_unique_rule(rule, opname_base, build_count, customize) + self.add_unique_rule( + rule, opname_base, collection_size, customize + ) pass if thousands > 0: self.add_unique_rule( "expr1024 ::=%s" % (" expr32" * 32), opname_base, - build_count, + collection_size, customize, ) pass @@ -468,7 +472,7 @@ class Python37BaseParser(PythonParser): ("%s ::= " % collection) + "expr1024 " * thousands + "expr32 " * thirty32s - + "expr " * (build_count % 32) + + "expr " * (collection_size % 32) + opname ) self.add_unique_rules(["expr ::= %s" % collection, rule], customize) @@ -478,8 +482,8 @@ class Python37BaseParser(PythonParser): if token.attr == 2: self.add_unique_rules( [ - "expr ::= build_slice2", - "build_slice2 ::= expr expr BUILD_SLICE_2", + "expr ::= slice2", + "slice2 ::= expr expr BUILD_SLICE_2", ], customize, ) @@ -489,8 +493,8 @@ class Python37BaseParser(PythonParser): ) self.add_unique_rules( [ - "expr ::= build_slice3", - "build_slice3 ::= expr expr expr BUILD_SLICE_3", + "expr ::= slice3", + "slice3 ::= expr expr expr BUILD_SLICE_3", ], customize, ) @@ -524,6 +528,7 @@ class Python37BaseParser(PythonParser): if opname == "CALL_FUNCTION" and token.attr == 1: rule = """ + expr ::= dict_comp dict_comp ::= LOAD_DICTCOMP LOAD_STR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1 classdefdeco1 ::= expr classdefdeco2 CALL_FUNCTION_1 @@ -563,6 +568,7 @@ class Python37BaseParser(PythonParser): + "expr " * nak + opname ) + self.add_unique_rule(rule, opname, token.attr, customize) elif opname == "CONTINUE": @@ -1252,20 +1258,13 @@ class Python37BaseParser(PythonParser): try: if fn: return fn(self, lhs, n, rule, ast, tokens, first, last) - except: + except Exception: import sys, traceback print( - ("Exception in %s %s\n" - + "rule: %s\n" - + "offsets %s .. %s") - % ( - fn.__name__, - sys.exc_info()[1], - rule2str(rule), - tokens[first].offset, - tokens[last].offset, - ) + f"Exception in {fn.__name__} {sys.exc_info()[1]}\n" + + f"rule: {rule2str(rule)}\n" + + f"offsets {tokens[first].offset} .. {tokens[last].offset}" ) print(traceback.print_tb(sys.exc_info()[2], -1)) raise ParserError(tokens[last], tokens[last].off2int(), self.debug["rules"]) From 18b5934b2db290503f17e1271a07eb81f2191cd7 Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 26 Feb 2023 19:21:30 -0500 Subject: [PATCH 18/22] Allow xdis 6.1.0 --- __pkginfo__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__pkginfo__.py b/__pkginfo__.py index d01a8bf4..4b6d9fe6 100644 --- a/__pkginfo__.py +++ b/__pkginfo__.py @@ -75,7 +75,7 @@ entry_points = { ] } ftp_url = None -install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 6.0.2, < 6.1.0"] +install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 6.0.2, < 6.2.0"] license = "GPL3" mailing_list = "python-debugger@googlegroups.com" From a20972dd12aa7187cf79bd67e68e4379456ab11e Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 26 Feb 2023 19:29:08 -0500 Subject: [PATCH 19/22] xdis PYTHON_VERSION removal --- test/test_pythonlib.py | 17 ++++++++++++----- uncompyle6/util.py | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/test/test_pythonlib.py b/test/test_pythonlib.py index cbef614f..6d06e1d2 100755 --- a/test/test_pythonlib.py +++ b/test/test_pythonlib.py @@ -29,11 +29,18 @@ Step 2: Run the test: from __future__ import print_function -import getopt, os, py_compile, sys, shutil, tempfile, time - +import getopt +import os +import py_compile +import shutil +import sys +import tempfile +import time from fnmatch import fnmatch + +from xdis.version_info import PYTHON_VERSION_TRIPLE + from uncompyle6.main import main -from xdis.version_info import PYTHON_VERSION def get_srcdir(): @@ -164,10 +171,10 @@ def do_tests(src_dir, obj_patterns, target_dir, opts): if opts["do_compile"]: compiled_version = opts["compiled_version"] - if compiled_version and PYTHON_VERSION != compiled_version: + if compiled_version and PYTHON_VERSION_TRIPLE != compiled_version: print( "Not compiling: desired Python version is %s but we are running %s" - % (compiled_version, PYTHON_VERSION), + % (compiled_version, PYTHON_VERSION_TRIPLE), file=sys.stderr, ) else: diff --git a/uncompyle6/util.py b/uncompyle6/util.py index 6f01995b..888ed368 100644 --- a/uncompyle6/util.py +++ b/uncompyle6/util.py @@ -3,7 +3,7 @@ # More could be done here though. from math import copysign -from xdis.version_info import PYTHON_VERSION +from xdis.version_info import PYTHON_VERSION_TRIPLE def is_negative_zero(n): @@ -36,7 +36,7 @@ def better_repr(v, version): if len(v) == 1: return "(%s,)" % better_repr(v[0], version) return "(%s)" % ", ".join(better_repr(i, version) for i in v) - elif PYTHON_VERSION < 3.0 and isinstance(v, long): + elif PYTHON_VERSION_TRIPLE < (3, 0) and isinstance(v, long): s = repr(v) if version >= 3.0 and s[-1] == "L": return s[:-1] From 82963cdf2c3b4629ddfdcc9690c5454a9e84380c Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 24 Mar 2023 20:31:49 -0400 Subject: [PATCH 20/22] Preserve docstring in closures.. This change synchronized from decompyle3 --- uncompyle6/semantics/customize36.py | 13 ++++++-- uncompyle6/semantics/customize37.py | 46 ++++++++++++++--------------- uncompyle6/semantics/transform.py | 11 ++----- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index a94f44b2..36dbf8b6 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -170,6 +170,7 @@ def customize_for_version36(self, version): class_name = node[1][1].attr if self.is_pypy and class_name.find("") > 0: class_name = class_name.split(".")[-1] + else: class_name = node[1][2].attr build_class = node @@ -206,7 +207,9 @@ def customize_for_version36(self, version): elif build_class[1][0] == "load_closure": # Python 3 with closures not functions load_closure = build_class[1] - if hasattr(load_closure[-3], "attr"): + if load_closure[-4] == "LOAD_CODE": + subclass_code = load_closure[-4].attr + elif hasattr(load_closure[-3], "attr"): # Python 3.3 classes with closures work like this. # Note have to test before 3.2 case because # index -2 also has an attr. @@ -215,14 +218,18 @@ def customize_for_version36(self, version): # Python 3.2 works like this subclass_code = load_closure[-2].attr else: - raise "Internal Error n_classdef: cannot find class body" + raise RuntimeError( + "Internal Error n_classdef: cannot find " "class body" + ) if hasattr(build_class[3], "__len__"): if not subclass_info: subclass_info = build_class[3] elif hasattr(build_class[2], "__len__"): subclass_info = build_class[2] else: - raise "Internal Error n_classdef: cannot superclass name" + raise RuntimeError( + "Internal Error n_classdef: cannot " "superclass name" + ) elif node == "classdefdeco2": subclass_info = node subclass_code = build_class[1][0].attr diff --git a/uncompyle6/semantics/customize37.py b/uncompyle6/semantics/customize37.py index d5ac3bc4..97109997 100644 --- a/uncompyle6/semantics/customize37.py +++ b/uncompyle6/semantics/customize37.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2022 by Rocky Bernstein +# Copyright (c) 2019-2023 by Rocky Bernstein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,12 +16,8 @@ """ import re -from uncompyle6.semantics.consts import ( - PRECEDENCE, - TABLE_DIRECT, - INDENT_PER_LEVEL, -) +from uncompyle6.semantics.consts import INDENT_PER_LEVEL, PRECEDENCE, TABLE_DIRECT from uncompyle6.semantics.helper import flatten_list FSTRING_CONVERSION_MAP = {1: "!s", 2: "!r", 3: "!a", "X": ":X"} @@ -54,10 +50,13 @@ def customize_for_version37(self, version): { "and_not": ("%c and not %c", (0, "expr"), (2, "expr")), "ann_assign": ( - "%|%[2]{attr}: %c\n", 0, + "%|%[2]{attr}: %c\n", + 0, ), "ann_assign_init": ( - "%|%[2]{attr}: %c = %c\n", 0, 1, + "%|%[2]{attr}: %c = %c\n", + 0, + 1, ), "async_for_stmt": ( "%|async for %c in %c:\n%+%c%-\n\n", @@ -89,9 +88,8 @@ def customize_for_version37(self, version): "attributes37": ( "%[0]{pattr} import %c", (0, "IMPORT_NAME_ATTR"), - (1, "IMPORT_FROM") + (1, "IMPORT_FROM"), ), - # nested await expressions like: # return await (await bar()) # need parenthesis. @@ -126,19 +124,24 @@ def customize_for_version37(self, version): (0, PRECEDENCE["compare"] - 1), (-2, PRECEDENCE["compare"] - 1), ), - "compare_chained2a_37": ('%[1]{pattr.replace("-", " ")} %p', (0, PRECEDENCE["compare"] - 1)), - "compare_chained2b_false_37": ('%[1]{pattr.replace("-", " ")} %p', (0, PRECEDENCE["compare"] - 1)), - "compare_chained2a_false_37": ('%[1]{pattr.replace("-", " ")} %p', (0, PRECEDENCE["compare"] - 1)), + "compare_chained2a_37": ( + '%[1]{pattr.replace("-", " ")} %p', + (0, PRECEDENCE["compare"] - 1), + ), + "compare_chained2b_false_37": ( + '%[1]{pattr.replace("-", " ")} %p', + (0, PRECEDENCE["compare"] - 1), + ), + "compare_chained2a_false_37": ( + '%[1]{pattr.replace("-", " ")} %p', + (0, PRECEDENCE["compare"] - 1), + ), "compare_chained2c_37": ( '%[3]{pattr.replace("-", " ")} %p %p', (0, PRECEDENCE["compare"] - 1), (6, PRECEDENCE["compare"] - 1), ), - 'if_exp37': ( - '%p if %c else %c', - (1, 'expr', 27), 0, 3 - ), - + "if_exp37": ("%p if %c else %c", (1, "expr", 27), 0, 3), "except_return": ("%|except:\n%+%c%-", 3), "if_exp_37a": ( "%p if %p else %p", @@ -153,9 +156,7 @@ def customize_for_version37(self, version): (5, "expr", 27), ), "ifstmtl": ("%|if %c:\n%+%c%-", (0, "testexpr"), (1, "_ifstmts_jumpl")), - 'import_as37': ( - "%|import %c as %c\n", 2, -2 - ), + "import_as37": ("%|import %c as %c\n", 2, -2), "import_from37": ("%|from %[2]{pattr} import %c\n", (3, "importlist37")), "import_from_as37": ( "%|from %c as %c\n", @@ -178,12 +179,11 @@ def customize_for_version37(self, version): (0, "get_aiter"), (3, "list_iter"), ), - "list_if37": (" if %p%c", (0, 27), 1), "list_if37_not": (" if not %p%c", (0, 27), 1), "testfalse_not_or": ("not %c or %c", (0, "expr"), (2, "expr")), "testfalse_not_and": ("not (%c)", 0), - "testfalsel": ("not %c", (0, "expr")), + "testfalsel": ("not %c", (0, "expr")), "try_except36": ("%|try:\n%+%c%-%c\n\n", 1, -2), "tryfinally36": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", (1, "returns"), 3), "dict_unpack": ("{**%C}", (0, -1, ", **")), diff --git a/uncompyle6/semantics/transform.py b/uncompyle6/semantics/transform.py index 6e5607d4..464ca911 100644 --- a/uncompyle6/semantics/transform.py +++ b/uncompyle6/semantics/transform.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2022 by Rocky Bernstein +# Copyright (c) 2019-2023 by Rocky Bernstein # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -119,15 +119,10 @@ class TreeTransform(GenericASTTraversal, object): mkfunc_pattr = node[-1].pattr if isinstance(mkfunc_pattr, tuple): - assert len(mkfunc_pattr, 4) and isinstance(mkfunc_pattr, int) - is_closure = node[-1].pattr[3] != 0 - else: - # FIXME: This is what we had before. It is hoaky and probably wrong. - is_closure = mkfunc_pattr == "closure" + assert len(mkfunc_pattr) == 4 and isinstance(mkfunc_pattr, int) if ( - (not is_closure) - and len(code.co_consts) > 0 + len(code.co_consts) > 0 and isinstance(code.co_consts[0], str) ): docstring_node = SyntaxTree( From f76c35c9027d389e1fac0179c4f7b17017cf4c0e Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 25 Mar 2023 02:20:26 -0400 Subject: [PATCH 21/22] Slightly nicer docstring detection for closure --- uncompyle6/semantics/customize36.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/uncompyle6/semantics/customize36.py b/uncompyle6/semantics/customize36.py index 36dbf8b6..30bacc01 100644 --- a/uncompyle6/semantics/customize36.py +++ b/uncompyle6/semantics/customize36.py @@ -207,17 +207,12 @@ def customize_for_version36(self, version): elif build_class[1][0] == "load_closure": # Python 3 with closures not functions load_closure = build_class[1] - if load_closure[-4] == "LOAD_CODE": - subclass_code = load_closure[-4].attr - elif hasattr(load_closure[-3], "attr"): - # Python 3.3 classes with closures work like this. - # Note have to test before 3.2 case because - # index -2 also has an attr. - subclass_code = load_closure[-3].attr - elif hasattr(load_closure[-2], "attr"): - # Python 3.2 works like this - subclass_code = load_closure[-2].attr - else: + subclass_code = None + for i in range(-4, -1): + if load_closure[i] == "LOAD_CODE": + subclass_code = load_closure[i].attr + break + if subclass_code is None: raise RuntimeError( "Internal Error n_classdef: cannot find " "class body" ) From 5a2e5cf6bb8f361edce675b38beb1a4fc382befa Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 25 Mar 2023 02:27:59 -0400 Subject: [PATCH 22/22] Merge branch 'master' into python-3.3-to-3.5 --- uncompyle6/parsers/parse37base.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uncompyle6/parsers/parse37base.py b/uncompyle6/parsers/parse37base.py index ac95e3f0..2553bf82 100644 --- a/uncompyle6/parsers/parse37base.py +++ b/uncompyle6/parsers/parse37base.py @@ -1261,11 +1261,6 @@ class Python37BaseParser(PythonParser): except Exception: import sys, traceback - print( - f"Exception in {fn.__name__} {sys.exc_info()[1]}\n" - + f"rule: {rule2str(rule)}\n" - + f"offsets {tokens[first].offset} .. {tokens[last].offset}" - ) print(traceback.print_tb(sys.exc_info()[2], -1)) raise ParserError(tokens[last], tokens[last].off2int(), self.debug["rules"])