Skip to content

Commit

Permalink
Merge pull request #502 from gdesmar/docstring_bytes
Browse files Browse the repository at this point in the history
Fix for print_docstring()'s `docstring.find(quote)` Type error
  • Loading branch information
rocky authored Oct 17, 2024
2 parents 193c262 + 7db6a27 commit 27c869b
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 65 deletions.
Binary file added test/bytecode_2.7/16_bytestring_docstring.pyc
Binary file not shown.
Binary file added test/bytecode_3.8/16_no_bytestring_docstring.pyc
Binary file not shown.
45 changes: 45 additions & 0 deletions test/simple_source/stmts/16_bytestring_docstring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Module docstring"""
class A:
b"""Got \xe7\xfe Bytes?"""
assert __doc__ == b"""Got \xe7\xfe Bytes?"""

def class_func(self):
b"""Got \xe7\xfe Bytes?"""
assert __doc__ == """Module docstring"""

class B:
"""Got no Bytes?"""
assert __doc__ == """Got no Bytes?"""

def class_func(self):
"""Got no Bytes?"""
assert __doc__ == """Module docstring"""

def single_func():
"""single docstring?"""
assert __doc__ == """Module docstring"""

def single_byte_func():
b"""Got \xe7\xfe Bytes?"""
assert __doc__ == """Module docstring"""

assert __doc__ == """Module docstring"""

assert single_func.__doc__ == """single docstring?"""
single_func()

assert single_byte_func.__doc__ == b"""Got \xe7\xfe Bytes?"""
single_byte_func()

assert A.__doc__ == b"""Got \xe7\xfe Bytes?"""
assert A.class_func.__doc__ == b"""Got \xe7\xfe Bytes?"""
a = A()
assert a.class_func.__doc__ == b"""Got \xe7\xfe Bytes?"""
a.class_func()

assert B.__doc__ == """Got no Bytes?"""
assert B.class_func.__doc__ == """Got no Bytes?"""
b = B()
assert b.class_func.__doc__ == """Got no Bytes?"""
b.class_func()

45 changes: 45 additions & 0 deletions test/simple_source/stmts/16_no_bytestring_docstring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Module docstring"""
class A:
b"""Got \xe7\xfe Bytes?"""
assert __doc__ == """Module docstring"""

def class_func(self):
b"""Got \xe7\xfe Bytes?"""
assert __doc__ == """Module docstring"""

class B:
"""Got no Bytes?"""
assert __doc__ == """Got no Bytes?"""

def class_func(self):
"""Got no Bytes?"""
assert __doc__ == """Module docstring"""

def single_func():
"""single docstring?"""
assert __doc__ == """Module docstring"""

def single_byte_func():
b"""Got \xe7\xfe Bytes?"""
assert __doc__ == """Module docstring"""

assert __doc__ == """Module docstring"""

assert single_func.__doc__ == """single docstring?"""
single_func()

assert single_byte_func.__doc__ is None
single_byte_func()

assert A.__doc__ is None
assert A.class_func.__doc__ is None
a = A()
assert a.class_func.__doc__ is None
a.class_func()

assert B.__doc__ == """Got no Bytes?"""
assert B.class_func.__doc__ == """Got no Bytes?"""
b = B()
assert b.class_func.__doc__ == """Got no Bytes?"""
b.class_func()

3 changes: 3 additions & 0 deletions uncompyle6/semantics/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ def is_lambda_mode(compile_mode: str) -> bool:


def print_docstring(self, indent, docstring):
if isinstance(docstring, bytes):
docstring = docstring.decode("utf8", errors="backslashreplace")

quote = '"""'
if docstring.find(quote) >= 0:
if docstring.find("'''") == -1:
Expand Down
67 changes: 2 additions & 65 deletions uncompyle6/semantics/n_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
PRECEDENCE,
minint,
)
from uncompyle6.semantics.helper import find_code_node, flatten_list
from uncompyle6.semantics.helper import find_code_node, flatten_list, print_docstring
from uncompyle6.util import better_repr, get_code_name


Expand Down Expand Up @@ -541,70 +541,7 @@ def n_docstring(self, node):
else:
docstring = node[0].pattr

quote = '"""'
if docstring.find(quote) >= 0:
if docstring.find("'''") == -1:
quote = "'''"

self.write(indent)
docstring = repr(docstring.expandtabs())[1:-1]

for orig, replace in (
("\\\\", "\t"),
("\\r\\n", "\n"),
("\\n", "\n"),
("\\r", "\n"),
('\\"', '"'),
("\\'", "'"),
):
docstring = docstring.replace(orig, replace)

# Do a raw string if there are backslashes but no other escaped characters:
# also check some edge cases
if (
"\t" in docstring
and "\\" not in docstring
and len(docstring) >= 2
and docstring[-1] != "\t"
and (docstring[-1] != '"' or docstring[-2] == "\t")
):
self.write("r") # raw string
# Restore backslashes unescaped since raw
docstring = docstring.replace("\t", "\\")
else:
# Escape the last character if it is the same as the
# triple quote character.
quote1 = quote[-1]
if len(docstring) and docstring[-1] == quote1:
docstring = docstring[:-1] + "\\" + quote1

# Escape triple quote when needed
if quote == '"""':
replace_str = '\\"""'
else:
assert quote == "'''"
replace_str = "\\'''"

docstring = docstring.replace(quote, replace_str)
docstring = docstring.replace("\t", "\\\\")

lines = docstring.split("\n")

self.write(quote)
if len(lines) == 0:
self.println(quote)
elif len(lines) == 1:
self.println(lines[0], quote)
else:
self.println(lines[0])
for line in lines[1:-1]:
if line:
self.println(line)
else:
self.println("\n\n")
pass
pass
self.println(lines[-1], quote)
print_docstring(self, indent, docstring)
self.prune()

def n_elifelsestmtr(self, node: SyntaxTree):
Expand Down

0 comments on commit 27c869b

Please sign in to comment.