我有一个Python模块,正在使用Sphinx编写包括doctest的教程。
这个模块配备了一些辅助程序。
我想在文档中包含这些帮助程序,并让doctest检查当前程序版本和文档之间的标准输出同步。
我想我可以使用sh模块或popen来检查给定程序的标准输出,但我希望这些技巧不会显示在文档中,否则非程序员用户肯定会迷失。
有什么方法可以实现这一点吗?
我有一个Python模块,正在使用Sphinx编写包括doctest的教程。
这个模块配备了一些辅助程序。
我想在文档中包含这些帮助程序,并让doctest检查当前程序版本和文档之间的标准输出同步。
我想我可以使用sh模块或popen来检查给定程序的标准输出,但我希望这些技巧不会显示在文档中,否则非程序员用户肯定会迷失。
有什么方法可以实现这一点吗?
doctest 模块仅检查可以从 Python 交互式提示符中运行的语句。
使用 subprocess 模块,可以从 Python 交互式提示符中调用命令行工具:
# Create Helper Function
>>> import subprocess
>>> run_commandline = lambda cmd: subprocess.check_output(cmd, shell=True).decode()
# Doctestable command-line calls
>>> print(run_commandline('cal 7 2017'))
July 2017
Su Mo Tu We Th Fr Sa
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
>>> print(run_commandline('echo $BASH_VERSION'))
3.2.57(1)-release
两个想法:首先,这些调用的详细信息可以大部分隐藏在辅助函数中,以最小化干扰。其次,如果您需要从Python调用命令行程序,那么使用popen或subprocess并不是一个技巧,因为这些是专门设计用于从Python进行这些调用的工具。我想我可以使用sh模块或popen检查给定程序的标准输出,但我更喜欢那些技巧不出现在文档中,否则非程序员用户肯定会迷失。
正如Raymond Hettinger所提到的,您应该创建一个函数(例如shell
),它接受一个字符串并使用subprocess
库运行相应的字符串。您还可以使用contextlib.redirect_stdout
装饰和管理输出流,以使结果可测试。
但是,在生成的HTML中,显示的是相同的Python代码而不是Shell代码。为了解决这个问题,我们使用了以下扩展JavaScript代码(基于copybutton.js):
$(document).ready(function() {
const NAME_CLASS = "n";
document.querySelectorAll(`.highlight-pycon pre .${NAME_CLASS}`).forEach(function(nameElement) {
if (nameElement.innerText !== "shell")
return;
const GENERIC_PROMPT_CLASS = "gp";
const promptElement = nameElement.previousElementSibling;
if (!promptElement.classList.contains(GENERIC_PROMPT_CLASS))
return;
const GENERIC_OUTPUT_CLASS = "go";
const GENERIC_TRACEBACK_CLASS = "gt";
let pythonCode = "";
let pythonCodeNodes = [];
let currentNode = nameElement;
while (
currentNode
&& !(currentNode.classList?.contains(GENERIC_OUTPUT_CLASS)
|| currentNode.classList?.contains(GENERIC_TRACEBACK_CLASS))
) {
pythonCode += currentNode.textContent;
pythonCodeNodes.push(currentNode);
currentNode = currentNode.nextSibling;
}
const outputStartElement = currentNode;
const match = pythonCode.match(/shell\("(?<command>[^"]*)".*\)/);
if (!match)
return;
const command = match.groups["command"]
const invisiblePartsRemovedCommand = command.replace(/\s?\[.*\]/, '')
const shellCode = invisiblePartsRemovedCommand + "\n"
promptElement.innerText = "$ ";
pythonCodeNodes.forEach(node => node.remove());
promptElement.parentNode.insertBefore(document.createTextNode(shellCode), outputStartElement);
});
});