|
124 | 124 | import time |
125 | 125 | import socketserver |
126 | 126 | import atexit |
127 | | -from textwrap import dedent |
| 127 | +import inspect |
128 | 128 | from itertools import count |
129 | 129 |
|
130 | 130 | try: |
131 | | - # macro-enabled console with imacropy semantics |
| 131 | + from macropy.core.macros import WrappedFunction |
| 132 | +except ModuleNotFoundError: # probably no MacroPy installed |
| 133 | + WrappedFunction = None |
| 134 | + |
| 135 | +try: |
| 136 | + # macro-enabled console with imacropy semantics and ?, ?? docstring/source viewing syntax |
132 | 137 | from imacropy.console import MacroConsole as Console |
133 | 138 | except ModuleNotFoundError: |
134 | 139 | try: |
|
170 | 175 | # -------------------------------------------------------------------------------- |
171 | 176 | # Exports for REPL sessions |
172 | 177 |
|
| 178 | +# This is a copy of `imacropy.doc` (from v0.3.0) with a slightly modified docstring. |
| 179 | +# We strictly need a local copy of this only if `imacropy` is not installed, |
| 180 | +# to allow viewing docstrings in the MacroPy or stdlib consoles. |
173 | 181 | def doc(obj): |
174 | | - """Print an object's docstring, non-interactively, but emulate help's dedenting.""" |
| 182 | + """Print an object's docstring, non-interactively. |
| 183 | +
|
| 184 | + Additionally, if the information is available, print the filename |
| 185 | + and the starting line number of the definition of `obj` in that file. |
| 186 | + This is printed before the actual docstring. |
| 187 | +
|
| 188 | + This works around the problem that in a REPL session, the stdin/stdout |
| 189 | + of the builtin `help()` are not properly redirected. |
| 190 | +
|
| 191 | + And that looking directly at `some_macro.__doc__` prints the string |
| 192 | + value as-is, without formatting it. |
| 193 | +
|
| 194 | + NOTE: if you have the `imacropy` package installed, you can use |
| 195 | + the IPython-like `obj?` and `obj??` syntax instead (provided by |
| 196 | + `imacropy.console.MacroConsole`). |
| 197 | + """ |
175 | 198 | if not hasattr(obj, "__doc__") or not obj.__doc__: |
176 | 199 | print("<no docstring>") |
177 | 200 | return |
178 | | - # Emulate help()'s dedenting. Typically, the first line in a docstring |
179 | | - # has no leading whitespace, while the rest follow the indentation of |
180 | | - # the function body. |
181 | | - firstline, *rest = obj.__doc__.split("\n") |
182 | | - rest = dedent("\n".join(rest)) |
183 | | - doc = [firstline, *rest.split("\n")] |
184 | | - for line in doc: |
185 | | - print(line) |
| 201 | + try: |
| 202 | + if isinstance(obj, WrappedFunction): |
| 203 | + obj = obj.__wrapped__ # this is needed to make inspect.getsourcefile work with macros |
| 204 | + filename = inspect.getsourcefile(obj) |
| 205 | + source, firstlineno = inspect.getsourcelines(obj) |
| 206 | + print(f"{filename}:{firstlineno}") |
| 207 | + except (TypeError, OSError): |
| 208 | + pass |
| 209 | + print(inspect.cleandoc(obj.__doc__)) |
186 | 210 |
|
187 | 211 | # TODO: detect stdout, stderr and redirect to the appropriate stream. |
188 | 212 | def server_print(*values, **kwargs): |
|
0 commit comments