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).""" 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import print_function 20 21import sys 22 23 24def 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 35def _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 76def add_notice_to_docstring( 77 doc, instructions, no_doc_str, suffix_str, notice): 78 """Adds a deprecation notice to a docstring. 79 80 Args: 81 doc: The original docstring. 82 instructions: A string, describing how to fix the problem. 83 no_doc_str: The default value to use for `doc` if `doc` is empty. 84 suffix_str: Is added to the end of the first line. 85 notice: A list of strings. The main notice warning body. 86 87 Returns: 88 A new docstring, with the notice attached. 89 90 Raises: 91 ValueError: If `notice` is empty. 92 """ 93 if not doc: 94 lines = [no_doc_str] 95 else: 96 lines = _normalize_docstring(doc).splitlines() 97 lines[0] += ' ' + suffix_str 98 99 if not notice: 100 raise ValueError('The `notice` arg must not be empty.') 101 102 notice[0] = 'Warning: ' + notice[0] 103 notice = [''] + notice + ([instructions] if instructions else []) 104 105 if len(lines) > 1: 106 # Make sure that we keep our distance from the main body 107 if lines[1].strip(): 108 notice.append('') 109 110 lines[1:1] = notice 111 else: 112 lines += notice 113 114 return '\n'.join(lines) 115 116 117def validate_callable(func, decorator_name): 118 if not hasattr(func, '__call__'): 119 raise ValueError( 120 '%s is not a function. If this is a property, make sure' 121 ' @property appears before @%s in your source code:' 122 '\n\n@property\n@%s\ndef method(...)' % ( 123 func, decorator_name, decorator_name)) 124 125 126class classproperty(object): # pylint: disable=invalid-name 127 """Class property decorator. 128 129 Example usage: 130 131 class MyClass(object): 132 133 @classproperty 134 def value(cls): 135 return '123' 136 137 > print MyClass.value 138 123 139 """ 140 141 def __init__(self, func): 142 self._func = func 143 144 def __get__(self, owner_self, owner_cls): 145 return self._func(owner_cls) 146