• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Documentation control decorators."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21_DO_NOT_DOC = "_tf_docs_do_not_document"
22
23
24def do_not_generate_docs(obj):
25  """A decorator: Do not generate docs for this object.
26
27  For example the following classes:
28
29  ```
30  class Parent(object):
31    def method1(self):
32      pass
33    def method2(self):
34      pass
35
36  class Child(Parent):
37    def method1(self):
38      pass
39    def method2(self):
40      pass
41  ```
42
43  Produce the following api_docs:
44
45  ```
46  /Parent.md
47    # method1
48    # method2
49  /Child.md
50    # method1
51    # method2
52  ```
53
54  This decorator allows you to skip classes or methods:
55
56  ```
57  @do_not_generate_docs
58  class Parent(object):
59    def method1(self):
60      pass
61    def method2(self):
62      pass
63
64  class Child(Parent):
65    @do_not_generate_docs
66    def method1(self):
67      pass
68    def method2(self):
69      pass
70  ```
71
72  This will only produce the following docs:
73
74  ```
75  /Child.md
76    # method2
77  ```
78
79  Note: This is implemented by adding a hidden attribute on the object, so it
80  cannot be used on objects which do not allow new attributes to be added. So
81  this decorator must go *below* `@property`, `@classmethod`,
82  or `@staticmethod`:
83
84  ```
85  class Example(object):
86    @property
87    @do_not_generate_docs
88    def x(self):
89      return self._x
90  ```
91
92  Args:
93    obj: The object to hide from the generated docs.
94
95  Returns:
96    obj
97  """
98  setattr(obj, _DO_NOT_DOC, None)
99  return obj
100
101
102_DO_NOT_DOC_INHERITABLE = "_tf_docs_do_not_doc_inheritable"
103
104
105def do_not_doc_inheritable(obj):
106  """A decorator: Do not generate docs for this method.
107
108  This version of the decorator is "inherited" by subclasses. No docs will be
109  generated for the decorated method in any subclass. Even if the sub-class
110  overrides the method.
111
112  For example, to ensure that `method1` is **never documented** use this
113  decorator on the base-class:
114
115  ```
116  class Parent(object):
117    @do_not_doc_inheritable
118    def method1(self):
119      pass
120    def method2(self):
121      pass
122
123  class Child(Parent):
124    def method1(self):
125      pass
126    def method2(self):
127      pass
128  ```
129  This will produce the following docs:
130
131  ```
132  /Parent.md
133    # method2
134  /Child.md
135    # method2
136  ```
137
138  When generating docs for a class's arributes, the `__mro__` is searched and
139  the attribute will be skipped if this decorator is detected on the attribute
140  on any class in the `__mro__`.
141
142  Note: This is implemented by adding a hidden attribute on the object, so it
143  cannot be used on objects which do not allow new attributes to be added. So
144  this decorator must go *below* `@property`, `@classmethod`,
145  or `@staticmethod`:
146
147  ```
148  class Example(object):
149    @property
150    @do_not_doc_inheritable
151    def x(self):
152      return self._x
153  ```
154
155  Args:
156    obj: The class-attribute to hide from the generated docs.
157
158  Returns:
159    obj
160  """
161  setattr(obj, _DO_NOT_DOC_INHERITABLE, None)
162  return obj
163
164
165_FOR_SUBCLASS_IMPLEMENTERS = "_tf_docs_tools_for_subclass_implementers"
166
167
168def for_subclass_implementers(obj):
169  """A decorator: Only generate docs for this method in the defining class.
170
171  Also group this method's docs with and `@abstractmethod` in the class's docs.
172
173  No docs will generated for this class attribute in sub-classes.
174
175  The canonical use case for this is `tf.keras.layers.Layer.call`: It's a
176  public method, essential for anyone implementing a subclass, but it should
177  never be called directly.
178
179  Works on method, or other class-attributes.
180
181  When generating docs for a class's arributes, the `__mro__` is searched and
182  the attribute will be skipped if this decorator is detected on the attribute
183  on any **parent** class in the `__mro__`.
184
185  For example:
186
187  ```
188  class Parent(object):
189    @for_subclass_implementers
190    def method1(self):
191      pass
192    def method2(self):
193      pass
194
195  class Child1(Parent):
196    def method1(self):
197      pass
198    def method2(self):
199      pass
200
201  class Child2(Parent):
202    def method1(self):
203      pass
204    def method2(self):
205      pass
206  ```
207
208  This will produce the following docs:
209
210  ```
211  /Parent.md
212    # method1
213    # method2
214  /Child1.md
215    # method2
216  /Child2.md
217    # method2
218  ```
219
220  Note: This is implemented by adding a hidden attribute on the object, so it
221  cannot be used on objects which do not allow new attributes to be added. So
222  this decorator must go *below* `@property`, `@classmethod`,
223  or `@staticmethod`:
224
225  ```
226  class Example(object):
227    @property
228    @for_subclass_implementers
229    def x(self):
230      return self._x
231  ```
232
233  Args:
234    obj: The class-attribute to hide from the generated docs.
235
236  Returns:
237    obj
238  """
239  setattr(obj, _FOR_SUBCLASS_IMPLEMENTERS, None)
240  return obj
241
242
243def should_skip(obj):
244  """Returns true if docs generation should be skipped for this object.
245
246  checks for the `do_not_generate_docs` or `do_not_doc_inheritable` decorators.
247
248  Args:
249    obj: The object to document, or skip.
250
251  Returns:
252    True if the object should be skipped
253  """
254  # Unwrap fget if the object is a property
255  if isinstance(obj, property):
256    obj = obj.fget
257
258  return hasattr(obj, _DO_NOT_DOC) or hasattr(obj, _DO_NOT_DOC_INHERITABLE)
259
260
261def should_skip_class_attr(cls, name):
262  """Returns true if docs should be skipped for this class attribute.
263
264  Args:
265    cls: The class the attribute belongs to.
266    name: The name of the attribute.
267
268  Returns:
269    True if the attribute should be skipped.
270  """
271  # Get the object with standard lookup, from the nearest
272  # defining parent.
273  try:
274    obj = getattr(cls, name)
275  except AttributeError:
276    # Avoid error caused by enum metaclasses in python3
277    if name in ("name", "value"):
278      return True
279    raise
280
281  # Unwrap fget if the object is a property
282  if isinstance(obj, property):
283    obj = obj.fget
284
285  # Skip if the object is decorated with `do_not_generate_docs` or
286  # `do_not_doc_inheritable`
287  if should_skip(obj):
288    return True
289
290  # Use __dict__ lookup to get the version defined in *this* class.
291  obj = cls.__dict__.get(name, None)
292  if isinstance(obj, property):
293    obj = obj.fget
294  if obj is not None:
295    # If not none, the object is defined in *this* class.
296    # Do not skip if decorated with `for_subclass_implementers`.
297    if hasattr(obj, _FOR_SUBCLASS_IMPLEMENTERS):
298      return False
299
300  # for each parent class
301  for parent in cls.__mro__[1:]:
302    obj = getattr(parent, name, None)
303
304    if obj is None:
305      continue
306
307    if isinstance(obj, property):
308      obj = obj.fget
309
310    # Skip if the parent's definition is decorated with `do_not_doc_inheritable`
311    # or `for_subclass_implementers`
312    if hasattr(obj, _DO_NOT_DOC_INHERITABLE):
313      return True
314
315    if hasattr(obj, _FOR_SUBCLASS_IMPLEMENTERS):
316      return True
317
318  # No blockng decorators --> don't skip
319  return False
320