From 0f80c38530382b3e6f8049dd8730b1d0efc49864 Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 19 Jul 2020 20:31:50 -0400 Subject: [PATCH] Better doc string detection A bug in 2.7 test_descr.py revealed a problem with the way we were detecting docstrings. __doc__ = DocDescr() was getting confused with a docstring. This program also reveals other bugs in 3.2+ but we'll deal with that in another commit. --- test/add-test.py | 2 +- test/bytecode_2.7_run/03_doc_assign.pyc | Bin 0 -> 1449 bytes test/bytecode_3.1_run/03_doc_assign.pyc | Bin 0 -> 1686 bytes .../bytecode_3.3_run/03_doc_assign.pyc-notyet | Bin 0 -> 1642 bytes .../bytecode_3.6_run/03_doc_assign.pyc-notyet | Bin 0 -> 1026 bytes test/simple_source/bug27+/03_doc_assign.py | 27 ++++++++++++++++ uncompyle6/semantics/pysource.py | 2 +- uncompyle6/semantics/transform.py | 30 ++++++++++++++---- 8 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 test/bytecode_2.7_run/03_doc_assign.pyc create mode 100644 test/bytecode_3.1_run/03_doc_assign.pyc create mode 100644 test/bytecode_3.3_run/03_doc_assign.pyc-notyet create mode 100644 test/bytecode_3.6_run/03_doc_assign.pyc-notyet create mode 100644 test/simple_source/bug27+/03_doc_assign.py diff --git a/test/add-test.py b/test/add-test.py index 513f3b63..ab4cbec8 100755 --- a/test/add-test.py +++ b/test/add-test.py @@ -21,7 +21,7 @@ for path in py_source: cfile = "bytecode_%s%s/%s" % (version, suffix, short) + "c" print("byte-compiling %s to %s" % (path, cfile)) optimize = 2 - if vers >= (3, 0): + if vers > (3, 1): py_compile.compile(path, cfile, optimize=optimize) else: py_compile.compile(path, cfile) diff --git a/test/bytecode_2.7_run/03_doc_assign.pyc b/test/bytecode_2.7_run/03_doc_assign.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b44aac15fefecb7b983f2f3e6a6473ae5df03da GIT binary patch literal 1449 zcmbtU$!-%t5Un0N-V%_4lRywc<`7{if*?2`#3Gh(HPV<1xin5s+Zo56v8LOCNHGUM zEQfqX4*V29!4L4NXT~<>WW`dK%de_mt?j~}OSMnGH>9HMBk=tk$6SX9hz=kHeT5=O z3;IGN0*~6w0{h&AczYC@#wdw_9W& za5T`;#N$Niy~A}e|{x6afcNR&h!XHB@MzDtb_ zQV!!_3{vTH0<(ZiM)f(!Dvn|5l5my283eDm@PFpT_PKfea4VM13l^H?=W`)hHC&-g zljEVfXv=0;!9||be1@E HuUh{Li5MZ_ literal 0 HcmV?d00001 diff --git a/test/bytecode_3.1_run/03_doc_assign.pyc b/test/bytecode_3.1_run/03_doc_assign.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0b5755c302ad5bb405e912e2d202a77634a5dc7 GIT binary patch literal 1686 zcmdT^OK;Oa5T4z%NfQDG(g)>$!lA-a1x4)vAs*t9){}|DCRb*5a2y(2A8;C zpcqU6SjeFpRB+qgLG|`DGVO7y2WfBA#-&kl|Gqp`@+?XQcW2xjO;=FpocV$3ji*Jj z3}9M;ZwP$>=raf_@ELDifhmW{99?y2H8-CyxBwWUbq&rqn0JtI_~bzJk?=K#>jGyE ztUK^>4(7%Uhkws;jJq3LLnk|u@<9*1NnW9tEmU~>k#cE4YswF&M0EoSR8o}m(3_-- zVjiQqV32N;9(pGkY+)n`Zg5C}CLipNIF{oCyIwP^Gv?P`Q7vPlL!Igdy%|bNJbV_W7+<{7~w#m2o2D`;Ja8zjk@^SP&|A8Q& YvvakCM|Aa5Kl#roPlow&>|t+xIb=7tr6o>mPn>@b`d7QSI@;zrrzFATeH#J`xRx zG|>f-7yESFgVWMWJQ_G6G)Exs-~%(bffjHJ=qse3iH1-F6gTJ-bPc-jX}(l6DQ-DM zYvDA9H6hy?Vus2Cl}Emk@->d>f}9h*MR5aXJ@BRCGW!6j0{BaDb_b-;rr zn%O~br6Z%0p>16Tu}xtmN)HS<;+i(&=`7J~GMg*CcVb6Rp6%{Ee(G>Q{P8I5&(5r1 z%skQsa~rH#mmprt>)^CK`v`x3gkFCUBrH*{mu@8{^E8Vs>R~H+m``n#l&#fk=*g59 z4&{K*Iz%h?B?VNIy#w9|zV|$bX(j(d`qT%v!pu4daLrwmXhsMHn`_(tgQmT~Ff6m*pzTS7C0U^^$-Q6R?gQc7F10&9 E0rewt!2kdN literal 0 HcmV?d00001 diff --git a/test/bytecode_3.6_run/03_doc_assign.pyc-notyet b/test/bytecode_3.6_run/03_doc_assign.pyc-notyet new file mode 100644 index 0000000000000000000000000000000000000000..3025a174641368b3adcb90a30f2b0f800a924462 GIT binary patch literal 1026 zcmb_b!EVz)5S?8+juR48r4dM+dI^$+NKt!0f>I$-iJLj}mdnWXZlXj^9CjU{Mh=%m zLfrWY4*ZZ0T=)x4%-Fa}B`P?u(u{W}GjHF%nVfdJ?T_F4_{{sa;^Od`mn>x}^9z{IP z+6V=N+6-@C2?%aT^y#2sL`frAfsY*JP=UK1WZGt^(ioMtv6(Kj)C|v(G>#H`KDK{h5d)`itxm>;SCxz!DHnVN)1b?L4(xQk zOf=ebWnz7NwVIqfe{%fnw0sajO(%=t^15guPIQLYVb*POAY9QQ^Thh;KM64WjSvQ| zVOfhd;yhI=x*LlQ;`>#URE$lNelFSVIgd;HTFrZOvW(&j!)$IfO;CVWIXQLHzR=`Q z%LFMY|I)H|iF(5-)=Z60V5UWmW#C3*Ni3-Du1Hq=>QbKBnjcH{U-^al!yWt#>v9*j zx{OD;=GLV>J%j%}fi%q2aOf69`Le@5^q6I_?7%r__GG_d?$bUbJpNX)@Qm#XbXK+e KysRx7c=!ul+}lb3 literal 0 HcmV?d00001 diff --git a/test/simple_source/bug27+/03_doc_assign.py b/test/simple_source/bug27+/03_doc_assign.py new file mode 100644 index 00000000..bc1be2d5 --- /dev/null +++ b/test/simple_source/bug27+/03_doc_assign.py @@ -0,0 +1,27 @@ +# From 2.7 test_descr.py +# Testing __doc__ descriptor... +# The bug in decompilation was erroneously matching +# __doc__ = as a docstring + +"""This program is self-checking!""" + +def test_doc_descriptor(): + # Testing __doc__ descriptor... + # Python SF bug 542984 + class DocDescr(object): + def __get__(self, object, otype): + if object: + object = object.__class__.__name__ + ' instance' + if otype: + otype = otype.__name__ + return 'object=%s; type=%s' % (object, otype) + class OldClass: + __doc__ = DocDescr() + class NewClass(object): + __doc__ = DocDescr() + assert OldClass.__doc__ == 'object=None; type=OldClass' + assert OldClass().__doc__ == 'object=OldClass instance; type=OldClass' + assert NewClass.__doc__ == 'object=None; type=NewClass' + assert NewClass().__doc__ == 'object=NewClass instance; type=NewClass' + +test_doc_descriptor() diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index a5d4ac83..da55eb7b 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -164,7 +164,6 @@ from uncompyle6.semantics.consts import ( NONE, RETURN_NONE, PASS, - ASSIGN_DOC_STRING, NAME_MODULE, TAB, INDENT_PER_LEVEL, @@ -2324,6 +2323,7 @@ class SourceWalker(GenericASTTraversal, object): if ast[0] == "docstring": self.println(self.traverse(ast[0])) del ast[0] + first_stmt = ast[0] if 3.0 <= self.version <= 3.3: try: diff --git a/uncompyle6/semantics/transform.py b/uncompyle6/semantics/transform.py index e01220ee..f9fe6bbf 100644 --- a/uncompyle6/semantics/transform.py +++ b/uncompyle6/semantics/transform.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from xdis import iscode from uncompyle6.show import maybe_show_tree from copy import copy from spark_parser import GenericASTTraversal, GenericASTTraversalPruningException @@ -21,16 +20,35 @@ from spark_parser import GenericASTTraversal, GenericASTTraversalPruningExceptio from uncompyle6.semantics.helper import find_code_node from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.scanners.tok import NoneToken, Token -from uncompyle6.semantics.consts import RETURN_NONE +from uncompyle6.semantics.consts import RETURN_NONE, ASSIGN_DOC_STRING def is_docstring(node): if node == "sstmt": node = node[0] - try: - return node.kind == "assign" and node[1][0].pattr == "__doc__" - except: - return False + # TODO: the test below on 2.7 succeeds for + # class OldClass: + # __doc__ = DocDescr() + # which produces: + # + # assign (2) + # 0. expr + # call (2) + # 0. expr + # L. 16 6 LOAD_DEREF 0 'DocDescr' + # 1. 9 CALL_FUNCTION_0 0 None + # 1. store + # + # See Python 2.7 test_descr.py + + # If ASSIGN_DOC_STRING doesn't work we need something like the below + # but more elaborate to address the above. + + # try: + # return node.kind == "assign" and node[1][0].pattr == "__doc__" + # except: + # return False + return node == ASSIGN_DOC_STRING def is_not_docstring(call_stmt_node):