為了使用 autodoc, 我在 conf.py 裡這樣寫:
sys.path.append('/usr/lib/python2.5/site-packages/Sphinx-1.0.7-py2.5.egg/sphinx/ext') extensions = ['graphviz', 'autodoc']
結果跑出:
autodoc documenter <class 'autodoc.ModuleDocumenter'> must be a subclass of Documenter
正確的寫法是:
extensions = ['sphinx.ext.graphviz', 'sphinx.ext.autodoc']
開 pdb 追下去, 發現是 sphinx.application 和 sphinx.ext.autodoc 之間 circular import 造成的問題。
在 sphinx.application 裡:
def add_autodocumenter(self, cls): from sphinx.ext import autodoc autodoc.add_documenter(cls) self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
會載入 sphinx.ext.autodoc, 但我原本的寫法, 讓 sphinx 透過 __import__ 載入 autodoc, 結果產生兩個不同的物件, 但都是 autodoc, 結果剩下的一些程式操作, 就發生了悲劇。
在 autodoc 裡的這段程式就丟出 exception:
if not issubclass(cls, Documenter): raise ExtensionError('autodoc documenter %r must be a subclass '
cls (即 ModuleDocumenter) 真的不是 Documenter 的 subclass, 因為這個 ModuleDocumenter 是其中一個 autodoc 的, 而 Documenter 卻是另一個 autodoc 的。
若硬註解掉這兩行, 雖然 autodoc 的部份指令能用, 卻也會部份失效, 像 :members: 就會完全沒效果, 原因也是出在有兩個 autodoc 上, 結果找 members 的那個 autodoc 的 AutoDirective._registry 是空的, 造成它找不到能處理 members 的 Documenter。
備註: autodoc.AutoDirective 負責處理 directive auto*。AutoDirective 依指令名稱從 _registry 中找出適用的 class 來處理, 比方說 automodule 就會找 ModuleDocumenter 來處理。正常的程序下, autodoc.setup() 會填好 AutoDirective._registry, 但在上面說的錯誤設定下, 會變成 sphinx.ext.autodoc.AutoDirective._registry 有設好, 而 autodoc.AutoDirective._registry 是空的。
下面是用 pdb 看設錯情況發生的狀況, 滿有趣的:
> /usr/lib/python2.5/site-packages/Sphinx-1.0.7-py2.5.egg/sphinx/ext/autodoc.py(1177)run() -> documenter.generate(more_content=self.content) /usr/lib/python2.5/site-packages/Sphinx-1.0.7-py2.5.egg/sphinx/ext/autodoc.py(715)generate() -> self.document_members(all_members) /usr/lib/python2.5/site-packages/Sphinx-1.0.7-py2.5.egg/sphinx/ext/autodoc.py(981)document_members() -> ModuleLevelDocumenter.document_members(self, all_members) /usr/lib/python2.5/site-packages/Sphinx-1.0.7-py2.5.egg/sphinx/ext/autodoc.py(609)document_members() -> for (mname, member, isattr) in self.filter_members(members, want_all): (Pdb) l 1172 # process the options with the selected documenter's option_spec 1173 self.genopt = Options(assemble_option_dict( 1174 self.options.items(), doc_class.option_spec)) 1175 # generate the output 1176 documenter = doc_class(self, self.arguments[0]) 1177 -> documenter.generate(more_content=self.content) 1178 if not self.result: 1179 return self.warnings 1180 1181 # record all filenames as dependencies -- this will at least 1182 # partially make automatic invalidation possible (Pdb) documenter.__module__ 'autodoc' (Pdb) id(AutoDirective) 21622640 (Pdb) down > /usr/lib/python2.5/site-packages/Sphinx-1.0.7-py2.5.egg/sphinx/ext/autodoc.py(715)generate() -> self.document_members(all_members) (Pdb) id(AutoDirective) 20956032 (Pdb) import sys (Pdb) id(sys.modules['autodoc'].AutoDirective) 20956032 (Pdb) id(sys.modules['sphinx.ext.autodoc'].AutoDirective) 21622640
表示一開始在 sphinx.ext.autodoc, 但 documenter 是從 autodoc 取來的, 結果就無聲無息的掛了。
沒有留言:
張貼留言