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.
This commit is contained in:
rocky
2020-07-19 20:31:50 -04:00
parent bd07de5172
commit 0f80c38530
8 changed files with 53 additions and 8 deletions

View File

@@ -21,7 +21,7 @@ for path in py_source:
cfile = "bytecode_%s%s/%s" % (version, suffix, short) + "c" cfile = "bytecode_%s%s/%s" % (version, suffix, short) + "c"
print("byte-compiling %s to %s" % (path, cfile)) print("byte-compiling %s to %s" % (path, cfile))
optimize = 2 optimize = 2
if vers >= (3, 0): if vers > (3, 1):
py_compile.compile(path, cfile, optimize=optimize) py_compile.compile(path, cfile, optimize=optimize)
else: else:
py_compile.compile(path, cfile) py_compile.compile(path, cfile)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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()

View File

@@ -164,7 +164,6 @@ from uncompyle6.semantics.consts import (
NONE, NONE,
RETURN_NONE, RETURN_NONE,
PASS, PASS,
ASSIGN_DOC_STRING,
NAME_MODULE, NAME_MODULE,
TAB, TAB,
INDENT_PER_LEVEL, INDENT_PER_LEVEL,
@@ -2324,6 +2323,7 @@ class SourceWalker(GenericASTTraversal, object):
if ast[0] == "docstring": if ast[0] == "docstring":
self.println(self.traverse(ast[0])) self.println(self.traverse(ast[0]))
del ast[0] del ast[0]
first_stmt = ast[0]
if 3.0 <= self.version <= 3.3: if 3.0 <= self.version <= 3.3:
try: try:

View File

@@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from xdis import iscode
from uncompyle6.show import maybe_show_tree from uncompyle6.show import maybe_show_tree
from copy import copy from copy import copy
from spark_parser import GenericASTTraversal, GenericASTTraversalPruningException 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.semantics.helper import find_code_node
from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6.scanners.tok import NoneToken, Token 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): def is_docstring(node):
if node == "sstmt": if node == "sstmt":
node = node[0] node = node[0]
try: # TODO: the test below on 2.7 succeeds for
return node.kind == "assign" and node[1][0].pattr == "__doc__" # class OldClass:
except: # __doc__ = DocDescr()
return False # 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): def is_not_docstring(call_stmt_node):