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 typing import TypeVar 18 19T = TypeVar("T") 20 21 22_DEPRECATED = "_tf_docs_deprecated" 23 24 25def set_deprecated(obj: T) -> T: 26 """Explicitly tag an object as deprecated for the doc generator.""" 27 setattr(obj, _DEPRECATED, None) 28 return obj 29 30 31_INHERITABLE_HEADER = "_tf_docs_inheritable_header" 32 33 34def inheritable_header(text): 35 36 def _wrapped(cls): 37 setattr(cls, _INHERITABLE_HEADER, text) 38 return cls 39 40 return _wrapped 41 42 43_DO_NOT_DOC = "_tf_docs_do_not_document" 44 45 46def do_not_generate_docs(obj: T) -> T: 47 """A decorator: Do not generate docs for this object. 48 49 For example the following classes: 50 51 ``` 52 class Parent(object): 53 def method1(self): 54 pass 55 def method2(self): 56 pass 57 58 class Child(Parent): 59 def method1(self): 60 pass 61 def method2(self): 62 pass 63 ``` 64 65 Produce the following api_docs: 66 67 ``` 68 /Parent.md 69 # method1 70 # method2 71 /Child.md 72 # method1 73 # method2 74 ``` 75 76 This decorator allows you to skip classes or methods: 77 78 ``` 79 @do_not_generate_docs 80 class Parent(object): 81 def method1(self): 82 pass 83 def method2(self): 84 pass 85 86 class Child(Parent): 87 @do_not_generate_docs 88 def method1(self): 89 pass 90 def method2(self): 91 pass 92 ``` 93 94 This will only produce the following docs: 95 96 ``` 97 /Child.md 98 # method2 99 ``` 100 101 Note: This is implemented by adding a hidden attribute on the object, so it 102 cannot be used on objects which do not allow new attributes to be added. So 103 this decorator must go *below* `@property`, `@classmethod`, 104 or `@staticmethod`: 105 106 ``` 107 class Example(object): 108 @property 109 @do_not_generate_docs 110 def x(self): 111 return self._x 112 ``` 113 114 Args: 115 obj: The object to hide from the generated docs. 116 117 Returns: 118 obj 119 """ 120 setattr(obj, _DO_NOT_DOC, None) 121 return obj 122 123 124_DO_NOT_DOC_INHERITABLE = "_tf_docs_do_not_doc_inheritable" 125 126 127def do_not_doc_inheritable(obj: T) -> T: 128 """A decorator: Do not generate docs for this method. 129 130 This version of the decorator is "inherited" by subclasses. No docs will be 131 generated for the decorated method in any subclass. Even if the sub-class 132 overrides the method. 133 134 For example, to ensure that `method1` is **never documented** use this 135 decorator on the base-class: 136 137 ``` 138 class Parent(object): 139 @do_not_doc_inheritable 140 def method1(self): 141 pass 142 def method2(self): 143 pass 144 145 class Child(Parent): 146 def method1(self): 147 pass 148 def method2(self): 149 pass 150 ``` 151 This will produce the following docs: 152 153 ``` 154 /Parent.md 155 # method2 156 /Child.md 157 # method2 158 ``` 159 160 When generating docs for a class's arributes, the `__mro__` is searched and 161 the attribute will be skipped if this decorator is detected on the attribute 162 on any class in the `__mro__`. 163 164 Note: This is implemented by adding a hidden attribute on the object, so it 165 cannot be used on objects which do not allow new attributes to be added. So 166 this decorator must go *below* `@property`, `@classmethod`, 167 or `@staticmethod`: 168 169 ``` 170 class Example(object): 171 @property 172 @do_not_doc_inheritable 173 def x(self): 174 return self._x 175 ``` 176 177 Args: 178 obj: The class-attribute to hide from the generated docs. 179 180 Returns: 181 obj 182 """ 183 setattr(obj, _DO_NOT_DOC_INHERITABLE, None) 184 return obj 185 186 187_FOR_SUBCLASS_IMPLEMENTERS = "_tf_docs_tools_for_subclass_implementers" 188 189 190def for_subclass_implementers(obj: T) -> T: 191 """A decorator: Only generate docs for this method in the defining class. 192 193 Also group this method's docs with and `@abstractmethod` in the class's docs. 194 195 No docs will generated for this class attribute in sub-classes. 196 197 The canonical use case for this is `tf.keras.layers.Layer.call`: It's a 198 public method, essential for anyone implementing a subclass, but it should 199 never be called directly. 200 201 Works on method, or other class-attributes. 202 203 When generating docs for a class's arributes, the `__mro__` is searched and 204 the attribute will be skipped if this decorator is detected on the attribute 205 on any **parent** class in the `__mro__`. 206 207 For example: 208 209 ``` 210 class Parent(object): 211 @for_subclass_implementers 212 def method1(self): 213 pass 214 def method2(self): 215 pass 216 217 class Child1(Parent): 218 def method1(self): 219 pass 220 def method2(self): 221 pass 222 223 class Child2(Parent): 224 def method1(self): 225 pass 226 def method2(self): 227 pass 228 ``` 229 230 This will produce the following docs: 231 232 ``` 233 /Parent.md 234 # method1 235 # method2 236 /Child1.md 237 # method2 238 /Child2.md 239 # method2 240 ``` 241 242 Note: This is implemented by adding a hidden attribute on the object, so it 243 cannot be used on objects which do not allow new attributes to be added. So 244 this decorator must go *below* `@property`, `@classmethod`, 245 or `@staticmethod`: 246 247 ``` 248 class Example(object): 249 @property 250 @for_subclass_implementers 251 def x(self): 252 return self._x 253 ``` 254 255 Args: 256 obj: The class-attribute to hide from the generated docs. 257 258 Returns: 259 obj 260 """ 261 setattr(obj, _FOR_SUBCLASS_IMPLEMENTERS, None) 262 return obj 263 264 265do_not_doc_in_subclasses = for_subclass_implementers 266 267_DOC_PRIVATE = "_tf_docs_doc_private" 268 269 270def doc_private(obj: T) -> T: 271 """A decorator: Generates docs for private methods/functions. 272 273 For example: 274 275 ``` 276 class Try: 277 278 @doc_controls.doc_private 279 def _private(self): 280 ... 281 ``` 282 283 As a rule of thumb, private(beginning with `_`) methods/functions are 284 not documented. 285 286 This decorator allows to force document a private method/function. 287 288 Args: 289 obj: The class-attribute to hide from the generated docs. 290 291 Returns: 292 obj 293 """ 294 295 setattr(obj, _DOC_PRIVATE, None) 296 return obj 297 298 299_DOC_IN_CURRENT_AND_SUBCLASSES = "_tf_docs_doc_in_current_and_subclasses" 300 301 302def doc_in_current_and_subclasses(obj: T) -> T: 303 """Overrides `do_not_doc_in_subclasses` decorator. 304 305 If this decorator is set on a child class's method whose parent's method 306 contains `do_not_doc_in_subclasses`, then that will be overriden and the 307 child method will get documented. All classes inherting from the child will 308 also document that method. 309 310 For example: 311 312 ``` 313 class Parent: 314 @do_not_doc_in_subclasses 315 def method1(self): 316 pass 317 def method2(self): 318 pass 319 320 class Child1(Parent): 321 @doc_in_current_and_subclasses 322 def method1(self): 323 pass 324 def method2(self): 325 pass 326 327 class Child2(Parent): 328 def method1(self): 329 pass 330 def method2(self): 331 pass 332 333 class Child11(Child1): 334 pass 335 ``` 336 337 This will produce the following docs: 338 339 ``` 340 /Parent.md 341 # method1 342 # method2 343 /Child1.md 344 # method1 345 # method2 346 /Child2.md 347 # method2 348 /Child11.md 349 # method1 350 # method2 351 ``` 352 353 Args: 354 obj: The class-attribute to hide from the generated docs. 355 356 Returns: 357 obj 358 """ 359 360 setattr(obj, _DOC_IN_CURRENT_AND_SUBCLASSES, None) 361 return obj 362