I have a module that should have a @property
, I solved this by setting a class as the module. I got the idea from this answer: Lazy module variables--can it be done?
I wanted this to be repeatable and easy to use so I made a metaclass for it. This works like a charm.
The problem is that when using Sphinx to generate documentation properties don't get documented. Everything else is documented as expected. I have no idea how to fix this, maybe this is a problem with Sphinx?
The module:
import sys
import types
class ClassAsModule(type):
def __new__(cls, name, bases, attrs):
# Make sure the name of the class is the module name.
name = attrs.pop('__module__')
# Create a class.
cls = type.__new__(cls, name, bases, attrs)
# Instantiate the class and register it.
sys.modules[name] = cls = cls(name)
# Update the dict so dir works properly
cls.__dict__.update(attrs)
class TestClass(types.ModuleType):
"""TestClass docstring."""
__metaclass__ = ClassAsModule
@property
def some_property(self):
"""Property docstring."""
pass
def meth():
"""meth doc"""
pass
And a copy-paste to generate/view Sphinx documentation:
sphinx-apidoc . -o doc --full
sphinx-build doc html
xdg-open html/module.html
The most essential part is to document the class' properties. Bonus points to also document original module members.
EDIT: The class should be documented as the module it is in. The class is used this way and should thus appear this way in Sphinx.
Example of desired output:
Module Foo
TestClass docstring.
some_property
Property docstring.
meth()
meth doc
EDIT 2: I found something that may aid in finding a solution. When having a regular module foo
with the following content:
#: Property of foo
prop = 'test'
Sphinx documents this like:
foo.prop = 'test'
Property of foo
The same works if prop
is an attribute of a class. I haven't figured out why it doesn't work in my special case.
Here's my understanding.
The theory is: making a mutant your class act like a module this (a bit hacky) way makes sphinx think that he doesn't need (to parse) properties from modules (because it's a class-level paradigm). So, for sphinx, TestClass
is a module.
First of all, to make sure that the culprit is the code for making a class act like a module - let's remove it:
class ClassAsModule(type):
pass
we'll see in docs:
package Package
script Module
class package.script.ClassAsModule
Bases: type
class package.script.TestClass
Bases: module
TestClass docstring.
meth()
meth doc
some_property
Property docstring.
As you see, sphinx read the property without any problems. Nothing special here.
Possible solution for your problem is to avoid using @property
decorator and replace it with calling property
class constructor. E.g.:
import sys
import types
class ClassAsModule(type):
def __new__(cls, name, bases, attrs):
# Make sure the name of the class is the module name.
name = attrs.pop('__module__')
# Create a class.
cls = type.__new__(cls, name, bases, attrs)
# Instantiate the class and register it.
sys.modules[name] = cls = cls(name)
# Update the dict so dir works properly
cls.__dict__.update(attrs)
class TestClass(types.ModuleType):
"""TestClass docstring."""
__metaclass__ = ClassAsModule
def get_some_property(self):
"""Property docstring."""
pass
some_property = property(get_some_property)
def meth(self):
"""meth doc"""
pass
For this code sphinx generates:
package Package
script Module
TestClass docstring.
package.script.get_some_property(self)
Property docstring.
package.script.meth(self)
meth doc
May be the answer is a piece of nonsense, but I hope it'll point you to the right direction.
The way I've found that works best is to keep the file contents the same as if you were writing a regular module, then at the end replace the embryonic module in sys.modules
:
"""Module docstring. """
import sys
import types
def _some_property(self):
pass
some_property = property(_some_property)
"""Property docstring."""
def meth():
"""meth doc"""
pass
def _make_class_module(name):
mod = sys.modules[name]
cls = type('ClassModule', (types.ModuleType,), mod.__dict__)
clsmod = cls(name)
clsmod.__dict__.update(mod.__dict__)
clsmod.__wrapped__ = mod
sys.modules[name] = clsmod
_make_class_module(__name__)
Text documentation:
mymod Module
************
Module docstring.
mymod.meth()
meth doc
mymod.some_property = None
Property docstring.
For the version of Sphinx I'm using (v1.1.3), it looks like you have to apply the property constructor explicitly (you can't use it as a decorator), and the docstring has to go in the file at the top level, on the line after the constructor call that creates the property (it doesn't work as a docstring inside the property getter). The source is still fairly readable, though.
来源:https://stackoverflow.com/questions/15896707/sphinx-document-module-properties