Skip to content

Commit e56d417

Browse files
author
Benoit Fuentes
committed
include_special_method option in DocInheritMeta
1 parent 7995560 commit e56d417

File tree

4 files changed

+75
-15
lines changed

4 files changed

+75
-15
lines changed

README.md

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ class Parent(metaclass=DocInheritMeta(style="numpy")):
5353
----------
5454
x: int
5555
blah-x
56-
56+
5757
y: Optional[int]
5858
blah-y
59-
59+
6060
Raises
6161
------
6262
NotImplementedError"""
@@ -85,7 +85,7 @@ Because we specified `style="numpy"` in `DocInheritMeta`, the inherited docstrin
8585
----------
8686
x: int
8787
blah-x
88-
88+
8989
y: Optional[int]
9090
blah-y
9191
@@ -146,15 +146,27 @@ class Parent(metaclass=DocInheritMeta(style="numpy", abstract_base_class=True)):
146146

147147
For the "numpy", "google", and "napoleon_numpy" inheritance styles, one then only needs to specify the "Returns" or "Yields" section in the derived class' attribute docstring for it to have a fully-detailed docstring.
148148

149+
Another option is to be able to decide whether to include all special methods, meaning methods that start and
150+
end by "__" such as "__init__" method, or not in the doctstring inheritance process. Such an option can be pass
151+
to the `DocInheritMeta` metaclass constructor:
152+
153+
```python
154+
# Each child class will also merge the special methods' docstring of its parent class
155+
class Parent(metaclass=DocInheritMeta(style="numpy", include_special_methods=True)):
156+
...
157+
```
158+
159+
Special methods are not included by default.
160+
149161
## Built-in Styles
150162

151163
Utilize a built-in style by specifying any of the following names (as a string), wherever the `style` parameter is to be specified. The built-in styles are:
152164

153165
- `"parent"`: Wherever the docstring for a child-class' attribute (or for the class itself) is
154166
`None`, inherit the corresponding docstring from the parent. (Deprecated in Python 3.5)
155167

156-
- `"numpy"`: [NumPy-styled docstrings](https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt#docstring-standard)
157-
from the parent and child are merged gracefully with nice formatting. The child's docstring sections take precedence
168+
- `"numpy"`: [NumPy-styled docstrings](https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt#docstring-standard)
169+
from the parent and child are merged gracefully with nice formatting. The child's docstring sections take precedence
158170
in the case of overlap.
159171

160172
- `"numpy_with_merge"`: Behaves identically to the "numpy" style, but also merges sections that overlap,
@@ -179,7 +191,7 @@ Utilize a built-in style by specifying any of the following names (as a string),
179191
behaviour applies.
180192

181193
- `"reST"`: reST-styled docstrings from the parent and child are merged gracefully
182-
with nice formatting. Docstring sections are specified by
194+
with nice formatting. Docstring sections are specified by
183195
[reST section titles](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections).
184196
The child's docstring sections take precedence in the case of overlap.
185197

@@ -188,15 +200,15 @@ For the `numpy`, `numpy_with_merge`, `numpy_napoleon`, `numpy_napoleon_with_merg
188200
Detailed documentation and example cases for the default styles can be found [here](https://github.com/meowklaski/custom_inherit/blob/master/custom_inherit/_style_store.py)
189201

190202
## Making New Inheritance Styles
191-
Implementing your inheritance style is simple.
203+
Implementing your inheritance style is simple.
192204

193205
- Provide an inheritance style on the fly wherever a style parameter is specified:
194206
- Supply any function of the form: `func(prnt_doc: str, child_doc: str) -> str`
195207

196208
- Log an inheritance style, and refer to it by name wherever a style parameter is specified, using either:
197-
- `custom_inherit.store["my_style"] = func`
198-
- `custom_inherit.add_style("my_style", func)`.
199-
209+
- `custom_inherit.store["my_style"] = func`
210+
- `custom_inherit.add_style("my_style", func)`.
211+
200212
## Installation and Getting Started
201213
Install via pip:
202214

@@ -206,7 +218,7 @@ Install via pip:
206218
Install via conda:
207219

208220
```
209-
conda install -c conda-forge custom-inherit
221+
conda install -c conda-forge custom-inherit
210222
```
211223

212224
or
@@ -264,7 +276,7 @@ custom_inherit.doc_inherit(parent, style="parent"):
264276
Parameters
265277
----------
266278
parent : Union[str, Any]
267-
The object whose docstring, is utilized as the parent docstring
279+
The object whose docstring, is utilized as the parent docstring
268280
during the docstring merge. Or, a string can be provided directly.
269281
270282
style : Union[Hashable, Callable[[str, str], str]], optional (default: "parent")

src/custom_inherit/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def remove_style(style):
143143
store.pop(style)
144144

145145

146-
def DocInheritMeta(style="parent", abstract_base_class=False):
146+
def DocInheritMeta(style="parent", abstract_base_class=False, include_special_methods=False):
147147
""" A metaclass that merges the respective docstrings of a parent class and of its child, along with their
148148
properties, methods (including classmethod, staticmethod, decorated methods).
149149
@@ -159,13 +159,18 @@ def DocInheritMeta(style="parent", abstract_base_class=False):
159159
will be an abstract base class, whose derived classes will inherit docstrings
160160
using the numpy-style inheritance scheme.
161161
162+
include_special_methods: bool, optional (defaul: False)
163+
Wether special methods of class (i.e. starting en ending with "__") are included in the docstring
164+
inheritance process.
165+
162166
163167
Returns
164168
-------
165169
custom_inherit.DocInheritorBase"""
166170

167171
merge_func = store[style]
168172
metaclass = _DocInheritorBase
173+
metaclass.include_special_methods = include_special_methods
169174
metaclass.class_doc_inherit = staticmethod(merge_func)
170175
metaclass.attr_doc_inherit = staticmethod(merge_func)
171176

src/custom_inherit/_metaclass_base.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class DocInheritorBase(type):
2121
This merge-style must be implemented via the static methods `class_doc_inherit`
2222
and `attr_doc_inherit`, which are set within `custom_inherit.DocInheritMeta`."""
2323

24+
include_special_methods = False
25+
2426
def __new__(mcs, class_name, class_bases, class_dict):
2527
# inherit class docstring: the docstring is constructed by traversing
2628
# the mro for the class and merging their docstrings, with each next
@@ -42,8 +44,10 @@ def __new__(mcs, class_name, class_bases, class_dict):
4244
attribute,
4345
(FunctionType, MethodType, classmethod, staticmethod, property),
4446
)
45-
46-
if (attr.startswith("__") and attr.endswith("__")) or not is_doc_type:
47+
if (
48+
(attr.startswith("__") and attr.endswith("__") and not mcs.include_special_methods) or
49+
not is_doc_type
50+
):
4751
continue
4852

4953
is_static_or_class = isinstance(attribute, (staticmethod, classmethod))

tests/metaclass_test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ def absproperty(self):
5252

5353

5454
class Kid(Parent):
55+
def __init__(self):
56+
"""kid"""
57+
pass
58+
5559
def kid_method(self):
5660
"""kid"""
5761
pass
@@ -89,6 +93,12 @@ def test_sideeffect():
8993
assert signature(Kid.method) == signature(Parent.method)
9094

9195

96+
def test_special_method():
97+
# by default, Kid.__init__ docstring should not inherit from its parent
98+
assert isinstance(Kid().__init__, MethodType)
99+
assert getdoc(Kid.__init__) == "kid"
100+
101+
92102
def test_method():
93103
assert isinstance(Kid().method, MethodType)
94104
assert getdoc(Kid.method) == "valid"
@@ -145,6 +155,10 @@ def prop(self):
145155

146156

147157
class Kid2(Parent2):
158+
def __init__(self):
159+
"""kid"""
160+
pass
161+
148162
def kid_method(self):
149163
"""kid"""
150164
pass
@@ -170,6 +184,12 @@ def test_sideeffect2():
170184
assert signature(Kid2.method) == signature(Parent.method)
171185

172186

187+
def test_special_method2():
188+
# by default, Kid.__init__ docstring should not inherit from its parent
189+
assert isinstance(Kid2().__init__, MethodType)
190+
assert getdoc(Kid2.__init__) == "kid"
191+
192+
173193
def test_method2():
174194
assert isinstance(Kid2().method, MethodType)
175195
assert getdoc(Kid2.method) == "valid"
@@ -218,3 +238,22 @@ class Child(Mixin, Parent):
218238
getdoc(Child)
219239
== "This is mixin which does something.\n\nAttributes\n----------\nbar\n\nReturns\n-------\nfoo"
220240
)
241+
242+
243+
""" Include special method option"""
244+
245+
@add_metaclass(DocInheritMeta(style=style, include_special_methods=True))
246+
class Parent3(object):
247+
def __init__(self):
248+
""""""
249+
pass
250+
251+
class Kid3(Parent3):
252+
def __init__(self):
253+
"""kid"""
254+
pass
255+
256+
def test_special_method3():
257+
# __init__ docstring should inherit from Parent3
258+
assert isinstance(Kid3().__init__, MethodType)
259+
assert getdoc(Kid3.__init__) == "valid"

0 commit comments

Comments
 (0)