1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
Description: Fix handling of docstring dedenting in Python 3.13
Forwarded: https://github.com/jupyter/jupyter_client/pull/1052
Bug-Debian: https://bugs.debian.org/1092533
Author: Julian Gilbey <jdg@debian.org>
Last-Update: 2025-01-13
--- a/jupyter_client/client.py 2024-07-29 09:27:07.000000000 +0100
+++ b/jupyter_client/client.py 2025-01-13 20:23:42.392822889 +0000
@@ -3,7 +3,9 @@
# Distributed under the terms of the Modified BSD License.
import asyncio
import inspect
+import re
import sys
+import textwrap
import time
import typing as t
from functools import partial
@@ -35,6 +37,24 @@
raise ValueError("value %r in dict must be a string" % v)
+def get_docstring_indent(doc: str) -> str:
+ # Python 3.13 dedents docstrings automatically.
+ # In this module, the docstring indent is the indent of the
+ # first non-blank line after the initial line, which is returned
+ # as a whitespace string.
+ # This is not a general method for determining the indent of a docstring!
+ # See the source code of textwrap.dedent() for that.
+ doclines = doc.split("\n")
+ for line in doclines[1:]:
+ if re.match(r"\s*$", line):
+ continue
+ linematch = re.match(r"(\s*)\S", line)
+ assert linematch is not None
+ return linematch.group(1)
+ # If there was no content in the docstring beyond the initial line
+ return ""
+
+
def reqrep(wrapped: t.Callable, meth: t.Callable, channel: str = "shell") -> t.Callable:
wrapped = wrapped(meth, channel)
if not meth.__doc__:
@@ -42,31 +61,34 @@
# so don't bother building the wrapped docstring
return wrapped
- basedoc, _ = meth.__doc__.split("Returns\n", 1)
- parts = [basedoc.strip()]
- if "Parameters" not in basedoc:
- parts.append(
- """
+ params_header = """\
Parameters
----------
"""
- )
- parts.append(
- """
- reply: bool (default: False)
+ returns_doc = """\
+ reply : bool (default: False)
Whether to wait for and return reply
- timeout: float or None (default: None)
+
+ timeout : float or None (default: None)
Timeout to use when waiting for a reply
Returns
-------
- msg_id: str
+ msg_id : str
The msg_id of the request sent, if reply=False (default)
- reply: dict
+
+ reply : dict
The reply message for this request, if reply=True
- """
- )
- wrapped.__doc__ = "\n".join(parts)
+ """
+
+ basedoc, _ = meth.__doc__.split("Returns\n", 1)
+ parts = [basedoc.strip()]
+ indent = get_docstring_indent(basedoc)
+ if "Parameters" not in basedoc:
+ parts.append(textwrap.indent(textwrap.dedent(params_header), indent))
+ parts.append(textwrap.indent(textwrap.dedent(returns_doc), indent))
+
+ wrapped.__doc__ = "\n\n".join(parts)
return wrapped
|