Example #1
0
def parse_entity(entity, future_features):
  """Returns the AST and source code of given entity.

  Args:
    entity: Any, Python function/method/class
    future_features: Iterable[Text], future features to use (e.g.
      'print_statement'). See
      https://docs.python.org/2/reference/simple_stmts.html#future

  Returns:
    gast.AST, Text: the parsed AST node; the source code that was parsed to
    generate the AST (including any prefixes that this function may have added).
  """
  if inspect_utils.islambda(entity):
    return _parse_lambda(entity)

  try:
    original_source = inspect_utils.getimmediatesource(entity)
  except (IOError, OSError) as e:
    raise ValueError(
        'Unable to locate the source code of {}. Note that functions defined'
        ' in certain environments, like the interactive Python shell, do not'
        ' expose their source code. If that is the case, you should define'
        ' them in a .py source file. If you are certain the code is'
        ' graph-compatible, wrap the call using'
        ' @tf.autograph.experimental.do_not_convert. Original error: {}'.format(
            entity, e))

  source = dedent_block(original_source)

  future_statements = tuple(
      'from __future__ import {}'.format(name) for name in future_features)
  source = '\n'.join(future_statements + (source,))

  return parse(source, preamble_len=len(future_features)), source
Example #2
0
    def test_getimmediatesource_normal_decorator_different_module(self):

        expected = textwrap.dedent("""
      def standalone_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    """)

        @decorators.standalone_decorator
        def test_fn(a):
            """Test docstring."""
            return [a]

        self.assertSourceIdentical(inspect_utils.getimmediatesource(test_fn),
                                   expected)
  def test_getimmediatesource_normal_decorator_different_module(self):

    expected = textwrap.dedent("""
      def standalone_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    """)

    @decorators.standalone_decorator
    def test_fn(a):
      """Test docstring."""
      return [a]

    self.assertSourceIdentical(
        inspect_utils.getimmediatesource(test_fn), expected)
Example #4
0
    def test_getimmediatesource_functools_wrapper_different_module(self):

        expected = textwrap.dedent("""
      @functools.wraps(f)
      def wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    """)

        @decorators.wrapping_decorator
        def test_fn(a):
            """Test docstring."""
            return [a]

        self.assertSourceIdentical(inspect_utils.getimmediatesource(test_fn),
                                   expected)
  def test_getimmediatesource_functools_wrapper_different_module(self):

    expected = textwrap.dedent("""
      @functools.wraps(f)
      def wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    """)

    @decorators.wrapping_decorator
    def test_fn(a):
      """Test docstring."""
      return [a]

    self.assertSourceIdentical(
        inspect_utils.getimmediatesource(test_fn), expected)
Example #6
0
    def test_getimmediatesource_noop_decorator(self):
        def test_decorator(f):
            return f

        expected = '''
      @test_decorator
      def test_fn(a):
        """Test docstring."""
        return [a]
    '''

        @test_decorator
        def test_fn(a):
            """Test docstring."""
            return [a]

        self.assertSourceIdentical(inspect_utils.getimmediatesource(test_fn),
                                   expected)
Example #7
0
    def test_getimmediatesource_basic(self):
        def test_decorator(f):
            def f_wrapper(*args, **kwargs):
                return f(*args, **kwargs)

            return f_wrapper

        expected = """
      def f_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    """

        @test_decorator
        def test_fn(a):
            """Test docstring."""
            return [a]

        self.assertSourceIdentical(inspect_utils.getimmediatesource(test_fn),
                                   expected)
  def test_getimmediatesource_noop_decorator(self):

    def test_decorator(f):
      return f

    expected = '''
      @test_decorator
      def test_fn(a):
        """Test docstring."""
        return [a]
    '''

    @test_decorator
    def test_fn(a):
      """Test docstring."""
      return [a]

    self.assertSourceIdentical(
        inspect_utils.getimmediatesource(test_fn), expected)
  def test_getimmediatesource_basic(self):

    def test_decorator(f):

      def f_wrapper(*args, **kwargs):
        return f(*args, **kwargs)

      return f_wrapper

    expected = """
      def f_wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    """

    @test_decorator
    def test_fn(a):
      """Test docstring."""
      return [a]

    self.assertSourceIdentical(
        inspect_utils.getimmediatesource(test_fn), expected)
Example #10
0
def parse_entity(entity, future_features):
    """Returns the AST and source code of given entity.

  Args:
    entity: Any, Python function/method/class
    future_features: Iterable[Text], future features to use (e.g.
      'print_statement'). See
      https://docs.python.org/2/reference/simple_stmts.html#future

  Returns:
    gast.AST, Text: the parsed AST node; the source code that was parsed to
    generate the AST (including any prefixes that this function may have added).
  """
    try:
        original_source = inspect_utils.getimmediatesource(entity)
    except (IOError, OSError) as e:
        raise ValueError(
            'Unable to locate the source code of {}. Note that functions defined'
            ' in certain environments, like the interactive Python shell do not'
            ' expose their source code. If that is the case, you should to define'
            ' them in a .py source file. If you are certain the code is'
            ' graph-compatible, wrap the call using'
            ' @tf.autograph.do_not_convert. Original error: {}'.format(
                entity, e))

    def raise_parse_failure(comment):
        raise ValueError(
            'Failed to parse source code of {}, which Python reported as:\n{}\n'
            '{}'.format(entity, original_source, comment))

    # Comments and multiline strings can appear at arbitrary indentation levels,
    # causing textwrap.dedent to not correctly dedent source code.
    # TODO(b/115884650): Automatic handling of comments/multiline strings.
    source = textwrap.dedent(original_source)

    future_statements = tuple('from __future__ import {}'.format(name)
                              for name in future_features)
    source = '\n'.join(future_statements + (source, ))

    try:
        return parse_str(source, preamble_len=len(future_features)), source

    except IndentationError:
        # The text below lists the causes of this error known to us. There may
        # be more.
        raise_parse_failure(
            'This may be caused by multiline strings or comments not indented at'
            ' the same level as the code.')

    except SyntaxError as e:
        if not tf_inspect.isfunction(entity) or entity.__name__ != '<lambda>':
            raise

        # Certain entities, like lambdas, only hold the raw code lines which defined
        # them, which may include surrounding tokens and may be syntactically
        # invalid out of context. For example:
        #
        #     l = (
        #         lambda x: x,)[0]
        #
        # will have the dedented source "lambda x: x,)[0]"
        # Here we make an attempt to stip away the garbage by looking at the
        # information in the syntax error.
        lines = source.split('\n')
        lineno, offset = e.lineno, e.offset  # 1-based

        # Give up if there's nothing we can chip away.
        if len(lines) == lineno and len(lines[-1]) == offset:
            raise_parse_failure(
                'If this is a lambda function, the error may be avoided by creating'
                ' the lambda in a standalone statement.')

        # Drop all lines following the error location
        # TODO(mdan): What's with the pylint errors?
        lines = lines[:lineno]  # pylint:disable=invalid-slice-index
        # Drop all characters following the error location
        lines[-1] = lines[-1][:offset - 1]  # pylint:disable=invalid-slice-index
        source = '\n'.join(lines)

        try:
            return parse_str(source, preamble_len=len(future_features)), source
        except SyntaxError as e:
            raise_parse_failure(
                'If this is a lambda function, the error may be avoided by creating'
                ' the lambda in a standalone statement.')
Example #11
0
def parse_entity(entity, future_features):
  """Returns the AST and source code of given entity.

  Args:
    entity: Any, Python function/method/class
    future_features: Iterable[Text], future features to use (e.g.
      'print_statement'). See
      https://docs.python.org/2/reference/simple_stmts.html#future

  Returns:
    gast.AST, Text: the parsed AST node; the source code that was parsed to
    generate the AST (including any prefixes that this function may have added).
  """
  try:
    source = inspect_utils.getimmediatesource(entity)
  except (IOError, OSError) as e:
    raise ValueError(
        'Unable to locate the source code of {}. Note that functions defined'
        ' in certain environments, like the interactive Python shell do not'
        ' expose their source code. If that is the case, you should to define'
        ' them in a .py source file. If you are certain the code is'
        ' graph-compatible, wrap the call using'
        ' @tf.autograph.do_not_convert. Original error: {}'.format(entity, e))

  def raise_parse_failure(comment):
    raise ValueError(
        'Failed to parse source code of {}, which Python reported as:\n{}\n'
        '{}'.format(entity, source, comment))

  # Comments and multiline strings can appear at arbitrary indentation levels,
  # causing textwrap.dedent to not correctly dedent source code.
  # TODO(b/115884650): Automatic handling of comments/multiline strings.
  source = textwrap.dedent(source)

  future_statements = tuple(
      'from __future__ import {}'.format(name) for name in future_features)
  source = '\n'.join(future_statements + (source,))

  try:
    return parse_str(source, preamble_len=len(future_features)), source

  except IndentationError:
    # The text below lists the causes of this error known to us. There may
    # be more.
    raise_parse_failure(
        'This may be caused by multiline strings or comments not indented at'
        ' the same level as the code.')

  except SyntaxError as e:
    if not tf_inspect.isfunction(entity) or entity.__name__ != '<lambda>':
      raise

    # Certain entities, like lambdas, only hold the raw code lines which defined
    # them, which may include surrounding tokens and may be syntactically
    # invalid out of context. For example:
    #
    #     l = (
    #         lambda x: x,)[0]
    #
    # will have the dedented source "lambda x: x,)[0]"
    # Here we make an attempt to stip away the garbage by looking at the
    # information in the syntax error.
    lines = source.split('\n')
    lineno, offset = e.lineno, e.offset  # 1-based

    # Give up if there's nothing we can chip away.
    if len(lines) == lineno and len(lines[-1]) == offset:
      raise_parse_failure(
          'If this is a lambda function, the error may be avoided by creating'
          ' the lambda in a standalone statement.')

    # Drop all lines following the error location
    # TODO(mdan): What's with the pylint errors?
    lines = lines[:lineno]  # pylint:disable=invalid-slice-index
    # Drop all characters following the error location
    lines[-1] = lines[-1][:offset - 1]  # pylint:disable=invalid-slice-index
    source = '\n'.join(lines)

    try:
      return parse_str(source, preamble_len=len(future_features)), source
    except SyntaxError as e:
      raise_parse_failure(
          'If this is a lambda function, the error may be avoided by creating'
          ' the lambda in a standalone statement. Tried to strip down the'
          ' source to:\n{}\nBut that did not work.'.format(source))