如何让IPython的stdout输出纳入%history -o以生成doctest?
Great questions! Let's tackle both of them since they're tied directly to your core goal of generating doctests from your IPython session—where you need to capture not just expression return values, but also stdout/stderr output from print() statements and other writes to standard output.
First: Can we capture all stdout output in IPython's history?
Absolutely. By default, IPython's %history -o only tracks return values of expressions (like the result of 1+2), not content written directly to stdout (like print("hello")). But we can fix this with either a quick magic command or a persistent configuration tweak.
Second: Making print() output show up in %history -o (for doctests)
Your main need is getting stdout into the output history so you can generate valid doctest cases. Here are two reliable approaches:
Approach 1: Quick and dirty with %logstart (ideal for one-off sessions)
If you just need this for a single session, use IPython's built-in logging to capture everything—input, return values, and stdout:
- Start your IPython session.
- Run this command:
%logstart -o -r doctest_session.log-otells IPython to log output (including stdout)-rlogs all input commandsdoctest_session.logis the file where everything gets saved
Now every print() statement, expression result, and input command will be logged in the file. The log will look exactly like your IPython session:
In [1]: print("Hello, doctest!") Hello, doctest! In [2]: 5 * 4 Out[2]: 20
This format is perfect for copying directly into doctests—just replace In [x]: with >>> and you're good to go.
Approach 2: Persistent configuration (for all future sessions)
If you want this behavior every time you launch IPython, add a custom hook to your IPython startup script:
- Create a startup script file at
~/.ipython/profile_default/startup/00-capture-stdout.py(create thestartupfolder if it doesn't exist). - Paste this code into the file:
import sys from io import StringIO from IPython import get_ipython class StdoutCapture: def __init__(self, original_stdout): self.original = original_stdout self.buffer = StringIO() def write(self, data): self.buffer.write(data) self.original.write(data) # Keep output visible in the terminal def flush(self): self.original.flush() def get_output(self): return self.buffer.getvalue() original_stdout = sys.stdout ip = get_ipython() def pre_execute(): sys.stdout = StdoutCapture(original_stdout) def post_execute(result): captured = sys.stdout.get_output() sys.stdout = original_stdout # Update the output history with captured stdout + return value if captured: if result is not None: combined = captured + str(result) else: combined = captured ip.user_ns['_oh'][ip.execution_count] = combined ip.events.register('pre_execute', pre_execute) ip.events.register('post_execute', post_execute) - Restart IPython. Now every
print()output will be merged with the return value (if any) in the output history. When you run%history -o, you'll see the stdout content included.
A quick note on doctest generation
Once you have the output captured (either via log file or modified history), you can easily convert it to doctest format. For example, using the log file, you can run a simple script to replace In [x]: with >>> and strip any unnecessary lines.
内容的提问来源于stack exchange,提问作者Tonis F. Piip

