cs-py-doc 20250426__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cs_py_doc-20250426/PKG-INFO +101 -0
- cs_py_doc-20250426/pyproject.toml +126 -0
- cs_py_doc-20250426/src/cs/py/doc.py +234 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cs-py-doc
|
|
3
|
+
Version: 20250426
|
|
4
|
+
Summary: Create documentation from python modules and other objects.
|
|
5
|
+
Keywords: python2,python3
|
|
6
|
+
Author-email: Cameron Simpson <cs@cskk.id.au>
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
15
|
+
Requires-Dist: cs.lex>=20250414
|
|
16
|
+
Requires-Dist: cs.logutils>=20250323
|
|
17
|
+
Requires-Dist: cs.pfx>=20250308
|
|
18
|
+
Requires-Dist: cs.py.modules>=20241122
|
|
19
|
+
Project-URL: MonoRepo Commits, https://bitbucket.org/cameron_simpson/css/commits/branch/main
|
|
20
|
+
Project-URL: Monorepo Git Mirror, https://github.com/cameron-simpson/css
|
|
21
|
+
Project-URL: Monorepo Hg/Mercurial Mirror, https://hg.sr.ht/~cameron-simpson/css
|
|
22
|
+
Project-URL: Source, https://github.com/cameron-simpson/css/blob/main/lib/python/cs/py/doc.py
|
|
23
|
+
|
|
24
|
+
Create documentation from python modules and other objects.
|
|
25
|
+
|
|
26
|
+
*Latest release 20250426*:
|
|
27
|
+
* module_doc: new doc_item inner function to format an item, now using a list instead of a heading - more compact and readable.
|
|
28
|
+
* module_doc: restore mangled command usage.
|
|
29
|
+
* module_doc: provide a short summary of every module top level name before the full docs.
|
|
30
|
+
|
|
31
|
+
Short summary:
|
|
32
|
+
* `is_dunder`: Test whether a name is a dunder name (`__`*foo*`__`).
|
|
33
|
+
* `module_doc`: Fetch the docstrings from a module and assemble a MarkDown document.
|
|
34
|
+
* `obj_docstring`: Return a docstring for `obj` which has been passed through `stripped_dedent`.
|
|
35
|
+
|
|
36
|
+
Module contents:
|
|
37
|
+
- <a name="is_dunder"></a>`is_dunder(name)`: Test whether a name is a dunder name (`__`*foo*`__`).
|
|
38
|
+
- <a name="module_doc"></a>`module_doc(module, *, sort_key=<function <lambda> at 0x10a7ec220>, filter_key=<function <lambda> at 0x10a7ec720>, method_names=None)`: Fetch the docstrings from a module and assemble a MarkDown document.
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
* `module`: the module or module name to inspect
|
|
42
|
+
* `sort_key`: optional key for sorting names in the documentation;
|
|
43
|
+
default: `name`
|
|
44
|
+
* filter_key`: optional test for a key used to select or reject keys
|
|
45
|
+
to appear in the documentation
|
|
46
|
+
* `method_names`: optional list of method names to document;
|
|
47
|
+
the default is to document `__init__`, then CONSTANTS, the
|
|
48
|
+
dunders, then other public names
|
|
49
|
+
- <a name="obj_docstring"></a>`obj_docstring(obj)`: Return a docstring for `obj` which has been passed through `stripped_dedent`.
|
|
50
|
+
|
|
51
|
+
This function uses `obj.__doc__` if it is not `None`,
|
|
52
|
+
otherwise `getcomments(obj)` if that is not `None`,
|
|
53
|
+
otherwise `''`.
|
|
54
|
+
The chosen string is passed through `stripped_dedent` before return.
|
|
55
|
+
|
|
56
|
+
# Release Log
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
*Release 20250426*:
|
|
61
|
+
* module_doc: new doc_item inner function to format an item, now using a list instead of a heading - more compact and readable.
|
|
62
|
+
* module_doc: restore mangled command usage.
|
|
63
|
+
* module_doc: provide a short summary of every module top level name before the full docs.
|
|
64
|
+
|
|
65
|
+
*Release 20241007*:
|
|
66
|
+
* module_doc: for subclasses of cs.fsm.FSM embed an SVG state transition diagram.
|
|
67
|
+
* module_doc: put HTML named anchors on the headings.
|
|
68
|
+
* module_doc: use BaseCommand.extract_usage() with BaseCOmmand subclasses.
|
|
69
|
+
|
|
70
|
+
*Release 20240709*:
|
|
71
|
+
module_doc: do not insert a BaseCommand usage into the docs, the BaseCommand.__init_subclass__ will be doing that for us.
|
|
72
|
+
|
|
73
|
+
*Release 20240630.1*:
|
|
74
|
+
module_doc: build the usage message from an instance of the baseCommand, needed since the last cs.cmdutils release.
|
|
75
|
+
|
|
76
|
+
*Release 20240630*:
|
|
77
|
+
module_doc: insert the class usage message for subclasses of BaseCommand.
|
|
78
|
+
|
|
79
|
+
*Release 20240422*:
|
|
80
|
+
module_doc: only list things in __all__ if provided.
|
|
81
|
+
|
|
82
|
+
*Release 20240412*:
|
|
83
|
+
module_doc: classes: MRO: suppress classes which are not immediate superclasses.
|
|
84
|
+
|
|
85
|
+
*Release 20220311*:
|
|
86
|
+
module_doc: class members no longer rendered as headings, too verbose.
|
|
87
|
+
|
|
88
|
+
*Release 20210306*:
|
|
89
|
+
Drop noise leaked into output.
|
|
90
|
+
|
|
91
|
+
*Release 20210123*:
|
|
92
|
+
* module_doc: include properties/descriptors.
|
|
93
|
+
* DISTINFO: this is not Python 2 compatible, drop tag.
|
|
94
|
+
|
|
95
|
+
*Release 20200718*:
|
|
96
|
+
* New is_dunder(name) function to test whether name is a dunder name.
|
|
97
|
+
* module_doc: new method_names parameter to report only specific attributes from a class - default is all public names and most dunder methods - things without docs are not reported.
|
|
98
|
+
* Assorted small changes.
|
|
99
|
+
|
|
100
|
+
*Release 20200521*:
|
|
101
|
+
Initial PyPI release.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cs-py-doc"
|
|
3
|
+
description = "Create documentation from python modules and other objects."
|
|
4
|
+
authors = [
|
|
5
|
+
{ name = "Cameron Simpson", email = "cs@cskk.id.au" },
|
|
6
|
+
]
|
|
7
|
+
keywords = [
|
|
8
|
+
"python2",
|
|
9
|
+
"python3",
|
|
10
|
+
]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"cs.lex>=20250414",
|
|
13
|
+
"cs.logutils>=20250323",
|
|
14
|
+
"cs.pfx>=20250308",
|
|
15
|
+
"cs.py.modules>=20241122",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
25
|
+
]
|
|
26
|
+
version = "20250426"
|
|
27
|
+
|
|
28
|
+
[project.license]
|
|
29
|
+
text = "GNU General Public License v3 or later (GPLv3+)"
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
"Monorepo Hg/Mercurial Mirror" = "https://hg.sr.ht/~cameron-simpson/css"
|
|
33
|
+
"Monorepo Git Mirror" = "https://github.com/cameron-simpson/css"
|
|
34
|
+
"MonoRepo Commits" = "https://bitbucket.org/cameron_simpson/css/commits/branch/main"
|
|
35
|
+
Source = "https://github.com/cameron-simpson/css/blob/main/lib/python/cs/py/doc.py"
|
|
36
|
+
|
|
37
|
+
[project.readme]
|
|
38
|
+
text = """
|
|
39
|
+
Create documentation from python modules and other objects.
|
|
40
|
+
|
|
41
|
+
*Latest release 20250426*:
|
|
42
|
+
* module_doc: new doc_item inner function to format an item, now using a list instead of a heading - more compact and readable.
|
|
43
|
+
* module_doc: restore mangled command usage.
|
|
44
|
+
* module_doc: provide a short summary of every module top level name before the full docs.
|
|
45
|
+
|
|
46
|
+
Short summary:
|
|
47
|
+
* `is_dunder`: Test whether a name is a dunder name (`__`*foo*`__`).
|
|
48
|
+
* `module_doc`: Fetch the docstrings from a module and assemble a MarkDown document.
|
|
49
|
+
* `obj_docstring`: Return a docstring for `obj` which has been passed through `stripped_dedent`.
|
|
50
|
+
|
|
51
|
+
Module contents:
|
|
52
|
+
- <a name=\"is_dunder\"></a>`is_dunder(name)`: Test whether a name is a dunder name (`__`*foo*`__`).
|
|
53
|
+
- <a name=\"module_doc\"></a>`module_doc(module, *, sort_key=<function <lambda> at 0x10a7ec220>, filter_key=<function <lambda> at 0x10a7ec720>, method_names=None)`: Fetch the docstrings from a module and assemble a MarkDown document.
|
|
54
|
+
|
|
55
|
+
Parameters:
|
|
56
|
+
* `module`: the module or module name to inspect
|
|
57
|
+
* `sort_key`: optional key for sorting names in the documentation;
|
|
58
|
+
default: `name`
|
|
59
|
+
* filter_key`: optional test for a key used to select or reject keys
|
|
60
|
+
to appear in the documentation
|
|
61
|
+
* `method_names`: optional list of method names to document;
|
|
62
|
+
the default is to document `__init__`, then CONSTANTS, the
|
|
63
|
+
dunders, then other public names
|
|
64
|
+
- <a name=\"obj_docstring\"></a>`obj_docstring(obj)`: Return a docstring for `obj` which has been passed through `stripped_dedent`.
|
|
65
|
+
|
|
66
|
+
This function uses `obj.__doc__` if it is not `None`,
|
|
67
|
+
otherwise `getcomments(obj)` if that is not `None`,
|
|
68
|
+
otherwise `''`.
|
|
69
|
+
The chosen string is passed through `stripped_dedent` before return.
|
|
70
|
+
|
|
71
|
+
# Release Log
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
*Release 20250426*:
|
|
76
|
+
* module_doc: new doc_item inner function to format an item, now using a list instead of a heading - more compact and readable.
|
|
77
|
+
* module_doc: restore mangled command usage.
|
|
78
|
+
* module_doc: provide a short summary of every module top level name before the full docs.
|
|
79
|
+
|
|
80
|
+
*Release 20241007*:
|
|
81
|
+
* module_doc: for subclasses of cs.fsm.FSM embed an SVG state transition diagram.
|
|
82
|
+
* module_doc: put HTML named anchors on the headings.
|
|
83
|
+
* module_doc: use BaseCommand.extract_usage() with BaseCOmmand subclasses.
|
|
84
|
+
|
|
85
|
+
*Release 20240709*:
|
|
86
|
+
module_doc: do not insert a BaseCommand usage into the docs, the BaseCommand.__init_subclass__ will be doing that for us.
|
|
87
|
+
|
|
88
|
+
*Release 20240630.1*:
|
|
89
|
+
module_doc: build the usage message from an instance of the baseCommand, needed since the last cs.cmdutils release.
|
|
90
|
+
|
|
91
|
+
*Release 20240630*:
|
|
92
|
+
module_doc: insert the class usage message for subclasses of BaseCommand.
|
|
93
|
+
|
|
94
|
+
*Release 20240422*:
|
|
95
|
+
module_doc: only list things in __all__ if provided.
|
|
96
|
+
|
|
97
|
+
*Release 20240412*:
|
|
98
|
+
module_doc: classes: MRO: suppress classes which are not immediate superclasses.
|
|
99
|
+
|
|
100
|
+
*Release 20220311*:
|
|
101
|
+
module_doc: class members no longer rendered as headings, too verbose.
|
|
102
|
+
|
|
103
|
+
*Release 20210306*:
|
|
104
|
+
Drop noise leaked into output.
|
|
105
|
+
|
|
106
|
+
*Release 20210123*:
|
|
107
|
+
* module_doc: include properties/descriptors.
|
|
108
|
+
* DISTINFO: this is not Python 2 compatible, drop tag.
|
|
109
|
+
|
|
110
|
+
*Release 20200718*:
|
|
111
|
+
* New is_dunder(name) function to test whether name is a dunder name.
|
|
112
|
+
* module_doc: new method_names parameter to report only specific attributes from a class - default is all public names and most dunder methods - things without docs are not reported.
|
|
113
|
+
* Assorted small changes.
|
|
114
|
+
|
|
115
|
+
*Release 20200521*:
|
|
116
|
+
Initial PyPI release."""
|
|
117
|
+
content-type = "text/markdown"
|
|
118
|
+
|
|
119
|
+
[build-system]
|
|
120
|
+
build-backend = "flit_core.buildapi"
|
|
121
|
+
requires = [
|
|
122
|
+
"flit_core >=3.2,<4",
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
[tool.flit.module]
|
|
126
|
+
name = "cs.py.doc"
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
''' Create documentation from python modules and other objects.
|
|
4
|
+
'''
|
|
5
|
+
|
|
6
|
+
import abc
|
|
7
|
+
import importlib
|
|
8
|
+
from inspect import (
|
|
9
|
+
getcomments,
|
|
10
|
+
getmodule,
|
|
11
|
+
isclass,
|
|
12
|
+
isdatadescriptor,
|
|
13
|
+
isfunction,
|
|
14
|
+
ismethod,
|
|
15
|
+
signature,
|
|
16
|
+
)
|
|
17
|
+
from itertools import chain
|
|
18
|
+
|
|
19
|
+
from cs.fsm import FSM
|
|
20
|
+
from cs.gvutils import gvdataurl, GVDATAURL, gvsvg
|
|
21
|
+
from cs.lex import cutprefix, stripped_dedent, indent
|
|
22
|
+
from cs.logutils import warning
|
|
23
|
+
from cs.pfx import Pfx, pfx_call
|
|
24
|
+
from cs.py.modules import module_attributes
|
|
25
|
+
|
|
26
|
+
__version__ = '20250426'
|
|
27
|
+
|
|
28
|
+
DISTINFO = {
|
|
29
|
+
'keywords': ["python2", "python3"],
|
|
30
|
+
'classifiers': [
|
|
31
|
+
"Programming Language :: Python",
|
|
32
|
+
"Programming Language :: Python :: 3",
|
|
33
|
+
],
|
|
34
|
+
'install_requires': [
|
|
35
|
+
'cs.lex',
|
|
36
|
+
'cs.logutils',
|
|
37
|
+
'cs.pfx',
|
|
38
|
+
'cs.py.modules',
|
|
39
|
+
],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def is_dunder(name):
|
|
43
|
+
''' Test whether a name is a dunder name (`__`*foo*`__`).
|
|
44
|
+
'''
|
|
45
|
+
return len(name) > 4 and name.startswith('__') and name.endswith('__')
|
|
46
|
+
|
|
47
|
+
def module_doc(
|
|
48
|
+
module,
|
|
49
|
+
*,
|
|
50
|
+
sort_key=lambda item: item[0].lower(),
|
|
51
|
+
filter_key=lambda key: key != 'DISTINFO' and not key.startswith('_'),
|
|
52
|
+
method_names=None,
|
|
53
|
+
):
|
|
54
|
+
''' Fetch the docstrings from a module and assemble a MarkDown document.
|
|
55
|
+
|
|
56
|
+
Parameters:
|
|
57
|
+
* `module`: the module or module name to inspect
|
|
58
|
+
* `sort_key`: optional key for sorting names in the documentation;
|
|
59
|
+
default: `name`
|
|
60
|
+
* filter_key`: optional test for a key used to select or reject keys
|
|
61
|
+
to appear in the documentation
|
|
62
|
+
* `method_names`: optional list of method names to document;
|
|
63
|
+
the default is to document `__init__`, then CONSTANTS, the
|
|
64
|
+
dunders, then other public names
|
|
65
|
+
'''
|
|
66
|
+
from cs.cmdutils import BaseCommand
|
|
67
|
+
if isinstance(module, str):
|
|
68
|
+
module = pfx_call(importlib.import_module, module)
|
|
69
|
+
full_docs = [obj_docstring(module)]
|
|
70
|
+
ALL = getattr(module, '__all__', None)
|
|
71
|
+
|
|
72
|
+
def doc_item(anchor, header, obj_doc, nl="\n"):
|
|
73
|
+
## return f'\n\n## <a name="{anchor}"></a>`{header}`\n\n{obj_doc}'
|
|
74
|
+
list_item = f'{nl}- <a name="{anchor}"></a>`{str(header)}`: {stripped_dedent(obj_doc,sub_indent=" ")}'
|
|
75
|
+
return list_item
|
|
76
|
+
|
|
77
|
+
full_docs.append('\n\nShort summary:')
|
|
78
|
+
for Mname, obj in sorted(module_attributes(module), key=sort_key):
|
|
79
|
+
with Pfx(Mname):
|
|
80
|
+
if ALL and Mname not in ALL:
|
|
81
|
+
continue
|
|
82
|
+
if not filter_key(Mname):
|
|
83
|
+
continue
|
|
84
|
+
obj_module = getmodule(obj)
|
|
85
|
+
if obj_module is not module:
|
|
86
|
+
# name imported from another module
|
|
87
|
+
continue
|
|
88
|
+
docstring = getattr(obj, '__doc__', '').strip()
|
|
89
|
+
if not docstring:
|
|
90
|
+
continue
|
|
91
|
+
line1 = " ".join(
|
|
92
|
+
line.strip()
|
|
93
|
+
for line in docstring.split("\n\n")[0].split(". ")[0].split("\n")
|
|
94
|
+
)
|
|
95
|
+
if line1[0].isupper() and not line1.endswith('.'):
|
|
96
|
+
line1 += '.'
|
|
97
|
+
full_docs.append(f'\n* `{Mname}`: {line1}')
|
|
98
|
+
|
|
99
|
+
full_docs.append('\n\nModule contents:')
|
|
100
|
+
for Mname, obj in sorted(module_attributes(module), key=sort_key):
|
|
101
|
+
with Pfx(Mname):
|
|
102
|
+
if ALL and Mname not in ALL:
|
|
103
|
+
continue
|
|
104
|
+
if not filter_key(Mname):
|
|
105
|
+
continue
|
|
106
|
+
obj_module = getmodule(obj)
|
|
107
|
+
if obj_module is not module:
|
|
108
|
+
# name imported from another module
|
|
109
|
+
continue
|
|
110
|
+
assert obj_module
|
|
111
|
+
obj_doc = obj_docstring(obj) if obj_module else ''
|
|
112
|
+
if not callable(obj):
|
|
113
|
+
if obj_doc:
|
|
114
|
+
full_docs.append(doc_item(Mname, f'{Mname} = {obj!r}', obj_doc))
|
|
115
|
+
continue
|
|
116
|
+
if not obj_doc:
|
|
117
|
+
continue
|
|
118
|
+
if isfunction(obj):
|
|
119
|
+
sig = signature(obj)
|
|
120
|
+
full_docs.append(doc_item(Mname, f'{Mname}{sig}', obj_doc))
|
|
121
|
+
elif isclass(obj):
|
|
122
|
+
classname_etc = Mname
|
|
123
|
+
# compute the list of immediate superclass names
|
|
124
|
+
mro_names = []
|
|
125
|
+
mro_set = set(obj.__mro__)
|
|
126
|
+
for superclass in obj.__mro__:
|
|
127
|
+
if superclass not in mro_set:
|
|
128
|
+
continue
|
|
129
|
+
if (superclass is not object and superclass is not obj
|
|
130
|
+
and superclass is not abc.ABC):
|
|
131
|
+
supername = superclass.__name__
|
|
132
|
+
supermod = getmodule(superclass)
|
|
133
|
+
if supermod is not module:
|
|
134
|
+
supername = supermod.__name__ + '.' + supername
|
|
135
|
+
mro_names.append(supername)
|
|
136
|
+
mro_set.difference_update(superclass.__mro__)
|
|
137
|
+
if mro_names:
|
|
138
|
+
classname_etc += '(' + ', '.join(mro_names) + ')'
|
|
139
|
+
if issubclass(obj, FSM) and hasattr(obj, 'FSM_TRANSITIONS'):
|
|
140
|
+
# append an FSM state diagram
|
|
141
|
+
obj_doc += (
|
|
142
|
+
f'\n\nState diagram:\n,
|
|
147
|
+
fmt='svg',
|
|
148
|
+
dataurl_encoding='base64',
|
|
149
|
+
) + f' "{Mname} State Diagram")\n'
|
|
150
|
+
)
|
|
151
|
+
if issubclass(obj, BaseCommand):
|
|
152
|
+
# extract the Usage: paragraph if present, append a full usage
|
|
153
|
+
doc_without_usage, usage_text = obj.extract_usage()
|
|
154
|
+
obj_doc += ''.join(
|
|
155
|
+
(
|
|
156
|
+
doc_without_usage,
|
|
157
|
+
"\n\nUsage summary:\n\n",
|
|
158
|
+
indent("Usage: " + usage_text, " "),
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
full_docs.append(doc_item(Mname, f'Class `{classname_etc}', obj_doc))
|
|
162
|
+
seen_names = set()
|
|
163
|
+
direct_attrs = dict(obj.__dict__)
|
|
164
|
+
# iterate over specified names or default names in order
|
|
165
|
+
for attr_name in method_names or chain(
|
|
166
|
+
# constructor and initialiser
|
|
167
|
+
(
|
|
168
|
+
'__init__',),
|
|
169
|
+
# "constants"
|
|
170
|
+
sorted(filter(lambda name: name and name[0].isupper(),
|
|
171
|
+
direct_attrs)),
|
|
172
|
+
# dunder methods
|
|
173
|
+
sorted(filter(is_dunder, direct_attrs)),
|
|
174
|
+
# remaining attributes
|
|
175
|
+
sorted(filter(lambda name: name and not name.startswith('_'),
|
|
176
|
+
direct_attrs)),
|
|
177
|
+
):
|
|
178
|
+
# prevent repeats, as the automatic list is composed of
|
|
179
|
+
# overlapping components
|
|
180
|
+
if attr_name in seen_names:
|
|
181
|
+
continue
|
|
182
|
+
seen_names.add(attr_name)
|
|
183
|
+
if not method_names:
|
|
184
|
+
# prune some boring names
|
|
185
|
+
if attr_name in ('__abstractmethods__', '__doc__',
|
|
186
|
+
'__getnewargs__', '__module__', '__new__',
|
|
187
|
+
'__repr__', '__weakref__'):
|
|
188
|
+
continue
|
|
189
|
+
# prune private names which are not dunder names
|
|
190
|
+
if attr_name.startswith('_') and not is_dunder(attr_name):
|
|
191
|
+
continue
|
|
192
|
+
if attr_name not in direct_attrs:
|
|
193
|
+
##print(" skip, not in direct_attrs", direct_attrs)
|
|
194
|
+
continue
|
|
195
|
+
attr = getattr(obj, attr_name)
|
|
196
|
+
attr_doc = obj_docstring(attr)
|
|
197
|
+
if not attr_doc:
|
|
198
|
+
continue
|
|
199
|
+
# Class.name is a function, not a method
|
|
200
|
+
if ismethod(attr) or isfunction(attr):
|
|
201
|
+
method_sig = signature(attr)
|
|
202
|
+
full_docs.append(
|
|
203
|
+
f'\n\n*`{Mname}.{attr_name}{method_sig}`*:\n{attr_doc}'
|
|
204
|
+
)
|
|
205
|
+
elif isdatadescriptor(attr):
|
|
206
|
+
full_docs.append(f'\n\n*`{Mname}.{attr_name}`*:\n{attr_doc}')
|
|
207
|
+
elif not callable(attr):
|
|
208
|
+
pass
|
|
209
|
+
elif isinstance(attr, property):
|
|
210
|
+
full_docs.append(f'\n\n*`{Mname}.{attr_name}`*:\n{attr_doc}')
|
|
211
|
+
else:
|
|
212
|
+
full_docs.append(f'\n\n*`{Mname}.{attr_name}`*')
|
|
213
|
+
else:
|
|
214
|
+
warning("UNHANDLED %r, neither function nor class", Mname)
|
|
215
|
+
return ''.join(full_docs)
|
|
216
|
+
|
|
217
|
+
# TODO: use inspect.getdoc() initially
|
|
218
|
+
def obj_docstring(obj):
|
|
219
|
+
''' Return a docstring for `obj` which has been passed through `stripped_dedent`.
|
|
220
|
+
|
|
221
|
+
This function uses `obj.__doc__` if it is not `None`,
|
|
222
|
+
otherwise `getcomments(obj)` if that is not `None`,
|
|
223
|
+
otherwise `''`.
|
|
224
|
+
The chosen string is passed through `stripped_dedent` before return.
|
|
225
|
+
'''
|
|
226
|
+
docstring = getattr(obj, '__doc__', None)
|
|
227
|
+
if docstring is None:
|
|
228
|
+
docstring = '\n'.join(
|
|
229
|
+
map(
|
|
230
|
+
lambda line: cutprefix(line, '# '), (getcomments(obj)
|
|
231
|
+
or '').rstrip().split('\n')
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
return stripped_dedent(docstring)
|