-
-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathautodoc_enhancements.py
More file actions
86 lines (71 loc) · 3.07 KB
/
autodoc_enhancements.py
File metadata and controls
86 lines (71 loc) · 3.07 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""
Miscellaneous enhancements to help autodoc along.
"""
import warnings
from sphinx.ext.autodoc import AttributeDocumenter
__all__ = []
class_types = (type,)
MethodDescriptorType = type(type.__subclasses__)
# See
# https://github.com/astropy/astropy-helpers/issues/116#issuecomment-71254836
# for further background on this.
def type_object_attrgetter(obj, attr, *defargs):
"""
This implements an improved attrgetter for type objects (i.e. classes)
that can handle class attributes that are implemented as properties on
a metaclass.
Normally `getattr` on a class with a `property` (say, "foo"), would return
the `property` object itself. However, if the class has a metaclass which
*also* defines a `property` named "foo", ``getattr(cls, 'foo')`` will find
the "foo" property on the metaclass and resolve it. For the purposes of
autodoc we just want to document the "foo" property defined on the class,
not on the metaclass.
For example::
>>> class Meta(type):
... @property
... def foo(cls):
... return 'foo'
...
>>> class MyClass(metaclass=Meta):
... @property
... def foo(self):
... \"\"\"Docstring for MyClass.foo property.\"\"\"
... return 'myfoo'
...
>>> getattr(MyClass, 'foo')
'foo'
>>> type_object_attrgetter(MyClass, 'foo')
<property at 0x...>
>>> type_object_attrgetter(MyClass, 'foo').__doc__
'Docstring for MyClass.foo property.'
The last line of the example shows the desired behavior for the purposes
of autodoc.
"""
for base in obj.__mro__:
if attr in base.__dict__:
if isinstance(base.__dict__[attr], property):
# Note, this should only be used for properties--for any other
# type of descriptor (classmethod, for example) this can mess
# up existing expectations of what getattr(cls, ...) returns
return base.__dict__[attr]
break
# In some cases, getting attributes with getattr can lead to warnings, e.g.
# deprecation warnings (this is not normally the case with methods and
# regular properties since we don't execute them but using getattr does run
# the code inside those properties, so we filter out any warnings.
with warnings.catch_warnings(record=False):
warnings.simplefilter('ignore')
return getattr(obj, attr, *defargs)
def setup(app):
# Must have the autodoc extension set up first so we can override it
app.setup_extension('sphinx.ext.autodoc')
app.add_autodoc_attrgetter(type, type_object_attrgetter)
suppress_warnings_orig = app.config.suppress_warnings[:]
if 'app.add_directive' not in app.config.suppress_warnings:
app.config.suppress_warnings.append('app.add_directive')
try:
app.add_autodocumenter(AttributeDocumenter)
finally:
app.config.suppress_warnings = suppress_warnings_orig
return {'parallel_read_safe': True,
'parallel_write_safe': True}