1 # Copyright 2016 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 16 """Utility functions for writing decorators (which modify docstrings).""" 17 from __future__ import absolute_import 18 from __future__ import division 19 from __future__ import print_function 20 21 import sys 22 23 24 def get_qualified_name(function): 25 # Python 3 26 if hasattr(function, '__qualname__'): 27 return function.__qualname__ 28 29 # Python 2 30 if hasattr(function, 'im_class'): 31 return function.im_class.__name__ + '.' + function.__name__ 32 return function.__name__ 33 34 35 def _normalize_docstring(docstring): 36 """Normalizes the docstring. 37 38 Replaces tabs with spaces, removes leading and trailing blanks lines, and 39 removes any indentation. 40 41 Copied from PEP-257: 42 https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation 43 44 Args: 45 docstring: the docstring to normalize 46 47 Returns: 48 The normalized docstring 49 """ 50 if not docstring: 51 return '' 52 # Convert tabs to spaces (following the normal Python rules) 53 # and split into a list of lines: 54 lines = docstring.expandtabs().splitlines() 55 # Determine minimum indentation (first line doesn't count): 56 # (we use sys.maxsize because sys.maxint doesn't exist in Python 3) 57 indent = sys.maxsize 58 for line in lines[1:]: 59 stripped = line.lstrip() 60 if stripped: 61 indent = min(indent, len(line) - len(stripped)) 62 # Remove indentation (first line is special): 63 trimmed = [lines[0].strip()] 64 if indent < sys.maxsize: 65 for line in lines[1:]: 66 trimmed.append(line[indent:].rstrip()) 67 # Strip off trailing and leading blank lines: 68 while trimmed and not trimmed[-1]: 69 trimmed.pop() 70 while trimmed and not trimmed[0]: 71 trimmed.pop(0) 72 # Return a single string: 73 return '\n'.join(trimmed) 74 75 76 def add_notice_to_docstring( 77 doc, instructions, no_doc_str, suffix_str, notice): 78 """Adds a deprecation notice to a docstring.""" 79 if not doc: 80 lines = [no_doc_str] 81 else: 82 lines = _normalize_docstring(doc).splitlines() 83 lines[0] += ' ' + suffix_str 84 85 notice = [''] + notice + [instructions] 86 87 if len(lines) > 1: 88 # Make sure that we keep our distance from the main body 89 if lines[1].strip(): 90 notice.append('') 91 92 lines[1:1] = notice 93 else: 94 lines += notice 95 96 return '\n'.join(lines) 97 98 99 def validate_callable(func, decorator_name): 100 if not hasattr(func, '__call__'): 101 raise ValueError( 102 '%s is not a function. If this is a property, make sure' 103 ' @property appears before @%s in your source code:' 104 '\n\n@property\n@%s\ndef method(...)' % ( 105 func, decorator_name, decorator_name)) 106 107 108 class classproperty(object): # pylint: disable=invalid-name 109 """Class property decorator. 110 111 Example usage: 112 113 class MyClass(object): 114 115 @classproperty 116 def value(cls): 117 return '123' 118 119 > print MyClass.value 120 123 121 """ 122 123 def __init__(self, func): 124 self._func = func 125 126 def __get__(self, owner_self, owner_cls): 127 return self._func(owner_cls) 128