• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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