Home | History | Annotate | Download | only in util
      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