Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for print_docstring()'s docstring.find(quote) Type error #502

Merged
merged 3 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading