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 243do_not_doc_in_subclasses = for_subclass_implementers 244 245 246def should_skip(obj): 247 """Returns true if docs generation should be skipped for this object. 248 249 checks for the `do_not_generate_docs` or `do_not_doc_inheritable` decorators. 250 251 Args: 252 obj: The object to document, or skip. 253 254 Returns: 255 True if the object should be skipped 256 """ 257 # Unwrap fget if the object is a property 258 if isinstance(obj, property): 259 obj = obj.fget 260 261 return hasattr(obj, _DO_NOT_DOC) or hasattr(obj, _DO_NOT_DOC_INHERITABLE) 262 263 264def should_skip_class_attr(cls, name): 265 """Returns true if docs should be skipped for this class attribute. 266 267 Args: 268 cls: The class the attribute belongs to. 269 name: The name of the attribute. 270 271 Returns: 272 True if the attribute should be skipped. 273 """ 274 # Get the object with standard lookup, from the nearest 275 # defining parent. 276 try: 277 obj = getattr(cls, name) 278 except AttributeError: 279 # Avoid error caused by enum metaclasses in python3 280 if name in ("name", "value"): 281 return True 282 raise 283 284 # Unwrap fget if the object is a property 285 if isinstance(obj, property): 286 obj = obj.fget 287 288 # Skip if the object is decorated with `do_not_generate_docs` or 289 # `do_not_doc_inheritable` 290 if should_skip(obj): 291 return True 292 293 # Use __dict__ lookup to get the version defined in *this* class. 294 obj = cls.__dict__.get(name, None) 295 if isinstance(obj, property): 296 obj = obj.fget 297 if obj is not None: 298 # If not none, the object is defined in *this* class. 299 # Do not skip if decorated with `for_subclass_implementers`. 300 if hasattr(obj, _FOR_SUBCLASS_IMPLEMENTERS): 301 return False 302 303 # for each parent class 304 for parent in cls.__mro__[1:]: 305 obj = getattr(parent, name, None) 306 307 if obj is None: 308 continue 309 310 if isinstance(obj, property): 311 obj = obj.fget 312 313 # Skip if the parent's definition is decorated with `do_not_doc_inheritable` 314 # or `for_subclass_implementers` 315 if hasattr(obj, _DO_NOT_DOC_INHERITABLE): 316 return True 317 318 if hasattr(obj, _FOR_SUBCLASS_IMPLEMENTERS): 319 return True 320 321 # No blockng decorators --> don't skip 322 return False 323