def parse_build_file()

in src/python/pants/build_graph/build_file_parser.py [0:0]


  def parse_build_file(self, build_file):
    """Capture Addressable instances from parsing `build_file`.
    Prepare a context for parsing, read a BUILD file from the filesystem, and return the
    Addressable instances generated by executing the code.
    """

    def _format_context_msg(lineno, offset, error_type, message):
      """Show the line of the BUILD file that has the error along with a few line of context"""
      build_contents = build_file.source().decode('utf-8')
      context = "While parsing {build_file}:\n".format(build_file=build_file)
      curr_lineno = 0
      for line in build_contents.split('\n'):
        line = line.encode('ascii', 'backslashreplace')
        curr_lineno += 1
        if curr_lineno == lineno:
          highlight = '*'
        else:
          highlight = ' '
        if curr_lineno >= lineno - 3:
          context += "{highlight}{curr_lineno:4d}: {line}\n".format(
            highlight=highlight, line=line, curr_lineno=curr_lineno)
          if lineno == curr_lineno:
            if offset:
              context += ("       {caret:>{width}} {error_type}: {message}\n\n"
                          .format(caret="^", width=int(offset), error_type=error_type,
                                  message=message))
            else:
              context += ("        {error_type}: {message}\n\n"
                          .format(error_type=error_type, message=message))
        if curr_lineno > lineno + 3:
          break
      return context

    logger.debug("Parsing BUILD file {build_file}."
                 .format(build_file=build_file))

    try:
      build_file_code = build_file.code()
    except SyntaxError as e:
      raise self.ParseError(_format_context_msg(e.lineno, e.offset, e.__class__.__name__, e))
    except Exception as e:
      raise self.ParseError("{error_type}: {message}\n while parsing BUILD file {build_file}"
                            .format(error_type=e.__class__.__name__,
                                    message=e, build_file=build_file))

    parse_state = self._build_configuration.initialize_parse_state(build_file)
    try:
      with warnings.catch_warnings(record=True) as warns:
        six.exec_(build_file_code, parse_state.parse_globals)
        for warn in warns:
          logger.warning(_format_context_msg(lineno=warn.lineno,
                                             offset=None,
                                             error_type=warn.category.__name__,
                                             message=warn.message))
    except Exception as e:
      raise self.ExecuteError("{message}\n while executing BUILD file {build_file}"
                              .format(message=e, build_file=build_file))

    name_map = {}
    for addressable in parse_state.objects:
      name = addressable.addressed_name
      logger.debug('Adding {addressable} to the BuildFileParser address map for {build_file} with {name}'
                   .format(addressable=addressable,
                           build_file=build_file,
                           name=name))
      if name in name_map:
        raise self.AddressableConflictException(
          "File {conflicting_file} defines address '{target_name}' more than once."
          .format(conflicting_file=build_file,
                  target_name=name))
      name_map[name] = addressable

    logger.debug("{build_file} produced the following Addressables:"
                 .format(build_file=build_file))
    address_map = {}
    for name, addressable in name_map.items():
      address_map[BuildFileAddress(build_file=build_file, target_name=name)] = addressable
      logger.debug("  * {name}: {addressable}"
                   .format(name=name,
                           addressable=addressable))

    return address_map