aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/tools/docs/doc_controls.py
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/tools/docs/doc_controls.py')
-rw-r--r--tensorflow/tools/docs/doc_controls.py319
1 files changed, 319 insertions, 0 deletions
diff --git a/tensorflow/tools/docs/doc_controls.py b/tensorflow/tools/docs/doc_controls.py
new file mode 100644
index 0000000000..5e526443cc
--- /dev/null
+++ b/tensorflow/tools/docs/doc_controls.py
@@ -0,0 +1,319 @@
+# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+"""Documentation control decorators."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+_DO_NOT_DOC = "_tf_docs_do_not_document"
+
+
+def do_not_generate_docs(obj):
+ """A decorator: Do not generate docs for this object.
+
+ For example the following classes:
+
+ ```
+ class Parent(object):
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+
+ class Child(Parent):
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+ ```
+
+ Produce the following api_docs:
+
+ ```
+ /Parent.md
+ # method1
+ # method2
+ /Child.md
+ # method1
+ # method2
+ ```
+
+ This decorator allows you to skip classes or methods:
+
+ ```
+ @do_not_generate_docs
+ class Parent(object):
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+
+ class Child(Parent):
+ @do_not_generate_docs
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+ ```
+
+ This will only produce the following docs:
+
+ ```
+ /Child.md
+ # method2
+ ```
+
+ Note: This is implemented by adding a hidden attribute on the object, so it
+ cannot be used on objects which do not allow new attributes to be added. So
+ this decorator must go *below* `@property`, `@classmethod`,
+ or `@staticmethod`:
+
+ ```
+ class Example(object):
+ @property
+ @do_not_generate_docs
+ def x(self):
+ return self._x
+ ```
+
+ Args:
+ obj: The object to hide from the generated docs.
+
+ Returns:
+ obj
+ """
+ setattr(obj, _DO_NOT_DOC, None)
+ return obj
+
+
+_DO_NOT_DOC_INHERITABLE = "_tf_docs_do_not_doc_inheritable"
+
+
+def do_not_doc_inheritable(obj):
+ """A decorator: Do not generate docs for this method.
+
+ This version of the decorator is "inherited" by subclasses. No docs will be
+ generated for the decorated method in any subclass. Even if the sub-class
+ overrides the method.
+
+ For example, to ensure that `method1` is **never documented** use this
+ decorator on the base-class:
+
+ ```
+ class Parent(object):
+ @do_not_doc_inheritable
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+
+ class Child(Parent):
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+ ```
+ This will produce the following docs:
+
+ ```
+ /Parent.md
+ # method2
+ /Child.md
+ # method2
+ ```
+
+ When generating docs for a class's arributes, the `__mro__` is searched and
+ the attribute will be skipped if this decorator is detected on the attribute
+ on any class in the `__mro__`.
+
+ Note: This is implemented by adding a hidden attribute on the object, so it
+ cannot be used on objects which do not allow new attributes to be added. So
+ this decorator must go *below* `@property`, `@classmethod`,
+ or `@staticmethod`:
+
+ ```
+ class Example(object):
+ @property
+ @do_not_doc_inheritable
+ def x(self):
+ return self._x
+ ```
+
+ Args:
+ obj: The class-attribute to hide from the generated docs.
+
+ Returns:
+ obj
+ """
+ setattr(obj, _DO_NOT_DOC_INHERITABLE, None)
+ return obj
+
+
+_FOR_SUBCLASS_IMPLEMENTERS = "_tf_docs_tools_for_subclass_implementers"
+
+
+def for_subclass_implementers(obj):
+ """A decorator: Only generate docs for this method in the defining class.
+
+ Also group this method's docs with and `@abstractmethod` in the class's docs.
+
+ No docs will generated for this class attribute in sub-classes.
+
+ The canonical use case for this is `tf.keras.layers.Layer.call`: It's a
+ public method, essential for anyone implementing a subclass, but it should
+ never be called directly.
+
+ Works on method, or other class-attributes.
+
+ When generating docs for a class's arributes, the `__mro__` is searched and
+ the attribute will be skipped if this decorator is detected on the attribute
+ on any **parent** class in the `__mro__`.
+
+ For example:
+
+ ```
+ class Parent(object):
+ @for_subclass_implementers
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+
+ class Child1(Parent):
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+
+ class Child2(Parent):
+ def method1(self):
+ pass
+ def method2(self):
+ pass
+ ```
+
+ This will produce the following docs:
+
+ ```
+ /Parent.md
+ # method1
+ # method2
+ /Child1.md
+ # method2
+ /Child2.md
+ # method2
+ ```
+
+ Note: This is implemented by adding a hidden attribute on the object, so it
+ cannot be used on objects which do not allow new attributes to be added. So
+ this decorator must go *below* `@property`, `@classmethod`,
+ or `@staticmethod`:
+
+ ```
+ class Example(object):
+ @property
+ @for_subclass_implementers
+ def x(self):
+ return self._x
+ ```
+
+ Args:
+ obj: The class-attribute to hide from the generated docs.
+
+ Returns:
+ obj
+ """
+ setattr(obj, _FOR_SUBCLASS_IMPLEMENTERS, None)
+ return obj
+
+
+def should_skip(obj):
+ """Returns true if docs generation should be skipped for this object.
+
+ checks for the `do_not_generate_docs` or `do_not_doc_inheritable` decorators.
+
+ Args:
+ obj: The object to document, or skip.
+
+ Returns:
+ True if the object should be skipped
+ """
+ # Unwrap fget if the object is a property
+ if isinstance(obj, property):
+ obj = obj.fget
+
+ return hasattr(obj, _DO_NOT_DOC) or hasattr(obj, _DO_NOT_DOC_INHERITABLE)
+
+
+def should_skip_class_attr(cls, name):
+ """Returns true if docs should be skipped for this class attribute.
+
+ Args:
+ cls: The class the attribute belongs to.
+ name: The name of the attribute.
+
+ Returns:
+ True if the attribute should be skipped.
+ """
+ # Get the object with standard lookup, from the nearest
+ # defining parent.
+ try:
+ obj = getattr(cls, name)
+ except AttributeError:
+ # Avoid error caused by enum metaclasses in python3
+ if name in ("name", "value"):
+ return True
+ raise
+
+ # Unwrap fget if the object is a property
+ if isinstance(obj, property):
+ obj = obj.fget
+
+ # Skip if the object is decorated with `do_not_generate_docs` or
+ # `do_not_doc_inheritable`
+ if should_skip(obj):
+ return True
+
+ # Use __dict__ lookup to get the version defined in *this* class.
+ obj = cls.__dict__.get(name, None)
+ if isinstance(obj, property):
+ obj = obj.fget
+ if obj is not None:
+ # If not none, the object is defined in *this* class.
+ # Do not skip if decorated with `for_subclass_implementers`.
+ if hasattr(obj, _FOR_SUBCLASS_IMPLEMENTERS):
+ return False
+
+ # for each parent class
+ for parent in cls.__mro__[1:]:
+ obj = getattr(parent, name, None)
+
+ if obj is None:
+ continue
+
+ if isinstance(obj, property):
+ obj = obj.fget
+
+ # Skip if the parent's definition is decorated with `do_not_doc_inheritable`
+ # or `for_subclass_implementers`
+ if hasattr(obj, _DO_NOT_DOC_INHERITABLE):
+ return True
+
+ if hasattr(obj, _FOR_SUBCLASS_IMPLEMENTERS):
+ return True
+
+ # No blockng decorators --> don't skip
+ return False