Example #1
0
def _traverse_internal(root, visit, stack, path):
  """Internal helper for traverse."""

  # Only traverse modules and classes
  if not tf_inspect.isclass(root) and not tf_inspect.ismodule(root):
    return

  try:
    children = tf_inspect.getmembers(root)

    # Add labels for duplicate values in Enum.
    if tf_inspect.isclass(root) and issubclass(root, enum.Enum):
      for enum_member in root.__members__.items():
        if enum_member not in children:
          children.append(enum_member)
      children = sorted(children)
  except ImportError:
    # On some Python installations, some modules do not support enumerating
    # members (six in particular), leading to import errors.
    children = []

  new_stack = stack + [root]
  visit(path, root, children)
  for name, child in children:
    # Do not descend into built-in modules
    if tf_inspect.ismodule(
        child) and child.__name__ in sys.builtin_module_names:
      continue

    # Break cycles
    if any(child is item for item in new_stack):  # `in`, but using `is`
      continue

    child_path = path + '.' + name if path else name
    _traverse_internal(child, visit, new_stack, child_path)
Example #2
0
 def visit_Attribute(self, node):
   node = self.generic_visit(node)
   parent_val = anno.getanno(node.value, STATIC_VALUE, default=None)
   if parent_val is not None and tf_inspect.ismodule(parent_val):
     if hasattr(parent_val, node.attr):
       anno.setanno(node, STATIC_VALUE, getattr(parent_val, node.attr))
   return node
  def testDeprecationWarnings(self, mock_warning):
    module = MockModule('test')
    module.foo = 1
    module.bar = 2
    module.baz = 3
    all_renames_v2.symbol_renames['tf.test.bar'] = 'tf.bar2'
    all_renames_v2.symbol_renames['tf.test.baz'] = 'tf.compat.v1.baz'

    wrapped_module = deprecation_wrapper.DeprecationWrapper(
        module, 'test')
    self.assertTrue(tf_inspect.ismodule(wrapped_module))

    self.assertEqual(0, mock_warning.call_count)
    bar = wrapped_module.bar
    self.assertEqual(1, mock_warning.call_count)
    foo = wrapped_module.foo
    self.assertEqual(1, mock_warning.call_count)
    baz = wrapped_module.baz
    self.assertEqual(2, mock_warning.call_count)
    baz = wrapped_module.baz
    self.assertEqual(2, mock_warning.call_count)

    # Check that values stayed the same
    self.assertEqual(module.foo, foo)
    self.assertEqual(module.bar, bar)
    self.assertEqual(module.baz, baz)
Example #4
0
  def from_visitor(cls, visitor, doc_index, **kwargs):
    """A factory function for building a ReferenceResolver from a visitor.

    Args:
      visitor: an instance of `DocGeneratorVisitor`
      doc_index: a dictionary mapping document names to references objects with
        "title" and "url" fields
      **kwargs: all remaining args are passed to the constructor
    Returns:
      an instance of `ReferenceResolver` ()
    """
    is_class = {
        name: tf_inspect.isclass(visitor.index[name])
        for name, obj in visitor.index.items()
    }

    is_module = {
        name: tf_inspect.ismodule(visitor.index[name])
        for name, obj in visitor.index.items()
    }

    return cls(
        duplicate_of=visitor.duplicate_of,
        doc_index=doc_index,
        is_class=is_class,
        is_module=is_module,
        **kwargs)
Example #5
0
def getqualifiedname(namespace, object_, max_depth=5, visited=None):
  """Returns the name by which a value can be referred to in a given namespace.

  If the object defines a parent module, the function attempts to use it to
  locate the object.

  This function will recurse inside modules, but it will not search objects for
  attributes. The recursion depth is controlled by max_depth.

  Args:
    namespace: Dict[str, Any], the namespace to search into.
    object_: Any, the value to search.
    max_depth: Optional[int], a limit to the recursion depth when searching
      inside modules.
    visited: Optional[Set[int]], ID of modules to avoid visiting.
  Returns: Union[str, None], the fully-qualified name that resolves to the value
    o, or None if it couldn't be found.
  """
  if visited is None:
    visited = set()

  # Copy the dict to avoid "changed size error" during concurrent invocations.
  # TODO(mdan): This is on the hot path. Can we avoid the copy?
  namespace = dict(namespace)

  for name in namespace:
    # The value may be referenced by more than one symbol, case in which
    # any symbol will be fine. If the program contains symbol aliases that
    # change over time, this may capture a symbol that will later point to
    # something else.
    # TODO(mdan): Prefer the symbol that matches the value type name.
    if object_ is namespace[name]:
      return name

  # If an object is not found, try to search its parent modules.
  parent = tf_inspect.getmodule(object_)
  if (parent is not None and parent is not object_ and parent is not namespace):
    # No limit to recursion depth because of the guard above.
    parent_name = getqualifiedname(
        namespace, parent, max_depth=0, visited=visited)
    if parent_name is not None:
      name_in_parent = getqualifiedname(
          parent.__dict__, object_, max_depth=0, visited=visited)
      assert name_in_parent is not None, (
          'An object should always be found in its owner module')
      return '{}.{}'.format(parent_name, name_in_parent)

  if max_depth:
    # Iterating over a copy prevents "changed size due to iteration" errors.
    # It's unclear why those occur - suspecting new modules may load during
    # iteration.
    for name in namespace.keys():
      value = namespace[name]
      if tf_inspect.ismodule(value) and id(value) not in visited:
        visited.add(id(value))
        name_in_module = getqualifiedname(value.__dict__, object_,
                                          max_depth - 1, visited)
        if name_in_module is not None:
          return '{}.{}'.format(name, name_in_module)
  return None
Example #6
0
    def from_visitor(cls, visitor, doc_index, **kwargs):
        """A factory function for building a ReferenceResolver from a visitor.

    Args:
      visitor: an instance of `DocGeneratorVisitor`
      doc_index: a dictionary mapping document names to references objects with
        "title" and "url" fields
      **kwargs: all remaining args are passed to the constructor
    Returns:
      an instance of `ReferenceResolver` ()
    """
        is_class = {
            name: tf_inspect.isclass(visitor.index[name])
            for name, obj in visitor.index.items()
        }

        is_module = {
            name: tf_inspect.ismodule(visitor.index[name])
            for name, obj in visitor.index.items()
        }

        return cls(duplicate_of=visitor.duplicate_of,
                   doc_index=doc_index,
                   is_class=is_class,
                   is_module=is_module,
                   **kwargs)
  def __call__(self, parent_name, parent, children):
    """Visitor interface, see `tensorflow/tools/common:traverse` for details.

    This method is called for each symbol found in a traversal using
    `tensorflow/tools/common:traverse`. It should not be called directly in
    user code.

    Args:
      parent_name: The fully qualified name of a symbol found during traversal.
      parent: The Python object referenced by `parent_name`.
      children: A list of `(name, py_object)` pairs enumerating, in alphabetical
        order, the children (as determined by `tf_inspect.getmembers`) of
          `parent`. `name` is the local name of `py_object` in `parent`.

    Raises:
      RuntimeError: If this visitor is called with a `parent` that is not a
        class or module.
    """
    parent_name = self._add_prefix(parent_name)
    self._index[parent_name] = parent
    self._tree[parent_name] = []

    if not (tf_inspect.ismodule(parent) or tf_inspect.isclass(parent)):
      raise RuntimeError('Unexpected type in visitor -- %s: %r' % (parent_name,
                                                                   parent))

    for i, (name, child) in enumerate(list(children)):
      # Don't document __metaclass__
      if name in ['__metaclass__']:
        del children[i]
        continue

      full_name = '.'.join([parent_name, name]) if parent_name else name
      self._index[full_name] = child
      self._tree[parent_name].append(name)
Example #8
0
 def visit_Attribute(self, node):
   node = self.generic_visit(node)
   parent_val = anno.getanno(node.value, STATIC_VALUE, default=None)
   if parent_val is not None and tf_inspect.ismodule(parent_val):
     if hasattr(parent_val, node.attr):
       anno.setanno(node, STATIC_VALUE, getattr(parent_val, node.attr))
   return node
    def testDeprecationWarnings(self, mock_warning):
        module = MockModule('test')
        module.foo = 1
        module.bar = 2
        module.baz = 3
        all_renames_v2.symbol_renames['tf.test.bar'] = 'tf.bar2'
        all_renames_v2.symbol_renames['tf.test.baz'] = 'tf.compat.v1.baz'

        wrapped_module = deprecation_wrapper.DeprecationWrapper(module, 'test')
        self.assertTrue(tf_inspect.ismodule(wrapped_module))

        self.assertEqual(0, mock_warning.call_count)
        bar = wrapped_module.bar
        self.assertEqual(1, mock_warning.call_count)
        foo = wrapped_module.foo
        self.assertEqual(1, mock_warning.call_count)
        baz = wrapped_module.baz
        self.assertEqual(2, mock_warning.call_count)
        baz = wrapped_module.baz
        self.assertEqual(2, mock_warning.call_count)

        # Check that values stayed the same
        self.assertEqual(module.foo, foo)
        self.assertEqual(module.bar, bar)
        self.assertEqual(module.baz, baz)
Example #10
0
  def __call__(self, parent_name, parent, children):
    """Visitor interface, see `tensorflow/tools/common:traverse` for details.

    This method is called for each symbol found in a traversal using
    `tensorflow/tools/common:traverse`. It should not be called directly in
    user code.

    Args:
      parent_name: The fully qualified name of a symbol found during traversal.
      parent: The Python object referenced by `parent_name`.
      children: A list of `(name, py_object)` pairs enumerating, in alphabetical
        order, the children (as determined by `tf_inspect.getmembers`) of
          `parent`. `name` is the local name of `py_object` in `parent`.

    Raises:
      RuntimeError: If this visitor is called with a `parent` that is not a
        class or module.
    """
    parent_name = self._add_prefix(parent_name)
    self._index[parent_name] = parent
    self._tree[parent_name] = []

    if not (tf_inspect.ismodule(parent) or tf_inspect.isclass(parent)):
      raise RuntimeError('Unexpected type in visitor -- %s: %r' % (parent_name,
                                                                   parent))

    for i, (name, child) in enumerate(list(children)):
      # Don't document __metaclass__
      if name in ['__metaclass__']:
        del children[i]
        continue

      full_name = '.'.join([parent_name, name]) if parent_name else name
      self._index[full_name] = child
      self._tree[parent_name].append(name)
Example #11
0
def getqualifiedname(namespace, object_, max_depth=2):
    """Returns the name by which a value can be referred to in a given namespace.

  This function will recurse inside modules, but it will not search objects for
  attributes. The recursion depth is controlled by max_depth.

  Args:
    namespace: Dict[str, Any], the namespace to search into.
    object_: Any, the value to search.
    max_depth: Optional[int], a limit to the recursion depth when searching
        inside modules.
  Returns: Union[str, None], the fully-qualified name that resolves to the value
      o, or None if it couldn't be found.
  """
    for name, value in namespace.items():
        # The value may be referenced by more than one symbol, case in which
        # any symbol will be fine. If the program contains symbol aliases that
        # change over time, this may capture a symbol that will later point to
        # something else.
        # TODO(mdan): Prefer the symbol that matches the value type name.
        if object_ is value:
            return name

    # TODO(mdan): Use breadth-first search and avoid visiting modules twice.
    if max_depth:
        # Iterating over a copy prevents "changed size due to iteration" errors.
        # It's unclear why those occur - suspecting new modules may load during
        # iteration.
        for name, value in namespace.copy().items():
            if tf_inspect.ismodule(value):
                name_in_module = getqualifiedname(value.__dict__, object_,
                                                  max_depth - 1)
                if name_in_module is not None:
                    return '{}.{}'.format(name, name_in_module)
    return None
Example #12
0
def getqualifiedname(namespace, object_, max_depth=2):
  """Returns the name by which a value can be referred to in a given namespace.

  This function will recurse inside modules, but it will not search objects for
  attributes. The recursion depth is controlled by max_depth.

  Args:
    namespace: Dict[str, Any], the namespace to search into.
    object_: Any, the value to search.
    max_depth: Optional[int], a limit to the recursion depth when searching
        inside modules.
  Returns: Union[str, None], the fully-qualified name that resolves to the value
      o, or None if it couldn't be found.
  """
  for name, value in namespace.items():
    # The value may be referenced by more than one symbol, case in which
    # any symbol will be fine. If the program contains symbol aliases that
    # change over time, this may capture a symbol that will later point to
    # something else.
    # TODO(mdan): Prefer the symbol that matches the value type name.
    if object_ is value:
      return name

  # TODO(mdan): Use breadth-first search and avoid visiting modules twice.
  if max_depth:
    # Iterating over a copy prevents "changed size due to iteration" errors.
    # It's unclear why those occur - suspecting new modules may load during
    # iteration.
    for name, value in namespace.copy().items():
      if tf_inspect.ismodule(value):
        name_in_module = getqualifiedname(value.__dict__, object_,
                                          max_depth - 1)
        if name_in_module is not None:
          return '{}.{}'.format(name, name_in_module)
  return None
Example #13
0
    def __call__(self, path, parent, children):
        """Visitor interface, see `traverse` for details."""

        # Avoid long waits in cases of pretty unambiguous failure.
        if tf_inspect.ismodule(parent) and len(path.split('.')) > 10:
            raise RuntimeError(
                'Modules nested too deep:\n%s.%s\n\nThis is likely a '
                'problem with an accidental public import.' %
                (self._root_name, path))

        # Includes self._root_name
        full_path = '.'.join([self._root_name, path
                              ]) if path else self._root_name

        # Remove things that are not visible.
        for name, child in list(children):
            if self._is_private(full_path, name):
                children.remove((name, child))

        self._visitor(path, parent, children)

        # Remove things that are visible, but which should not be descended into.
        for name, child in list(children):
            if self._do_not_descend(full_path, name):
                children.remove((name, child))
Example #14
0
 def testWrapperIsAModule(self):
     module = MockModule('test')
     wrapped_module = deprecation_wrapper.DeprecationWrapper(
         module, 'test', {
             'bar': 'bar2',
             'baz': 'compat.v1.baz'
         })
     self.assertTrue(tf_inspect.ismodule(wrapped_module))
  def __call__(self, path, parent, children):
    # The path to the object.
    lib_path = 'tensorflow.%s' % path if path else 'tensorflow'

    # A small helper method to construct members(children) protos.
    def _AddMember(member_name, member_obj, proto):
      """Add the child object to the object being constructed."""
      _, member_obj = tf_decorator.unwrap(member_obj)
      if member_name == '__init__' or not member_name.startswith('_'):
        if tf_inspect.isroutine(member_obj):
          new_method = proto.member_method.add()
          new_method.name = member_name
          # If member_obj is a python builtin, there is no way to get its
          # argspec, because it is implemented on the C side. It also has no
          # func_code.
          if getattr(member_obj, 'func_code', None):
            new_method.argspec = _SanitizedArgSpec(member_obj)
        else:
          new_member = proto.member.add()
          new_member.name = member_name
          new_member.mtype = str(type(member_obj))

    parent_corner_cases = _CORNER_CASES.get(path, {})

    if path not in _CORNER_CASES or parent_corner_cases:
      # Decide if we have a module or a class.
      if tf_inspect.ismodule(parent):
        # Create a module object.
        module_obj = api_objects_pb2.TFAPIModule()
        for name, child in children:
          if name in parent_corner_cases:
            # If we have an empty entry, skip this object.
            if parent_corner_cases[name]:
              module_obj.member.add(**(parent_corner_cases[name]))
          else:
            _AddMember(name, child, module_obj)

        # Store the constructed module object.
        self._protos[lib_path] = api_objects_pb2.TFAPIObject(
            path=lib_path, tf_module=module_obj)
      elif tf_inspect.isclass(parent):
        # Construct a class.
        class_obj = api_objects_pb2.TFAPIClass()
        class_obj.is_instance.extend(_SanitizedMRO(parent))
        for name, child in children:
          if name in parent_corner_cases:
            # If we have an empty entry, skip this object.
            if parent_corner_cases[name]:
              module_obj.member.add(**(parent_corner_cases[name]))
          else:
            _AddMember(name, child, class_obj)

        # Store the constructed class object.
        self._protos[lib_path] = api_objects_pb2.TFAPIObject(
            path=lib_path, tf_class=class_obj)
      else:
        logging.error('Illegal call to ApiProtoDump::_py_obj_to_proto.'
                      'Object is neither a module nor a class: %s', path)
Example #16
0
def getqualifiedname(namespace, object_, max_depth=5, visited=None):
  """Returns the name by which a value can be referred to in a given namespace.

  If the object defines a parent module, the function attempts to use it to
  locate the object.

  This function will recurse inside modules, but it will not search objects for
  attributes. The recursion depth is controlled by max_depth.

  Args:
    namespace: Dict[str, Any], the namespace to search into.
    object_: Any, the value to search.
    max_depth: Optional[int], a limit to the recursion depth when searching
        inside modules.
    visited: Optional[Set[int]], ID of modules to avoid visiting.
  Returns: Union[str, None], the fully-qualified name that resolves to the value
      o, or None if it couldn't be found.
  """
  if visited is None:
    visited = set()

  for name in namespace:
    # The value may be referenced by more than one symbol, case in which
    # any symbol will be fine. If the program contains symbol aliases that
    # change over time, this may capture a symbol that will later point to
    # something else.
    # TODO(mdan): Prefer the symbol that matches the value type name.
    if object_ is namespace[name]:
      return name

  # If an object is not found, try to search its parent modules.
  parent = tf_inspect.getmodule(object_)
  if (parent is not None and parent is not object_ and
      parent is not namespace):
    # No limit to recursion depth because of the guard above.
    parent_name = getqualifiedname(
        namespace, parent, max_depth=0, visited=visited)
    if parent_name is not None:
      name_in_parent = getqualifiedname(
          parent.__dict__, object_, max_depth=0, visited=visited)
      assert name_in_parent is not None, (
          'An object should always be found in its owner module')
      return '{}.{}'.format(parent_name, name_in_parent)

  if max_depth:
    # Iterating over a copy prevents "changed size due to iteration" errors.
    # It's unclear why those occur - suspecting new modules may load during
    # iteration.
    for name in namespace.keys():
      value = namespace[name]
      if tf_inspect.ismodule(value) and id(value) not in visited:
        visited.add(id(value))
        name_in_module = getqualifiedname(value.__dict__, object_,
                                          max_depth - 1, visited)
        if name_in_module is not None:
          return '{}.{}'.format(name, name_in_module)
  return None
Example #17
0
  def _score_name(self, name):
    """Return a tuple of scores indicating how to sort for the best name.

    This function is meant to be used as the `key` to the `sorted` function.

    This sorting in order:
      Prefers names refering to the defining class, over a subclass.
      Prefers names that are not in "contrib".
      prefers submodules to the root namespace.
      Prefers short names `tf.thing` over `tf.a.b.c.thing`
      Sorts lexicographically on name parts.

    Args:
      name: the full name to score, for example `tf.estimator.Estimator`

    Returns:
      A tuple of scores. When sorted the preferred name will have the lowest
      value.
    """
    parts = six.ensure_str(name).split('.')
    short_name = parts[-1]

    container = self._index['.'.join(parts[:-1])]

    defining_class_score = 1
    if tf_inspect.isclass(container):
      if short_name in container.__dict__:
        # prefer the defining class
        defining_class_score = -1

    contrib_score = -1
    if 'contrib' in parts:
      contrib_score = 1

    while parts:
      container = self._index['.'.join(parts)]
      if tf_inspect.ismodule(container):
        break
      parts.pop()

    module_length = len(parts)
    if len(parts) == 2:
      # `tf.submodule.thing` is better than `tf.thing`
      module_length_score = -1
    else:
      # shorter is better
      module_length_score = module_length

    return (defining_class_score, contrib_score, module_length_score, name)
  def _score_name(self, name):
    """Return a tuple of scores indicating how to sort for the best name.

    This function is meant to be used as the `key` to the `sorted` function.

    This sorting in order:
      Prefers names refering to the defining class, over a subclass.
      Prefers names that are not in "contrib".
      prefers submodules to the root namespace.
      Prefers short names `tf.thing` over `tf.a.b.c.thing`
      Sorts lexicographically on name parts.

    Args:
      name: the full name to score, for example `tf.estimator.Estimator`

    Returns:
      A tuple of scores. When sorted the preferred name will have the lowest
      value.
    """
    parts = name.split('.')
    short_name = parts[-1]

    container = self._index['.'.join(parts[:-1])]

    defining_class_score = 1
    if tf_inspect.isclass(container):
      if short_name in container.__dict__:
        # prefer the defining class
        defining_class_score = -1

    contrib_score = -1
    if 'contrib' in parts:
      contrib_score = 1

    while parts:
      parts.pop()
      container = self._index['.'.join(parts)]
      if tf_inspect.ismodule(container):
        break
    module_length = len(parts)
    if len(parts) == 2:
      # `tf.submodule.thing` is better than `tf.thing`
      module_length_score = -1
    else:
      # shorter is better
      module_length_score = module_length

    return (defining_class_score, contrib_score, module_length_score, name)
Example #19
0
    def collect_docs_for_module(self, parser_config):
        """Collect information necessary specifically for a module's doc page.

    Mainly this is information about the members of the module.

    Args:
      parser_config: An instance of ParserConfig.
    """
        relative_path = os.path.relpath(
            path='.',
            start=os.path.dirname(documentation_path(self.full_name)) or '.')

        member_names = parser_config.tree.get(self.full_name, [])
        for name in member_names:

            if name in [
                    '__builtins__', '__doc__', '__file__', '__name__',
                    '__path__', '__package__'
            ]:
                continue

            member_full_name = self.full_name + '.' + name if self.full_name else name
            member = parser_config.py_name_to_object(member_full_name)

            member_doc = _parse_md_docstring(member, relative_path,
                                             parser_config.reference_resolver)

            url = parser_config.reference_resolver.reference_to_url(
                member_full_name, relative_path)

            if tf_inspect.ismodule(member):
                self._add_module(name, member_full_name, member, member_doc,
                                 url)

            elif tf_inspect.isclass(member):
                self._add_class(name, member_full_name, member, member_doc,
                                url)

            elif tf_inspect.isfunction(member):
                self._add_function(name, member_full_name, member, member_doc,
                                   url)

            else:
                self._add_other_member(name, member_full_name, member,
                                       member_doc)
Example #20
0
def _get_raw_docstring(py_object):
  """Get the docs for a given python object.

  Args:
    py_object: A python object to retrieve the docs for (class, function/method,
      or module).

  Returns:
    The docstring, or the empty string if no docstring was found.
  """
  # For object instances, tf_inspect.getdoc does give us the docstring of their
  # type, which is not what we want. Only return the docstring if it is useful.
  if (tf_inspect.isclass(py_object) or tf_inspect.ismethod(py_object) or
      tf_inspect.isfunction(py_object) or tf_inspect.ismodule(py_object) or
      isinstance(py_object, property)):
    return tf_inspect.getdoc(py_object) or ''
  else:
    return ''
Example #21
0
def _get_raw_docstring(py_object):
    """Get the docs for a given python object.

  Args:
    py_object: A python object to retrieve the docs for (class, function/method,
      or module).

  Returns:
    The docstring, or the empty string if no docstring was found.
  """
    # For object instances, tf_inspect.getdoc does give us the docstring of their
    # type, which is not what we want. Only return the docstring if it is useful.
    if (tf_inspect.isclass(py_object) or tf_inspect.ismethod(py_object)
            or tf_inspect.isfunction(py_object)
            or tf_inspect.ismodule(py_object)
            or isinstance(py_object, property)):
        return tf_inspect.getdoc(py_object) or ''
    else:
        return ''
Example #22
0
  def __call__(self, path, parent, children):
    """Visitor interface, see `traverse` for details."""

    # Avoid long waits in cases of pretty unambiguous failure.
    if tf_inspect.ismodule(parent) and len(path.split('.')) > 10:
      raise RuntimeError('Modules nested too deep:\n%s\n\nThis is likely a '
                         'problem with an accidental public import.' % path)

    # Remove things that are not visible.
    for name, child in list(children):
      if self._isprivate(name):
        children.remove((name, child))

    self._visitor(path, parent, children)

    # Remove things that are visible, but which should not be descended into.
    for name, child in list(children):
      if self._do_not_descend(path, name):
        children.remove((name, child))
Example #23
0
  def collect_docs_for_module(self, parser_config):
    """Collect information necessary specifically for a module's doc page.

    Mainly this is information about the members of the module.

    Args:
      parser_config: An instance of ParserConfig.
    """
    relative_path = os.path.relpath(
        path='.',
        start=os.path.dirname(documentation_path(self.full_name)) or '.')

    member_names = parser_config.tree.get(self.full_name, [])
    for name in member_names:

      if name in ['__builtins__', '__doc__', '__file__',
                  '__name__', '__path__', '__package__']:
        continue

      member_full_name = self.full_name + '.' + name if self.full_name else name
      member = parser_config.py_name_to_object(member_full_name)

      member_doc = _parse_md_docstring(member, relative_path,
                                       parser_config.reference_resolver)

      url = parser_config.reference_resolver.reference_to_url(
          member_full_name, relative_path)

      if tf_inspect.ismodule(member):
        self._add_module(name, member_full_name, member, member_doc, url)

      elif tf_inspect.isclass(member):
        self._add_class(name, member_full_name, member, member_doc, url)

      elif tf_inspect.isfunction(member):
        self._add_function(name, member_full_name, member, member_doc, url)

      else:
        self._add_other_member(name, member_full_name, member, member_doc)
Example #24
0
 def _AddMember(member_name, member_obj, proto):
   """Add the child object to the object being constructed."""
   _, member_obj = tf_decorator.unwrap(member_obj)
   if (_SkipMember(parent, member_name)
       or member_obj == deprecation.HIDDEN_ATTRIBUTE):
     return
   if member_name == '__init__' or not member_name.startswith('_'):
     if tf_inspect.isroutine(member_obj):
       new_method = proto.member_method.add()
       new_method.name = member_name
       # If member_obj is a python builtin, there is no way to get its
       # argspec, because it is implemented on the C side. It also has no
       # func_code.
       if hasattr(member_obj, '__code__'):
         new_method.argspec = _SanitizedArgSpec(member_obj)
     else:
       new_member = proto.member.add()
       new_member.name = member_name
       if tf_inspect.ismodule(member_obj):
         new_member.mtype = "<type \'module\'>"
       else:
         new_member.mtype = _NormalizeType(str(type(member_obj)))
Example #25
0
def generate_global_index(library_name, index, reference_resolver):
    """Given a dict of full names to python objects, generate an index page.

  The index page generated contains a list of links for all symbols in `index`
  that have their own documentation page.

  Args:
    library_name: The name for the documented library to use in the title.
    index: A dict mapping full names to python objects.
    reference_resolver: An instance of ReferenceResolver.

  Returns:
    A string containing an index page as Markdown.
  """
    symbol_links = []
    for full_name, py_object in six.iteritems(index):
        if (tf_inspect.ismodule(py_object) or tf_inspect.isfunction(py_object)
                or tf_inspect.isclass(py_object)):
            # In Python 3, unbound methods are functions, so eliminate those.
            if tf_inspect.isfunction(py_object):
                if full_name.count('.') == 0:
                    parent_name = ''
                else:
                    parent_name = full_name[:full_name.rfind('.')]
                if parent_name in index and tf_inspect.isclass(
                        index[parent_name]):
                    # Skip methods (=functions with class parents).
                    continue
            symbol_links.append(
                (full_name,
                 reference_resolver.python_link(full_name, full_name, '.')))

    lines = ['# All symbols in %s' % library_name, '']
    for _, link in sorted(symbol_links, key=lambda x: x[0]):
        lines.append('*  %s' % link)

    # TODO(markdaoust): use a _ModulePageInfo -> prety_docs.build_md_page()
    return '\n'.join(lines)
Example #26
0
def generate_global_index(library_name, index, reference_resolver):
  """Given a dict of full names to python objects, generate an index page.

  The index page generated contains a list of links for all symbols in `index`
  that have their own documentation page.

  Args:
    library_name: The name for the documented library to use in the title.
    index: A dict mapping full names to python objects.
    reference_resolver: An instance of ReferenceResolver.

  Returns:
    A string containing an index page as Markdown.
  """
  symbol_links = []
  for full_name, py_object in six.iteritems(index):
    if (tf_inspect.ismodule(py_object) or tf_inspect.isfunction(py_object) or
        tf_inspect.isclass(py_object)):
      # In Python 3, unbound methods are functions, so eliminate those.
      if tf_inspect.isfunction(py_object):
        if full_name.count('.') == 0:
          parent_name = ''
        else:
          parent_name = full_name[:full_name.rfind('.')]
        if parent_name in index and tf_inspect.isclass(index[parent_name]):
          # Skip methods (=functions with class parents).
          continue
      symbol_links.append((
          full_name, reference_resolver.python_link(full_name, full_name, '.')))

  lines = ['# All symbols in %s' % library_name, '']
  for _, link in sorted(symbol_links, key=lambda x: x[0]):
    lines.append('*  %s' % link)

  # TODO(markdaoust): use a _ModulePageInfo -> prety_docs.build_md_page()
  return '\n'.join(lines)
Example #27
0
 def testLinearOptimizer(self):
     # pylint: disable=g-import-not-at-top
     import tensorflow as tf
     assert tf_inspect.ismodule(tf.contrib.linear_optimizer)
 def testWrapperIsAModule(self):
     module = MockModule('test')
     wrapped_module = deprecation_wrapper.DeprecationWrapper(module, 'test')
     self.assertTrue(tf_inspect.ismodule(wrapped_module))
Example #29
0
def write_docs(output_dir, parser_config, yaml_toc):
  """Write previously extracted docs to disk.

  Write a docs page for each symbol included in the indices of parser_config to
  a tree of docs at `output_dir`.

  Symbols with multiple aliases will have only one page written about
  them, which is referenced for all aliases.

  Args:
    output_dir: Directory to write documentation markdown files to. Will be
      created if it doesn't exist.
    parser_config: A `parser.ParserConfig` object, containing all the necessary
      indices.
    yaml_toc: Set to `True` to generate a "_toc.yaml" file.

  Raises:
    ValueError: if `output_dir` is not an absolute path
  """
  # Make output_dir.
  if not os.path.isabs(output_dir):
    raise ValueError(
        "'output_dir' must be an absolute path.\n"
        "    output_dir='%s'" % output_dir)

  try:
    if not os.path.exists(output_dir):
      os.makedirs(output_dir)
  except OSError as e:
    print('Creating output dir "%s" failed: %s' % (output_dir, e))
    raise

  # These dictionaries are used for table-of-contents generation below
  # They will contain, after the for-loop below::
  #  - module name(string):classes and functions the module contains(list)
  module_children = {}
  #  - symbol name(string):pathname (string)
  symbol_to_file = {}

  # Parse and write Markdown pages, resolving cross-links (@{symbol}).
  for full_name, py_object in six.iteritems(parser_config.index):

    if full_name in parser_config.duplicate_of:
      continue

    # Methods and some routines are documented only as part of their class.
    if not (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or
            _is_free_function(py_object, full_name, parser_config.index)):
      continue

    sitepath = os.path.join('api_docs/python',
                            parser.documentation_path(full_name)[:-3])

    # For TOC, we need to store a mapping from full_name to the file
    # we're generating
    symbol_to_file[full_name] = sitepath

    # For a module, remember the module for the table-of-contents
    if tf_inspect.ismodule(py_object):
      if full_name in parser_config.tree:
        module_children.setdefault(full_name, [])

    # For something else that's documented,
    # figure out what module it lives in
    else:
      subname = str(full_name)
      while True:
        subname = subname[:subname.rindex('.')]
        if tf_inspect.ismodule(parser_config.index[subname]):
          module_children.setdefault(subname, []).append(full_name)
          break

    print('Writing docs for %s (%r).' % (full_name, py_object))

    # Generate docs for `py_object`, resolving references.
    page_info = parser.docs_for_object(full_name, py_object, parser_config)

    path = os.path.join(output_dir, parser.documentation_path(full_name))
    directory = os.path.dirname(path)
    try:
      if not os.path.exists(directory):
        os.makedirs(directory)
      with open(path, 'w') as f:
        f.write(pretty_docs.build_md_page(page_info))
    except OSError as e:
      print('Cannot write documentation for %s to %s: %s' % (full_name,
                                                             directory, e))
      raise

  if yaml_toc:
    # Generate table of contents

    # Put modules in alphabetical order, case-insensitive
    modules = sorted(module_children.keys(), key=lambda a: a.upper())

    leftnav_path = os.path.join(output_dir, '_toc.yaml')
    with open(leftnav_path, 'w') as f:

      # Generate header
      f.write('# Automatically generated file; please do not edit\ntoc:\n')
      for module in modules:
        f.write('  - title: ' + module + '\n'
                '    section:\n' + '    - title: Overview\n' +
                '      path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[module]
                + '\n')

        symbols_in_module = module_children.get(module, [])
        # Sort case-insensitive, if equal sort case sensitive (upper first)
        symbols_in_module.sort(key=lambda a: (a.upper(), a))

        for full_name in symbols_in_module:
          f.write('    - title: ' + full_name[len(module) + 1:] + '\n'
                  '      path: /TARGET_DOC_ROOT/VERSION/' +
                  symbol_to_file[full_name] + '\n')

  # Write a global index containing all full names with links.
  with open(os.path.join(output_dir, 'index.md'), 'w') as f:
    f.write(
        parser.generate_global_index('TensorFlow', parser_config.index,
                                     parser_config.reference_resolver))
Example #30
0
 def testLinearOptimizer(self):
   # pylint: disable=g-import-not-at-top
   import tensorflow as tf
   assert tf_inspect.ismodule(tf.contrib.linear_optimizer)
Example #31
0
def docs_for_object(full_name, py_object, parser_config):
  """Return a PageInfo object describing a given object from the TF API.

  This function uses _parse_md_docstring to parse the docs pertaining to
  `object`.

  This function resolves '@{symbol}' references in the docstrings into links to
  the appropriate location. It also adds a list of alternative names for the
  symbol automatically.

  It assumes that the docs for each object live in a file given by
  `documentation_path`, and that relative links to files within the
  documentation are resolvable.

  Args:
    full_name: The fully qualified name of the symbol to be
      documented.
    py_object: The Python object to be documented. Its documentation is sourced
      from `py_object`'s docstring.
    parser_config: A ParserConfig object.

  Returns:
    Either a `_FunctionPageInfo`, `_ClassPageInfo`, or a `_ModulePageInfo`
    depending on the type of the python object being documented.

  Raises:
    RuntimeError: If an object is encountered for which we don't know how
      to make docs.
  """

  # Which other aliases exist for the object referenced by full_name?
  master_name = parser_config.reference_resolver.py_master_name(full_name)
  duplicate_names = parser_config.duplicates.get(master_name, [full_name])

  # TODO(wicke): Once other pieces are ready, enable this also for partials.
  if (tf_inspect.ismethod(py_object) or tf_inspect.isfunction(py_object) or
      # Some methods in classes from extensions come in as routines.
      tf_inspect.isroutine(py_object)):
    page_info = _FunctionPageInfo(master_name)
    page_info.set_signature(py_object, parser_config.reverse_index)

  elif tf_inspect.isclass(py_object):
    page_info = _ClassPageInfo(master_name)
    page_info.collect_docs_for_class(py_object, parser_config)

  elif tf_inspect.ismodule(py_object):
    page_info = _ModulePageInfo(master_name)
    page_info.collect_docs_for_module(parser_config)

  else:
    raise RuntimeError('Cannot make docs for object %s: %r' % (full_name,
                                                               py_object))

  relative_path = os.path.relpath(
      path='.', start=os.path.dirname(documentation_path(full_name)) or '.')

  page_info.set_doc(_parse_md_docstring(
      py_object, relative_path, parser_config.reference_resolver))

  page_info.set_aliases(duplicate_names)

  page_info.set_guides(_get_guides_markdown(
      duplicate_names, parser_config.guide_index, relative_path))

  page_info.set_defined_in(_get_defined_in(py_object, parser_config))

  return page_info
Example #32
0
    def SmartSet(self, obj, attr_name, new_attr):
        """Replace obj.attr_name with new_attr.

    This method is smart and works at the module, class, and instance level
    while preserving proper inheritance. It will not stub out C types however
    unless that has been explicitly allowed by the type.

    This method supports the case where attr_name is a staticmethod or a
    classmethod of obj.

    Notes:
      - If obj is an instance, then it is its class that will actually be
        stubbed. Note that the method Set() does not do that: if obj is
        an instance, it (and not its class) will be stubbed.
      - The stubbing is using the builtin getattr and setattr. So, the __get__
        and __set__ will be called when stubbing (TODO: A better idea would
        probably be to manipulate obj.__dict__ instead of getattr() and
        setattr()).

    Args:
      obj: The object whose attributes we want to modify.
      attr_name: The name of the attribute to modify.
      new_attr: The new value for the attribute.

    Raises:
      AttributeError: If the attribute cannot be found.
    """
        _, obj = tf_decorator.unwrap(obj)
        if (tf_inspect.ismodule(obj) or
            (not tf_inspect.isclass(obj) and attr_name in obj.__dict__)):
            orig_obj = obj
            orig_attr = getattr(obj, attr_name)
        else:
            if not tf_inspect.isclass(obj):
                mro = list(tf_inspect.getmro(obj.__class__))
            else:
                mro = list(tf_inspect.getmro(obj))

            mro.reverse()

            orig_attr = None
            found_attr = False

            for cls in mro:
                try:
                    orig_obj = cls
                    orig_attr = getattr(obj, attr_name)
                    found_attr = True
                except AttributeError:
                    continue

            if not found_attr:
                raise AttributeError('Attribute not found.')

        # Calling getattr() on a staticmethod transforms it to a 'normal' function.
        # We need to ensure that we put it back as a staticmethod.
        old_attribute = obj.__dict__.get(attr_name)
        if old_attribute is not None and isinstance(old_attribute,
                                                    staticmethod):
            orig_attr = staticmethod(orig_attr)

        self.stubs.append((orig_obj, attr_name, orig_attr))
        setattr(orig_obj, attr_name, new_attr)
Example #33
0
  def visit_FunctionDef(self, node):
    self.generic_visit(node)
    kept_decorators = []
    for dec in node.decorator_list:
      if isinstance(dec, gast.Call):
        dec_func = dec.func
      else:
        dec_func = dec

      # Special cases.
      # TODO(mdan): Is there any way we can treat these more generically?
      # We may want to forego using decorators altogether if we can't
      # properly support them.
      if isinstance(dec_func, gast.Name) and dec_func.id in ('classmethod',):
        # Assumption: decorators are only visible in the AST when converting
        # a function inline (via another decorator).
        # In that case, the converted function is no longer part of the
        # original object that it was declared into.
        # This is currently verified by tests.
        continue

      if not anno.hasanno(dec_func, 'live_val'):
        raise ValueError('could not resolve the decorator "@%s"' %
                         (anno.getanno(dec_func, anno.Basic.QN)))

      original_dec = anno.getanno(dec_func, anno.Basic.QN)
      dec_value = anno.getanno(dec_func, 'live_val')

      if dec_value in self.ctx.program.options.strip_decorators:
        continue

      # When using foo.bar.baz, we only really need to grab foo and import
      # that.
      dec_support_node = dec_func
      while isinstance(dec_support_node, gast.Attribute):
        dec_support_node = dec_support_node.value

      if not anno.hasanno(dec_support_node, 'live_val'):
        raise ValueError(
            'could not resolve symbol "%s" when looking up decorator "%s"' %
            (anno.getanno(dec_support_node, anno.Basic.QN), original_dec))

      dec_support = anno.getanno(dec_support_node, 'live_val')
      # The tuple contains:
      #  * the AST that represents the decorator
      #  * the entity supporting the decorator (i.e., what we need to import)
      #  * the name of the module that needs to be imported for this decorator
      #    to properly resolve.
      # Examples:
      #  for foo.bar, the tuple is (<ast>, <module foo>, 'foo')
      #  for baz, the tuple is (<ast>, <module baz.__module__>, 'baz')
      kept_decorators.append((dec, dec_support,
                              anno.getanno(dec_support_node, anno.Basic.QN)))

    for _, dec_support, name in kept_decorators:
      if tf_inspect.ismodule(dec_support):
        self.ctx.program.additional_imports.add(
            'import %s as %s' % (dec_support.__name__, name))
      else:
        if dec_support.__module__ == '__main__':
          raise ValueError(
              'decorator "%s" was not allowed because it is declared '
              'in the module "%s". To fix this, declare it in a separate '
              'module that we can import it from.' % (dec_support,
                                                      dec_support.__module__))
        self.ctx.program.additional_imports.add(
            'from %s import %s' % (dec_support.__module__, name))

    node.decorator_list = [dec for dec, _, _ in kept_decorators]
    return node
Example #34
0
def write_docs(output_dir, parser_config, yaml_toc, root_title='TensorFlow'):
  """Write previously extracted docs to disk.

  Write a docs page for each symbol included in the indices of parser_config to
  a tree of docs at `output_dir`.

  Symbols with multiple aliases will have only one page written about
  them, which is referenced for all aliases.

  Args:
    output_dir: Directory to write documentation markdown files to. Will be
      created if it doesn't exist.
    parser_config: A `parser.ParserConfig` object, containing all the necessary
      indices.
    yaml_toc: Set to `True` to generate a "_toc.yaml" file.
    root_title: The title name for the root level index.md.

  Raises:
    ValueError: if `output_dir` is not an absolute path
  """
  # Make output_dir.
  if not os.path.isabs(output_dir):
    raise ValueError("'output_dir' must be an absolute path.\n"
                     "    output_dir='%s'" % output_dir)

  try:
    if not os.path.exists(output_dir):
      os.makedirs(output_dir)
  except OSError as e:
    print('Creating output dir "%s" failed: %s' % (output_dir, e))
    raise

  # These dictionaries are used for table-of-contents generation below
  # They will contain, after the for-loop below::
  #  - module name(string):classes and functions the module contains(list)
  module_children = {}
  #  - symbol name(string):pathname (string)
  symbol_to_file = {}

  # Parse and write Markdown pages, resolving cross-links (@{symbol}).
  for full_name, py_object in six.iteritems(parser_config.index):
    parser_config.reference_resolver.current_doc_full_name = full_name

    if full_name in parser_config.duplicate_of:
      continue

    # Methods and some routines are documented only as part of their class.
    if not (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or
            _is_free_function(py_object, full_name, parser_config.index)):
      continue

    sitepath = os.path.join('api_docs/python',
                            parser.documentation_path(full_name)[:-3])

    # For TOC, we need to store a mapping from full_name to the file
    # we're generating
    symbol_to_file[full_name] = sitepath

    # For a module, remember the module for the table-of-contents
    if tf_inspect.ismodule(py_object):
      if full_name in parser_config.tree:
        module_children.setdefault(full_name, [])

    # For something else that's documented,
    # figure out what module it lives in
    else:
      subname = str(full_name)
      while True:
        subname = subname[:subname.rindex('.')]
        if tf_inspect.ismodule(parser_config.index[subname]):
          module_children.setdefault(subname, []).append(full_name)
          break

    print('Writing docs for %s (%r).' % (full_name, py_object))

    # Generate docs for `py_object`, resolving references.
    page_info = parser.docs_for_object(full_name, py_object, parser_config)

    path = os.path.join(output_dir, parser.documentation_path(full_name))
    directory = os.path.dirname(path)
    try:
      if not os.path.exists(directory):
        os.makedirs(directory)
      with open(path, 'w') as f:
        f.write(pretty_docs.build_md_page(page_info))
    except OSError as e:
      print('Cannot write documentation for %s to %s: %s' % (full_name,
                                                             directory, e))
      raise

  if yaml_toc:
    # Generate table of contents

    # Put modules in alphabetical order, case-insensitive
    modules = sorted(module_children.keys(), key=lambda a: a.upper())

    leftnav_path = os.path.join(output_dir, '_toc.yaml')
    with open(leftnav_path, 'w') as f:

      # Generate header
      f.write('# Automatically generated file; please do not edit\ntoc:\n')
      for module in modules:
        indent_num = module.count('.')
        # Don't list `tf.submodule` inside `tf`
        indent_num = max(indent_num, 1)
        indent = '  '*indent_num

        if indent_num > 1:
          # tf.contrib.baysflow.entropy will be under
          #   tf.contrib->baysflow->entropy
          title = module.split('.')[-1]
        else:
          title = module

        header = [
            '- title: ' + title,
            '  section:',
            '  - title: Overview',
            '    path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[module]]
        header = ''.join([indent+line+'\n' for line in header])
        f.write(header)

        symbols_in_module = module_children.get(module, [])
        # Sort case-insensitive, if equal sort case sensitive (upper first)
        symbols_in_module.sort(key=lambda a: (a.upper(), a))

        for full_name in symbols_in_module:
          item = [
              '  - title: ' + full_name[len(module) + 1:],
              '    path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[full_name]]
          item = ''.join([indent+line+'\n' for line in item])
          f.write(item)

  # Write a global index containing all full names with links.
  with open(os.path.join(output_dir, 'index.md'), 'w') as f:
    f.write(
        parser.generate_global_index(root_title, parser_config.index,
                                     parser_config.reference_resolver))
Example #35
0
 def testLayers(self):
     # pylint: disable=g-import-not-at-top
     import tensorflow as tf
     assert tf_inspect.ismodule(tf.contrib.layers)
Example #36
0
 def testContrib(self):
     # pylint: disable=g-import-not-at-top
     import tensorflow as tf
     _ = tf.contrib.layers  # `tf.contrib` is loaded lazily on first use.
     assert tf_inspect.ismodule(tf.contrib)
Example #37
0
  def SmartSet(self, obj, attr_name, new_attr):
    """Replace obj.attr_name with new_attr.

    This method is smart and works at the module, class, and instance level
    while preserving proper inheritance. It will not stub out C types however
    unless that has been explicitly allowed by the type.

    This method supports the case where attr_name is a staticmethod or a
    classmethod of obj.

    Notes:
      - If obj is an instance, then it is its class that will actually be
        stubbed. Note that the method Set() does not do that: if obj is
        an instance, it (and not its class) will be stubbed.
      - The stubbing is using the builtin getattr and setattr. So, the __get__
        and __set__ will be called when stubbing (TODO: A better idea would
        probably be to manipulate obj.__dict__ instead of getattr() and
        setattr()).

    Args:
      obj: The object whose attributes we want to modify.
      attr_name: The name of the attribute to modify.
      new_attr: The new value for the attribute.

    Raises:
      AttributeError: If the attribute cannot be found.
    """
    _, obj = tf_decorator.unwrap(obj)
    if (tf_inspect.ismodule(obj) or
        (not tf_inspect.isclass(obj) and attr_name in obj.__dict__)):
      orig_obj = obj
      orig_attr = getattr(obj, attr_name)
    else:
      if not tf_inspect.isclass(obj):
        mro = list(tf_inspect.getmro(obj.__class__))
      else:
        mro = list(tf_inspect.getmro(obj))

      mro.reverse()

      orig_attr = None
      found_attr = False

      for cls in mro:
        try:
          orig_obj = cls
          orig_attr = getattr(obj, attr_name)
          found_attr = True
        except AttributeError:
          continue

      if not found_attr:
        raise AttributeError('Attribute not found.')

    # Calling getattr() on a staticmethod transforms it to a 'normal' function.
    # We need to ensure that we put it back as a staticmethod.
    old_attribute = obj.__dict__.get(attr_name)
    if old_attribute is not None and isinstance(old_attribute, staticmethod):
      orig_attr = staticmethod(orig_attr)

    self.stubs.append((orig_obj, attr_name, orig_attr))
    setattr(orig_obj, attr_name, new_attr)
Example #38
0
    def __call__(self, path, parent, children):
        # The path to the object.
        lib_path = 'tensorflow.%s' % path if path else 'tensorflow'
        _, parent = tf_decorator.unwrap(parent)

        # A small helper method to construct members(children) protos.
        def _AddMember(member_name, member_obj, proto):
            """Add the child object to the object being constructed."""
            _, member_obj = tf_decorator.unwrap(member_obj)
            if (_SkipMember(parent, member_name) or isinstance(
                    member_obj, deprecation.HiddenTfApiAttribute)):
                return
            if member_name == '__init__' or not member_name.startswith('_'):
                if tf_inspect.isroutine(member_obj):
                    new_method = proto.member_method.add()
                    new_method.name = member_name
                    # If member_obj is a python builtin, there is no way to get its
                    # argspec, because it is implemented on the C side. It also has no
                    # func_code.
                    if hasattr(member_obj, '__code__'):
                        new_method.argspec = _SanitizedArgSpec(member_obj)
                else:
                    new_member = proto.member.add()
                    new_member.name = member_name
                    if tf_inspect.ismodule(member_obj):
                        new_member.mtype = "<type \'module\'>"
                    else:
                        new_member.mtype = _NormalizeType(str(
                            type(member_obj)))

        parent_corner_cases = _CORNER_CASES.get(path, {})

        if path not in _CORNER_CASES or parent_corner_cases:
            # Decide if we have a module or a class.
            if tf_inspect.ismodule(parent):
                # Create a module object.
                module_obj = api_objects_pb2.TFAPIModule()
                for name, child in children:
                    if name in parent_corner_cases:
                        # If we have an empty entry, skip this object.
                        if parent_corner_cases[name]:
                            module_obj.member.add(
                                **(parent_corner_cases[name]))
                    else:
                        _AddMember(name, child, module_obj)

                # Store the constructed module object.
                self._protos[lib_path] = api_objects_pb2.TFAPIObject(
                    path=lib_path, tf_module=module_obj)
            elif _IsProtoClass(parent):
                proto_obj = api_objects_pb2.TFAPIProto()
                parent.DESCRIPTOR.CopyToProto(proto_obj.descriptor)

                # Store the constructed proto object.
                self._protos[lib_path] = api_objects_pb2.TFAPIObject(
                    path=lib_path, tf_proto=proto_obj)
            elif tf_inspect.isclass(parent):
                # Construct a class.
                class_obj = api_objects_pb2.TFAPIClass()
                class_obj.is_instance.extend(
                    _NormalizeIsInstance(i) for i in _SanitizedMRO(parent))
                for name, child in children:
                    if name in parent_corner_cases:
                        # If we have an empty entry, skip this object.
                        if parent_corner_cases[name]:
                            class_obj.member.add(**(parent_corner_cases[name]))
                    else:
                        _AddMember(name, child, class_obj)

                # Store the constructed class object.
                self._protos[lib_path] = api_objects_pb2.TFAPIObject(
                    path=lib_path, tf_class=class_obj)
            else:
                logging.error(
                    'Illegal call to ApiProtoDump::_py_obj_to_proto.'
                    'Object is neither a module nor a class: %s', path)
Example #39
0
def docs_for_object(full_name, py_object, parser_config):
    """Return a PageInfo object describing a given object from the TF API.

  This function uses _parse_md_docstring to parse the docs pertaining to
  `object`.

  This function resolves '@{symbol}' references in the docstrings into links to
  the appropriate location. It also adds a list of alternative names for the
  symbol automatically.

  It assumes that the docs for each object live in a file given by
  `documentation_path`, and that relative links to files within the
  documentation are resolvable.

  Args:
    full_name: The fully qualified name of the symbol to be
      documented.
    py_object: The Python object to be documented. Its documentation is sourced
      from `py_object`'s docstring.
    parser_config: A ParserConfig object.

  Returns:
    Either a `_FunctionPageInfo`, `_ClassPageInfo`, or a `_ModulePageInfo`
    depending on the type of the python object being documented.

  Raises:
    RuntimeError: If an object is encountered for which we don't know how
      to make docs.
  """

    # Which other aliases exist for the object referenced by full_name?
    master_name = parser_config.reference_resolver.py_master_name(full_name)
    duplicate_names = parser_config.duplicates.get(master_name, [full_name])

    # TODO(wicke): Once other pieces are ready, enable this also for partials.
    if (tf_inspect.ismethod(py_object) or tf_inspect.isfunction(py_object) or
            # Some methods in classes from extensions come in as routines.
            tf_inspect.isroutine(py_object)):
        page_info = _FunctionPageInfo(master_name)
        page_info.set_signature(py_object, parser_config.reverse_index)

    elif tf_inspect.isclass(py_object):
        page_info = _ClassPageInfo(master_name)
        page_info.collect_docs_for_class(py_object, parser_config)

    elif tf_inspect.ismodule(py_object):
        page_info = _ModulePageInfo(master_name)
        page_info.collect_docs_for_module(parser_config)

    else:
        raise RuntimeError('Cannot make docs for object %s: %r' %
                           (full_name, py_object))

    relative_path = os.path.relpath(
        path='.', start=os.path.dirname(documentation_path(full_name)) or '.')

    page_info.set_doc(
        _parse_md_docstring(py_object, relative_path,
                            parser_config.reference_resolver))

    page_info.set_aliases(duplicate_names)

    page_info.set_guides(
        _get_guides_markdown(duplicate_names, parser_config.guide_index,
                             relative_path))

    page_info.set_defined_in(_get_defined_in(py_object, parser_config))

    return page_info
Example #40
0
    def visit_FunctionDef(self, node):
        self.generic_visit(node)
        kept_decorators = []
        for dec in node.decorator_list:
            if isinstance(dec, gast.Call):
                dec_func = dec.func
            else:
                dec_func = dec

            # Special cases.
            # TODO(mdan): Is there any way we can treat these more generically?
            # We may want to forego using decorators altogether if we can't
            # properly support them.
            if isinstance(dec_func,
                          gast.Name) and dec_func.id in ('classmethod', ):
                # Assumption: decorators are only visible in the AST when converting
                # a function inline (via another decorator).
                # In that case, the converted function is no longer part of the
                # original object that it was declared into.
                # This is currently verified by tests.
                continue

            if not anno.hasanno(dec_func, 'live_val'):
                raise ValueError('could not resolve the decorator "@%s"' %
                                 (anno.getanno(dec_func, anno.Basic.QN)))

            original_dec = anno.getanno(dec_func, anno.Basic.QN)
            dec_value = anno.getanno(dec_func, 'live_val')

            if dec_value in self.ctx.program.options.strip_decorators:
                continue

            # When using foo.bar.baz, we only really need to grab foo and import
            # that.
            dec_support_node = dec_func
            while isinstance(dec_support_node, gast.Attribute):
                dec_support_node = dec_support_node.value

            if not anno.hasanno(dec_support_node, 'live_val'):
                raise ValueError(
                    'could not resolve symbol "%s" when looking up decorator "%s"'
                    % (anno.getanno(dec_support_node,
                                    anno.Basic.QN), original_dec))

            dec_support = anno.getanno(dec_support_node, 'live_val')
            # The tuple contains:
            #  * the AST that represents the decorator
            #  * the entity supporting the decorator (i.e., what we need to import)
            #  * the name of the module that needs to be imported for this decorator
            #    to properly resolve.
            # Examples:
            #  for foo.bar, the tuple is (<ast>, <module foo>, 'foo')
            #  for baz, the tuple is (<ast>, <module baz.__module__>, 'baz')
            kept_decorators.append(
                (dec, dec_support, anno.getanno(dec_support_node,
                                                anno.Basic.QN)))

        for _, dec_support, name in kept_decorators:
            if tf_inspect.ismodule(dec_support):
                self.ctx.program.additional_imports.add(
                    'import %s as %s' % (dec_support.__name__, name))
            else:
                if dec_support.__module__ == '__main__':
                    raise ValueError(
                        'decorator "%s" was not allowed because it is declared '
                        'in the module "%s". To fix this, declare it in a separate '
                        'module that we can import it from.' %
                        (dec_support, dec_support.__module__))
                self.ctx.program.additional_imports.add(
                    'from %s import %s' % (dec_support.__module__, name))

        node.decorator_list = [dec for dec, _, _ in kept_decorators]
        return node
 def testWrapperIsAModule(self):
   module = MockModule('test')
   wrapped_module = module_wrapper.TFModuleWrapper(module, 'test')
   self.assertTrue(tf_inspect.ismodule(wrapped_module))
 def testWrapperIsAModule(self):
   module = MockModule('test')
   wrapped_module = deprecation_wrapper.DeprecationWrapper(
       module, 'test')
   self.assertTrue(tf_inspect.ismodule(wrapped_module))
 def testIsModule(self):
     self.assertTrue(
         tf_inspect.ismodule(inspect.getmodule(inspect.currentframe())))
     self.assertFalse(tf_inspect.ismodule(test_decorated_function))
 def testIsModule(self):
   self.assertTrue(
       tf_inspect.ismodule(inspect.getmodule(inspect.currentframe())))
   self.assertFalse(tf_inspect.ismodule(test_decorated_function))
Example #45
0
def write_docs(output_dir,
               parser_config,
               yaml_toc,
               root_title='TensorFlow',
               search_hints=True,
               site_api_path=None):
    """Write previously extracted docs to disk.

  Write a docs page for each symbol included in the indices of parser_config to
  a tree of docs at `output_dir`.

  Symbols with multiple aliases will have only one page written about
  them, which is referenced for all aliases.

  Args:
    output_dir: Directory to write documentation markdown files to. Will be
      created if it doesn't exist.
    parser_config: A `parser.ParserConfig` object, containing all the necessary
      indices.
    yaml_toc: Set to `True` to generate a "_toc.yaml" file.
    root_title: The title name for the root level index.md.
    search_hints: (bool) include meta-data search hints at the top of each
      output file.
    site_api_path: Used to write the api-duplicates _redirects.yaml file. if
      None (the default) the file is not generated.

  Raises:
    ValueError: if `output_dir` is not an absolute path
  """
    # Make output_dir.
    if not os.path.isabs(output_dir):
        raise ValueError("'output_dir' must be an absolute path.\n"
                         "    output_dir='%s'" % output_dir)

    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # These dictionaries are used for table-of-contents generation below
    # They will contain, after the for-loop below::
    #  - module name(string):classes and functions the module contains(list)
    module_children = {}
    #  - symbol name(string):pathname (string)
    symbol_to_file = {}

    # Collect redirects for an api _redirects.yaml file.
    redirects = ['redirects:\n']

    # Parse and write Markdown pages, resolving cross-links (@{symbol}).
    for full_name, py_object in six.iteritems(parser_config.index):
        parser_config.reference_resolver.current_doc_full_name = full_name

        if full_name in parser_config.duplicate_of:
            continue

        # Methods and some routines are documented only as part of their class.
        if not (tf_inspect.ismodule(py_object)
                or tf_inspect.isclass(py_object) or _is_free_function(
                    py_object, full_name, parser_config.index)):
            continue

        sitepath = os.path.join('api_docs/python',
                                parser.documentation_path(full_name)[:-3])

        # For TOC, we need to store a mapping from full_name to the file
        # we're generating
        symbol_to_file[full_name] = sitepath

        # For a module, remember the module for the table-of-contents
        if tf_inspect.ismodule(py_object):
            if full_name in parser_config.tree:
                module_children.setdefault(full_name, [])

        # For something else that's documented,
        # figure out what module it lives in
        else:
            subname = str(full_name)
            while True:
                subname = subname[:subname.rindex('.')]
                if tf_inspect.ismodule(parser_config.index[subname]):
                    module_children.setdefault(subname, []).append(full_name)
                    break

        # Generate docs for `py_object`, resolving references.
        page_info = parser.docs_for_object(full_name, py_object, parser_config)

        path = os.path.join(output_dir, parser.documentation_path(full_name))
        directory = os.path.dirname(path)
        try:
            if not os.path.exists(directory):
                os.makedirs(directory)
            # This function returns raw bytes in PY2 or unicode in PY3.
            if search_hints:
                content = [page_info.get_metadata_html()]
            else:
                content = ['']

            content.append(pretty_docs.build_md_page(page_info))
            text = '\n'.join(content)
            if six.PY3:
                text = text.encode('utf-8')
            with open(path, 'wb') as f:
                f.write(text)
        except OSError:
            raise OSError('Cannot write documentation for %s to %s' %
                          (full_name, directory))

        if site_api_path:
            duplicates = parser_config.duplicates.get(full_name, [])
            if not duplicates:
                continue

            duplicates = [item for item in duplicates if item != full_name]
            template = ('- from: /{}\n' '  to: /{}\n')
            for dup in duplicates:
                from_path = os.path.join(site_api_path, dup.replace('.', '/'))
                to_path = os.path.join(site_api_path,
                                       full_name.replace('.', '/'))
                redirects.append(template.format(from_path, to_path))

    if site_api_path:
        api_redirects_path = os.path.join(output_dir, '_redirects.yaml')
        with open(api_redirects_path, 'w') as redirect_file:
            redirect_file.write(''.join(redirects))

    if yaml_toc:
        # Generate table of contents

        # Put modules in alphabetical order, case-insensitive
        modules = sorted(module_children.keys(), key=lambda a: a.upper())

        leftnav_path = os.path.join(output_dir, '_toc.yaml')
        with open(leftnav_path, 'w') as f:

            # Generate header
            f.write(
                '# Automatically generated file; please do not edit\ntoc:\n')
            for module in modules:
                indent_num = module.count('.')
                # Don't list `tf.submodule` inside `tf`
                indent_num = max(indent_num, 1)
                indent = '  ' * indent_num

                if indent_num > 1:
                    # tf.contrib.baysflow.entropy will be under
                    #   tf.contrib->baysflow->entropy
                    title = module.split('.')[-1]
                else:
                    title = module

                header = [
                    '- title: ' + title, '  section:', '  - title: Overview',
                    '    path: /TARGET_DOC_ROOT/VERSION/' +
                    symbol_to_file[module]
                ]
                header = ''.join([indent + line + '\n' for line in header])
                f.write(header)

                symbols_in_module = module_children.get(module, [])
                # Sort case-insensitive, if equal sort case sensitive (upper first)
                symbols_in_module.sort(key=lambda a: (a.upper(), a))

                for full_name in symbols_in_module:
                    item = [
                        '  - title: ' + full_name[len(module) + 1:],
                        '    path: /TARGET_DOC_ROOT/VERSION/' +
                        symbol_to_file[full_name]
                    ]
                    item = ''.join([indent + line + '\n' for line in item])
                    f.write(item)

    # Write a global index containing all full names with links.
    with open(os.path.join(output_dir, 'index.md'), 'w') as f:
        f.write(
            parser.generate_global_index(root_title, parser_config.index,
                                         parser_config.reference_resolver))
Example #46
0
 def testContrib(self):
   # pylint: disable=g-import-not-at-top
   import tensorflow as tf
   _ = tf.contrib.layers  # `tf.contrib` is loaded lazily on first use.
   assert tf_inspect.ismodule(tf.contrib)
Example #47
0
 def testLayers(self):
   # pylint: disable=g-import-not-at-top
   import tensorflow as tf
   assert tf_inspect.ismodule(tf.contrib.layers)
Example #48
0
def write_docs(output_dir,
               parser_config,
               yaml_toc,
               root_title='TensorFlow',
               search_hints=True,
               site_api_path=''):
  """Write previously extracted docs to disk.

  Write a docs page for each symbol included in the indices of parser_config to
  a tree of docs at `output_dir`.

  Symbols with multiple aliases will have only one page written about
  them, which is referenced for all aliases.

  Args:
    output_dir: Directory to write documentation markdown files to. Will be
      created if it doesn't exist.
    parser_config: A `parser.ParserConfig` object, containing all the necessary
      indices.
    yaml_toc: Set to `True` to generate a "_toc.yaml" file.
    root_title: The title name for the root level index.md.
    search_hints: (bool) include meta-data search hints at the top of each
      output file.
    site_api_path: The output path relative to the site root. Used in the
      `_toc.yaml` and `_redirects.yaml` files.

  Raises:
    ValueError: if `output_dir` is not an absolute path
  """
  # Make output_dir.
  if not os.path.isabs(output_dir):
    raise ValueError("'output_dir' must be an absolute path.\n"
                     "    output_dir='%s'" % output_dir)

  if not os.path.exists(output_dir):
    os.makedirs(output_dir)

  # These dictionaries are used for table-of-contents generation below
  # They will contain, after the for-loop below::
  #  - module name(string):classes and functions the module contains(list)
  module_children = {}
  #  - symbol name(string):pathname (string)
  symbol_to_file = {}

  # Collect redirects for an api _redirects.yaml file.
  redirects = []

  # Parse and write Markdown pages, resolving cross-links (@{symbol}).
  for full_name, py_object in six.iteritems(parser_config.index):
    parser_config.reference_resolver.current_doc_full_name = full_name

    if full_name in parser_config.duplicate_of:
      continue

    # Methods and some routines are documented only as part of their class.
    if not (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or
            parser.is_free_function(py_object, full_name, parser_config.index)):
      continue

    sitepath = os.path.join('api_docs/python',
                            parser.documentation_path(full_name)[:-3])

    # For TOC, we need to store a mapping from full_name to the file
    # we're generating
    symbol_to_file[full_name] = sitepath

    # For a module, remember the module for the table-of-contents
    if tf_inspect.ismodule(py_object):
      if full_name in parser_config.tree:
        module_children.setdefault(full_name, [])

    # For something else that's documented,
    # figure out what module it lives in
    else:
      subname = str(full_name)
      while True:
        subname = subname[:subname.rindex('.')]
        if tf_inspect.ismodule(parser_config.index[subname]):
          module_children.setdefault(subname, []).append(full_name)
          break

    # Generate docs for `py_object`, resolving references.
    page_info = parser.docs_for_object(full_name, py_object, parser_config)

    path = os.path.join(output_dir, parser.documentation_path(full_name))
    directory = os.path.dirname(path)
    try:
      if not os.path.exists(directory):
        os.makedirs(directory)
      # This function returns raw bytes in PY2 or unicode in PY3.
      if search_hints:
        content = [page_info.get_metadata_html()]
      else:
        content = ['']

      content.append(pretty_docs.build_md_page(page_info))
      text = '\n'.join(content)
      if six.PY3:
        text = text.encode('utf-8')
      with open(path, 'wb') as f:
        f.write(text)
    except OSError:
      raise OSError(
          'Cannot write documentation for %s to %s' % (full_name, directory))

    duplicates = parser_config.duplicates.get(full_name, [])
    if not duplicates:
      continue

    duplicates = [item for item in duplicates if item != full_name]

    for dup in duplicates:
      from_path = os.path.join(site_api_path, dup.replace('.', '/'))
      to_path = os.path.join(site_api_path, full_name.replace('.', '/'))
      redirects.append((
          os.path.join('/', from_path),
          os.path.join('/', to_path)))

  if redirects:
    redirects = sorted(redirects)
    template = ('- from: {}\n'
                '  to: {}\n')
    redirects = [template.format(f, t) for f, t in redirects]
    api_redirects_path = os.path.join(output_dir, '_redirects.yaml')
    with open(api_redirects_path, 'w') as redirect_file:
      redirect_file.write('redirects:\n')
      redirect_file.write(''.join(redirects))

  if yaml_toc:
    # Generate table of contents

    # Put modules in alphabetical order, case-insensitive
    modules = sorted(module_children.keys(), key=lambda a: a.upper())

    leftnav_path = os.path.join(output_dir, '_toc.yaml')
    with open(leftnav_path, 'w') as f:

      # Generate header
      f.write('# Automatically generated file; please do not edit\ntoc:\n')
      for module in modules:
        indent_num = module.count('.')
        # Don't list `tf.submodule` inside `tf`
        indent_num = max(indent_num, 1)
        indent = '  '*indent_num

        if indent_num > 1:
          # tf.contrib.baysflow.entropy will be under
          #   tf.contrib->baysflow->entropy
          title = module.split('.')[-1]
        else:
          title = module

        header = [
            '- title: ' + title,
            '  section:',
            '  - title: Overview',
            '    path: ' + os.path.join('/', site_api_path,
                                        symbol_to_file[module])]
        header = ''.join([indent+line+'\n' for line in header])
        f.write(header)

        symbols_in_module = module_children.get(module, [])
        # Sort case-insensitive, if equal sort case sensitive (upper first)
        symbols_in_module.sort(key=lambda a: (a.upper(), a))

        for full_name in symbols_in_module:
          item = [
              '  - title: ' + full_name[len(module) + 1:],
              '    path: ' + os.path.join('/', site_api_path,
                                          symbol_to_file[full_name])]
          item = ''.join([indent+line+'\n' for line in item])
          f.write(item)

  # Write a global index containing all full names with links.
  with open(os.path.join(output_dir, 'index.md'), 'w') as f:
    f.write(
        parser.generate_global_index(root_title, parser_config.index,
                                     parser_config.reference_resolver))