diff --git a/Lib/ast.py b/Lib/ast.py index 93ffa1edc84d55f..fb8460798157f07 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -1001,11 +1001,14 @@ def _write_docstring(self, node): if node.kind == "u": self.write("u") - # Preserve quotes in the docstring by escaping them - value = node.value.replace("\\", "\\\\") - value = value.replace('"""', '""\"') - if value[-1] == '"': - value = value.replace('"', '\\"', -1) + value = node.value + if value: + # Preserve quotes in the docstring by escaping them + value = value.replace("\\", "\\\\") + value = value.replace('"""', '""\"') + value = value.replace("\r", "\\r") + if value[-1] == '"': + value = value.replace('"', '\\"', -1) self.write(f'"""{value}"""') diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index d04db4d5f46e1ad..086a1b6ae851e19 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -304,11 +304,18 @@ def test_invalid_yield_from(self): def test_docstrings(self): docstrings = ( 'this ends with double quote"', - 'this includes a """triple quote"""' + 'this includes a """triple quote"""', + '\r', + '\\r', + '\t', + '\\t', + '\n', + '\\n', + '\r\\r\t\\t\n\\n' ) for docstring in docstrings: # check as Module docstrings for easy testing - self.check_ast_roundtrip(f"'{docstring}'") + self.check_ast_roundtrip(f"'''{docstring}'''") class CosmeticTestCase(ASTTestCase): @@ -354,6 +361,10 @@ def test_docstrings(self): empty newline"""''', '"""With some \t"""', '"""Foo "bar" baz """', + '"""\\r"""', + '""""""', + '"""\'\'\'"""', + '"""\'\'\'\'\'\'"""', ) for prefix in docstring_prefixes: