Example #1
0
    def _generate_html(testsuites):
        values = {
            'total_tests': 0,
            'total_errors': 0,
            'total_failures': 0,
            'total_skipped': 0,
            'total_time': 0.0
        }

        for testsuite in testsuites:
            values['total_tests'] += testsuite.tests
            values['total_errors'] += testsuite.errors
            values['total_failures'] += testsuite.failures
            values['total_skipped'] += testsuite.skipped
            values['total_time'] += testsuite.time

        values['total_success'] = ReportTestSuite.success_rate(
            values['total_tests'], values['total_errors'],
            values['total_failures'], values['total_skipped'])
        values['summary_icon_class'] = ReportTestSuite.icon_class(
            values['total_tests'], values['total_errors'],
            values['total_failures'], values['total_skipped'])
        values['testsuites'] = [ts.as_dict() for ts in testsuites]

        package_name, _, _ = __name__.rpartition('.')
        renderer = MustacheRenderer(package_name=package_name)
        html = renderer.render_name('junit_report.html', values)
        return html
Example #2
0
    def _generate_html(testsuites):
        values = {
            "total_tests": 0,
            "total_errors": 0,
            "total_failures": 0,
            "total_skipped": 0,
            "total_time": 0.0,
        }

        for testsuite in testsuites:
            values["total_tests"] += testsuite.tests
            values["total_errors"] += testsuite.errors
            values["total_failures"] += testsuite.failures
            values["total_skipped"] += testsuite.skipped
            values["total_time"] += testsuite.time

        values["total_success"] = ReportTestSuite.success_rate(
            values["total_tests"],
            values["total_errors"],
            values["total_failures"],
            values["total_skipped"],
        )
        values["summary_icon_class"] = ReportTestSuite.icon_class(
            values["total_tests"],
            values["total_errors"],
            values["total_failures"],
            values["total_skipped"],
        )
        values["testsuites"] = [ts.as_dict() for ts in testsuites]

        package_name, _, _ = __name__.rpartition(".")
        renderer = MustacheRenderer(package_name=package_name)
        html = renderer.render_name("junit_report.html", values)
        return html
Example #3
0
  def _generate_html(testsuites):
    values = {
      'total_tests': 0,
      'total_errors': 0,
      'total_failures': 0,
      'total_skipped': 0,
      'total_time': 0.0
    }

    for testsuite in testsuites:
      values['total_tests'] += testsuite.tests
      values['total_errors'] += testsuite.errors
      values['total_failures'] += testsuite.failures
      values['total_skipped'] += testsuite.skipped
      values['total_time'] += testsuite.time

    values['total_success'] = ReportTestSuite.success_rate(values['total_tests'],
                                                           values['total_errors'],
                                                           values['total_failures'],
                                                           values['total_skipped'])
    values['summary_icon_class'] = ReportTestSuite.icon_class(values['total_tests'],
                                                              values['total_errors'],
                                                              values['total_failures'],
                                                              values['total_skipped'])
    values['testsuites'] = map(lambda ts: ts.as_dict(), testsuites)

    package_name, _, _ = __name__.rpartition('.')
    renderer = MustacheRenderer(package_name=package_name)
    html = renderer.render_name('junit_report.html', values)
    return html
    def __init__(self, *args, **kwargs):
        super(EnsimeGen, self).__init__(*args, **kwargs)

        self._renderer = MustacheRenderer(
            self.get_options().template_directory, __name__)
        self.project_filename = os.path.join(self.cwd, '.ensime')
        self.ensime_output_dir = os.path.join(self.gen_project_workdir, 'out')
Example #5
0
 def _do_render(self, filename, args):
     package_name, _, _ = __name__.rpartition('.')
     renderer = MustacheRenderer(package_name=package_name)
     output_path = os.path.join(self._outdir, os.path.basename(filename))
     self.context.log.info('Generating {}'.format(output_path))
     html = renderer.render_name(filename, args)
     with safe_open(output_path, 'w') as outfile:
         outfile.write(html.encode('utf8'))
 def _do_render(self, filename, args):
   package_name, _, _ = __name__.rpartition('.')
   renderer = MustacheRenderer(package_name=package_name)
   output_path = os.path.join(self._outdir, os.path.basename(filename))
   self.context.log.info('Generating {}'.format(output_path))
   html = renderer.render_name(filename, args)
   with safe_open(output_path, 'w') as outfile:
     outfile.write(html.encode('utf8'))
Example #7
0
 def _do_render(self, filename, args):
     package_name, _, _ = __name__.rpartition(".")
     renderer = MustacheRenderer(package_name=package_name)
     output_path = os.path.join(self._outdir, os.path.basename(filename))
     self.context.log.info("Generating {}".format(output_path))
     html = renderer.render_name(filename, args)
     with safe_open(output_path, "w") as outfile:
         outfile.write(html)
     return output_path
Example #8
0
  def __init__(self, run_tracker, settings):
    Reporter.__init__(self, run_tracker, settings)
     # The main report, and associated tool outputs, go under this dir.
    self._html_dir = settings.html_dir

    # We render HTML from mustache templates.
    self._renderer = MustacheRenderer(settings.template_dir, __name__)

    # We serve files relative to the build root.
    self._buildroot = get_buildroot()
    self._html_path_base = os.path.relpath(self._html_dir, self._buildroot)

    # We write the main report body to this file object.
    self._report_file = None

    # We redirect stdout, stderr etc. of tool invocations to these files.
    self._output_files = defaultdict(dict)  # workunit_id -> {path -> fileobj}.
Example #9
0
    def __init__(self, port, settings):
        renderer = MustacheRenderer(settings.template_dir, __name__)

        class MyHandler(PantsHandler):
            def __init__(self, request, client_address, server):
                PantsHandler.__init__(self, settings, renderer, request,
                                      client_address, server)

        self._httpd = BaseHTTPServer.HTTPServer(('', port), MyHandler)
        self._httpd.timeout = 0.1  # Not the network timeout, but how often handle_request yields.
Example #10
0
    def generate_html(self, testsuites):
        values = {"total_tests": 0, "total_errors": 0, "total_failures": 0, "total_skipped": 0, "total_time": 0.0}

        for testsuite in testsuites:
            values["total_tests"] += testsuite.tests
            values["total_errors"] += testsuite.errors
            values["total_failures"] += testsuite.failures
            values["total_skipped"] += testsuite.skipped
            values["total_time"] += testsuite.time

        values["total_success"] = ReportTestSuite.success_rate(
            values["total_tests"], values["total_errors"], values["total_failures"], values["total_skipped"]
        )
        values["summary_icon_class"] = ReportTestSuite.icon_class(
            values["total_tests"], values["total_errors"], values["total_failures"], values["total_skipped"]
        )
        values["testsuites"] = map(lambda ts: ts.as_dict(), testsuites)

        package_name, _, _ = __name__.rpartition(".")
        renderer = MustacheRenderer(package_name=package_name)
        html = renderer.render_name("junit_report.html", values)
        return html
Example #11
0
  def __init__(self, run_tracker, settings):
    super(HtmlReporter, self).__init__(run_tracker, settings)
     # The main report, and associated tool outputs, go under this dir.
    self._html_dir = settings.html_dir

    # We render HTML from mustache templates.
    self._renderer = MustacheRenderer(settings.template_dir, __name__)

    # We serve files relative to the build root.
    self._buildroot = get_buildroot()
    self._html_path_base = os.path.relpath(self._html_dir, self._buildroot)

    # We write the main report body to this file object.
    self._report_file = None

    # We redirect stdout, stderr etc. of tool invocations to these files.
    self._output_files = defaultdict(dict)  # workunit_id -> {path -> fileobj}.
    self._linkify_memo = {}

    # Map from filename to timestamp (ms since the epoch) of when we last overwrote that file.
    # Useful for preventing too-frequent overwrites of, e.g., timing stats,
    # which can noticeably slow down short pants runs with many workunits.
    self._last_overwrite_time = {}
Example #12
0
 def __init__(self, **kwargs):
     super(TemplateData, self).__init__(MustacheRenderer.expand(kwargs))
Example #13
0
 def __init__(self, **kwargs):
   dict.__init__(self, MustacheRenderer.expand(kwargs))
Example #14
0
class HtmlReporter(Reporter):
  """HTML reporting to files.

  The files are intended to be served by the ReportingServer,
  not accessed directly from the filesystem.
  """

  # HTML reporting settings.
  #   html_dir: Where the report files go.
  #   template_dir: Where to find mustache templates.
  Settings = namedtuple('Settings', Reporter.Settings._fields + ('html_dir', 'template_dir'))

  def __init__(self, run_tracker, settings):
    Reporter.__init__(self, run_tracker, settings)
     # The main report, and associated tool outputs, go under this dir.
    self._html_dir = settings.html_dir

    # We render HTML from mustache templates.
    self._renderer = MustacheRenderer(settings.template_dir, __name__)

    # We serve files relative to the build root.
    self._buildroot = get_buildroot()
    self._html_path_base = os.path.relpath(self._html_dir, self._buildroot)

    # We write the main report body to this file object.
    self._report_file = None

    # We redirect stdout, stderr etc. of tool invocations to these files.
    self._output_files = defaultdict(dict)  # workunit_id -> {path -> fileobj}.
    self._linkify_memo = {}

  def report_path(self):
    """The path to the main report file."""
    return os.path.join(self._html_dir, 'build.html')

  def open(self):
    """Implementation of Reporter callback."""
    safe_mkdir(os.path.dirname(self._html_dir))
    self._report_file = open(self.report_path(), 'w')

  def close(self):
    """Implementation of Reporter callback."""
    self._report_file.close()
    # Make sure everything's closed.
    for files in self._output_files.values():
      for f in files.values():
        f.close()

  def start_workunit(self, workunit):
    """Implementation of Reporter callback."""
    # We use these properties of the workunit to decide how to render information about it.
    is_bootstrap = workunit.has_label(WorkUnit.BOOTSTRAP)
    is_tool = workunit.has_label(WorkUnit.TOOL)
    is_multitool = workunit.has_label(WorkUnit.MULTITOOL)
    is_test = workunit.has_label(WorkUnit.TEST)

    # Get useful properties from the workunit.
    workunit_dict = workunit.to_dict()
    if workunit_dict['cmd']:
      workunit_dict['cmd'] = linkify(self._buildroot, workunit_dict['cmd'].replace('$', '\\\\$'),
                                     self._linkify_memo)

    # Create the template arguments.
    args = { 'indent': len(workunit.ancestors()) * 10,
             'html_path_base': self._html_path_base,
             'workunit': workunit_dict,
             'header_text': workunit.name,
             'initially_open': is_test or not (is_bootstrap or is_tool or is_multitool),
             'is_tool': is_tool,
             'is_multitool': is_multitool }
    args.update({ 'collapsible': lambda x: self._renderer.render_callable('collapsible', x, args) })

    # Render the workunit's div.
    s = self._renderer.render_name('workunit_start', args)

    if is_tool:
      # This workunit is a tool invocation, so render the appropriate content.
      # We use the same args, slightly modified.
      del args['initially_open']
      if is_test:
        # Have test framework stdout open by default, but not that of other tools.
        # This is an arbitrary choice, but one that turns out to be useful to users in practice.
        args['stdout_initially_open'] = True
      s += self._renderer.render_name('tool_invocation_start', args)

    # ... and we're done.
    self._emit(s)

  # CSS classes from pants.css that we use to style the header text to reflect the outcome.
  _outcome_css_classes = ['aborted', 'failure', 'warning', 'success', 'unknown']

  def end_workunit(self, workunit):
    """Implementation of Reporter callback."""
    # Create the template arguments.
    duration = workunit.duration()
    timing = '{:.3f}'.format(duration)
    unaccounted_time = None
    # Background work may be idle a lot, no point in reporting that as unaccounted.
    if self.is_under_main_root(workunit):
      unaccounted_time_secs = workunit.unaccounted_time()
      if unaccounted_time_secs >= 1 and unaccounted_time_secs > 0.05 * duration:
        unaccounted_time = '{:.3f}'.format(unaccounted_time_secs)
    args = { 'workunit': workunit.to_dict(),
             'status': HtmlReporter._outcome_css_classes[workunit.outcome()],
             'timing': timing,
             'unaccounted_time': unaccounted_time,
             'aborted': workunit.outcome() == WorkUnit.ABORTED }

    s = ''
    if workunit.has_label(WorkUnit.TOOL):
      s += self._renderer.render_name('tool_invocation_end', args)
    s += self._renderer.render_name('workunit_end', args)
    self._emit(s)

    # Update the timings.
    def render_timings(timings):
      timings_dict = timings.get_all()
      for item in timings_dict:
        item['timing_string'] = '{:.3f}'.format(item['timing'])
      args = {
        'timings': timings_dict
      }
      return self._renderer.render_name('aggregated_timings', args)

    self._overwrite('cumulative_timings', render_timings(self.run_tracker.cumulative_timings))
    self._overwrite('self_timings', render_timings(self.run_tracker.self_timings))

    # Update the artifact cache stats.
    def render_cache_stats(artifact_cache_stats):
      def fix_detail_id(e, _id):
        return e if isinstance(e, string_types) else e + (_id, )

      msg_elements = []
      for cache_name, stat in artifact_cache_stats.stats_per_cache.items():
        msg_elements.extend([
          cache_name + ' artifact cache: ',
          # Explicitly set the detail ids, so their displayed/hidden state survives a refresh.
          fix_detail_id(items_to_report_element(stat.hit_targets, 'hit'), 'cache-hit-details'),
          ', ',
          fix_detail_id(items_to_report_element(stat.miss_targets, 'miss'), 'cache-miss-details'),
          '.'
        ])
      if not msg_elements:
        msg_elements = ['No artifact cache use.']
      return self._render_message(*msg_elements)

    self._overwrite('artifact_cache_stats',
                    render_cache_stats(self.run_tracker.artifact_cache_stats))

    for f in self._output_files[workunit.id].values():
      f.close()

  def handle_output(self, workunit, label, s):
    """Implementation of Reporter callback."""
    if os.path.exists(self._html_dir):  # Make sure we're not immediately after a clean-all.
      path = os.path.join(self._html_dir, '{}.{}'.format(workunit.id, label))
      output_files = self._output_files[workunit.id]
      if path not in output_files:
        f = open(path, 'w')
        output_files[path] = f
      else:
        f = output_files[path]
      f.write(self._htmlify_text(s).encode('utf-8'))
      # We must flush in the same thread as the write.
      f.flush()

  _log_level_css_map = {
    Report.FATAL: 'fatal',
    Report.ERROR: 'error',
    Report.WARN:  'warn',
    Report.INFO:  'info',
    Report.DEBUG: 'debug'
  }
  def do_handle_log(self, workunit, level, *msg_elements):
    """Implementation of Reporter callback."""
    content = '<span class="{}">{}</span>'.format(
              HtmlReporter._log_level_css_map[level], self._render_message(*msg_elements))

    # Generate some javascript that appends the content to the workunit's div.
    args = {
      'content_id': uuid.uuid4(),  # Identifies this content.
      'workunit_id': workunit.id,  # The workunit this reporting content belongs to.
      'content': content,  # The content to append.
      }
    s = self._renderer.render_name('append_to_workunit', args)

    # Emit that javascript to the main report body.
    self._emit(s)

  def _render_message(self, *msg_elements):
    elements = []
    detail_ids = []
    for element in msg_elements:
      # Each element can be a message or a (message, detail) pair, as received by handle_log().
      #
      # However, as an internal implementation detail, we also allow an element to be a tuple
      # (message, detail, detail_initially_visible[, detail_id])
      #
      # - If the detail exists, clicking on the text will toggle display of the detail and close
      #   all other details in this message.
      # - If detail_initially_visible is True, the detail will be displayed by default.
      #
      # Toggling is managed via detail_ids: when clicking on a detail, it closes all details
      # in this message with detail_ids different than that of the one being clicked on.
      # We allow detail_id to be explicitly specified, so that the open/closed state can be
      # preserved through refreshes. For example, when looking at the artifact cache stats,
      # if "hits" are open and "misses" are closed, we want to remember that even after
      # the cache stats are updated and the message re-rendered.
      if isinstance(element, string_types):
        element = [element]
      defaults = ('', None, None, False)
      # Map assumes None for missing values, so this will pick the default for those.
      (text, detail, detail_id, detail_initially_visible) = \
        map(lambda x, y: x or y, element, defaults)
      element_args = {'text': self._htmlify_text(text) }
      if detail is not None:
        detail_id = detail_id or uuid.uuid4()
        detail_ids.append(detail_id)
        element_args.update({
          'detail': self._htmlify_text(detail),
          'detail_initially_visible': detail_initially_visible,
          'detail-id': detail_id
        })
      elements.append(element_args)
    args = { 'elements': elements,
             'all-detail-ids': detail_ids }
    return self._renderer.render_name('message', args)

  def _emit(self, s):
    """Append content to the main report file."""
    if os.path.exists(self._html_dir):  # Make sure we're not immediately after a clean-all.
      self._report_file.write(s)
      self._report_file.flush()  # We must flush in the same thread as the write.

  def _overwrite(self, filename, s):
    """Overwrite a file with the specified contents."""
    if os.path.exists(self._html_dir):  # Make sure we're not immediately after a clean-all.
      with open(os.path.join(self._html_dir, filename), 'w') as f:
        f.write(s)

  def _htmlify_text(self, s):
    """Make text HTML-friendly."""
    colored = self._handle_ansi_color_codes(cgi.escape(s.decode('utf-8')))
    return linkify(self._buildroot, colored, self._linkify_memo).replace('\n', '</br>')

  _ANSI_COLOR_CODE_RE = re.compile(r'\033\[((?:\d|;)*)m')

  def _handle_ansi_color_codes(self, s):
    """Replace ansi escape sequences with spans of appropriately named css classes."""
    parts = HtmlReporter._ANSI_COLOR_CODE_RE.split(s)
    ret = []
    span_depth = 0
    # Note that len(parts) is always odd: text, code, text, code, ..., text.
    for i in range(0, len(parts), 2):
      ret.append(parts[i])
      if i + 1 < len(parts):
        for code in parts[i + 1].split(';'):
          if code == 0:  # Reset.
            while span_depth > 0:
              ret.append('</span>')
              span_depth -= 1
          else:
            ret.append('<span class="ansi-{}">'.format(code))
            span_depth += 1
    while span_depth > 0:
      ret.append('</span>')
      span_depth -= 1

    return ''.join(ret)
Example #15
0
 def __init__(self, template_text, **template_data):
   self._template = MustacheRenderer.parse_template(template_text)
   self.template_data = template_data
Example #16
0
 def __init__(self, **kwargs):
   super().__init__(MustacheRenderer.expand(kwargs))
Example #17
0
class HtmlReporter(Reporter):
    """HTML reporting to files.

  The files are intended to be served by the ReportingServer,
  not accessed directly from the filesystem.
  """

    # HTML reporting settings.
    #   html_dir: Where the report files go.
    #   template_dir: Where to find mustache templates.
    Settings = namedtuple(
        'Settings', Reporter.Settings._fields + ('html_dir', 'template_dir'))

    def __init__(self, run_tracker, settings):
        Reporter.__init__(self, run_tracker, settings)
        # The main report, and associated tool outputs, go under this dir.
        self._html_dir = settings.html_dir

        # We render HTML from mustache templates.
        self._renderer = MustacheRenderer(settings.template_dir, __name__)

        # We serve files relative to the build root.
        self._buildroot = get_buildroot()
        self._html_path_base = os.path.relpath(self._html_dir, self._buildroot)

        # We write the main report body to this file object.
        self._report_file = None

        # We redirect stdout, stderr etc. of tool invocations to these files.
        self._output_files = defaultdict(
            dict)  # workunit_id -> {path -> fileobj}.
        self._linkify_memo = {}

    def report_path(self):
        """The path to the main report file."""
        return os.path.join(self._html_dir, 'build.html')

    def open(self):
        """Implementation of Reporter callback."""
        safe_mkdir(os.path.dirname(self._html_dir))
        self._report_file = open(self.report_path(), 'w')

    def close(self):
        """Implementation of Reporter callback."""
        self._report_file.close()
        # Make sure everything's closed.
        for files in self._output_files.values():
            for f in files.values():
                f.close()

    def start_workunit(self, workunit):
        """Implementation of Reporter callback."""
        # We use these properties of the workunit to decide how to render information about it.
        is_bootstrap = workunit.has_label(WorkUnitLabel.BOOTSTRAP)
        is_tool = workunit.has_label(WorkUnitLabel.TOOL)
        is_multitool = workunit.has_label(WorkUnitLabel.MULTITOOL)
        is_test = workunit.has_label(WorkUnitLabel.TEST)

        # Get useful properties from the workunit.
        workunit_dict = workunit.to_dict()
        if workunit_dict['cmd']:
            workunit_dict['cmd'] = linkify(
                self._buildroot, workunit_dict['cmd'].replace('$', '\\\\$'),
                self._linkify_memo)

        # Create the template arguments.
        args = {
            'indent':
            len(workunit.ancestors()) * 10,
            'html_path_base':
            self._html_path_base,
            'workunit':
            workunit_dict,
            'header_text':
            workunit.name,
            'initially_open':
            is_test or not (is_bootstrap or is_tool or is_multitool),
            'is_tool':
            is_tool,
            'is_multitool':
            is_multitool
        }
        args.update({
            'collapsible':
            lambda x: self._renderer.render_callable('collapsible', x, args)
        })

        # Render the workunit's div.
        s = self._renderer.render_name('workunit_start', args)

        if is_tool:
            # This workunit is a tool invocation, so render the appropriate content.
            # We use the same args, slightly modified.
            del args['initially_open']
            if is_test:
                # Have test framework stdout open by default, but not that of other tools.
                # This is an arbitrary choice, but one that turns out to be useful to users in practice.
                args['stdout_initially_open'] = True
            s += self._renderer.render_name('tool_invocation_start', args)

        # ... and we're done.
        self._emit(s)

    # CSS classes from pants.css that we use to style the header text to reflect the outcome.
    _outcome_css_classes = [
        'aborted', 'failure', 'warning', 'success', 'unknown'
    ]

    def end_workunit(self, workunit):
        """Implementation of Reporter callback."""
        # Create the template arguments.
        duration = workunit.duration()
        timing = '{:.3f}'.format(duration)
        unaccounted_time = None
        # Background work may be idle a lot, no point in reporting that as unaccounted.
        if self.is_under_main_root(workunit):
            unaccounted_time_secs = workunit.unaccounted_time()
            if unaccounted_time_secs >= 1 and unaccounted_time_secs > 0.05 * duration:
                unaccounted_time = '{:.3f}'.format(unaccounted_time_secs)
        args = {
            'workunit': workunit.to_dict(),
            'status': HtmlReporter._outcome_css_classes[workunit.outcome()],
            'timing': timing,
            'unaccounted_time': unaccounted_time,
            'aborted': workunit.outcome() == WorkUnit.ABORTED
        }

        s = ''
        if workunit.has_label(WorkUnitLabel.TOOL):
            s += self._renderer.render_name('tool_invocation_end', args)
        s += self._renderer.render_name('workunit_end', args)
        self._emit(s)

        # Update the timings.
        def render_timings(timings):
            timings_dict = timings.get_all()
            for item in timings_dict:
                item['timing_string'] = '{:.3f}'.format(item['timing'])
            args = {'timings': timings_dict}
            return self._renderer.render_name('aggregated_timings', args)

        self._overwrite('cumulative_timings',
                        render_timings(self.run_tracker.cumulative_timings))
        self._overwrite('self_timings',
                        render_timings(self.run_tracker.self_timings))

        # Update the artifact cache stats.
        def render_cache_stats(artifact_cache_stats):
            def fix_detail_id(e, _id):
                return e if isinstance(e, string_types) else e + (_id, )

            msg_elements = []
            for cache_name, stat in artifact_cache_stats.stats_per_cache.items(
            ):
                msg_elements.extend([
                    cache_name + ' artifact cache: ',
                    # Explicitly set the detail ids, so their displayed/hidden state survives a refresh.
                    fix_detail_id(
                        items_to_report_element(stat.hit_targets, 'hit'),
                        'cache-hit-details'),
                    ', ',
                    fix_detail_id(
                        items_to_report_element(stat.miss_targets, 'miss'),
                        'cache-miss-details'),
                    '.'
                ])
            if not msg_elements:
                msg_elements = ['No artifact cache use.']
            return self._render_message(*msg_elements)

        self._overwrite(
            'artifact_cache_stats',
            render_cache_stats(self.run_tracker.artifact_cache_stats))

        for f in self._output_files[workunit.id].values():
            f.close()

    def handle_output(self, workunit, label, s):
        """Implementation of Reporter callback."""
        if os.path.exists(
                self._html_dir
        ):  # Make sure we're not immediately after a clean-all.
            path = os.path.join(self._html_dir,
                                '{}.{}'.format(workunit.id, label))
            output_files = self._output_files[workunit.id]
            if path not in output_files:
                f = open(path, 'w')
                output_files[path] = f
            else:
                f = output_files[path]
            f.write(self._htmlify_text(s).encode('utf-8'))
            # We must flush in the same thread as the write.
            f.flush()

    _log_level_css_map = {
        Report.FATAL: 'fatal',
        Report.ERROR: 'error',
        Report.WARN: 'warn',
        Report.INFO: 'info',
        Report.DEBUG: 'debug'
    }

    def do_handle_log(self, workunit, level, *msg_elements):
        """Implementation of Reporter callback."""
        content = '<span class="{}">{}</span>'.format(
            HtmlReporter._log_level_css_map[level],
            self._render_message(*msg_elements))

        # Generate some javascript that appends the content to the workunit's div.
        args = {
            'content_id': uuid.uuid4(),  # Identifies this content.
            'workunit_id':
            workunit.id,  # The workunit this reporting content belongs to.
            'content': content,  # The content to append.
        }
        s = self._renderer.render_name('append_to_workunit', args)

        # Emit that javascript to the main report body.
        self._emit(s)

    def _render_message(self, *msg_elements):
        elements = []
        detail_ids = []
        for element in msg_elements:
            # Each element can be a message or a (message, detail) pair, as received by handle_log().
            #
            # However, as an internal implementation detail, we also allow an element to be a tuple
            # (message, detail, detail_initially_visible[, detail_id])
            #
            # - If the detail exists, clicking on the text will toggle display of the detail and close
            #   all other details in this message.
            # - If detail_initially_visible is True, the detail will be displayed by default.
            #
            # Toggling is managed via detail_ids: when clicking on a detail, it closes all details
            # in this message with detail_ids different than that of the one being clicked on.
            # We allow detail_id to be explicitly specified, so that the open/closed state can be
            # preserved through refreshes. For example, when looking at the artifact cache stats,
            # if "hits" are open and "misses" are closed, we want to remember that even after
            # the cache stats are updated and the message re-rendered.
            if isinstance(element, string_types):
                element = [element]
            defaults = ('', None, None, False)
            # Map assumes None for missing values, so this will pick the default for those.
            (text, detail, detail_id, detail_initially_visible) = \
              map(lambda x, y: x or y, element, defaults)
            element_args = {'text': self._htmlify_text(text)}
            if detail is not None:
                detail_id = detail_id or uuid.uuid4()
                detail_ids.append(detail_id)
                element_args.update({
                    'detail': self._htmlify_text(detail),
                    'detail_initially_visible': detail_initially_visible,
                    'detail-id': detail_id
                })
            elements.append(element_args)
        args = {'elements': elements, 'all-detail-ids': detail_ids}
        return self._renderer.render_name('message', args)

    def _emit(self, s):
        """Append content to the main report file."""
        if os.path.exists(
                self._html_dir
        ):  # Make sure we're not immediately after a clean-all.
            self._report_file.write(s)
            self._report_file.flush(
            )  # We must flush in the same thread as the write.

    def _overwrite(self, filename, s):
        """Overwrite a file with the specified contents."""
        if os.path.exists(
                self._html_dir
        ):  # Make sure we're not immediately after a clean-all.
            with open(os.path.join(self._html_dir, filename), 'w') as f:
                f.write(s)

    def _htmlify_text(self, s):
        """Make text HTML-friendly."""
        colored = self._handle_ansi_color_codes(cgi.escape(s.decode('utf-8')))
        return linkify(self._buildroot, colored,
                       self._linkify_memo).replace('\n', '</br>')

    _ANSI_COLOR_CODE_RE = re.compile(r'\033\[((?:\d|;)*)m')

    def _handle_ansi_color_codes(self, s):
        """Replace ansi escape sequences with spans of appropriately named css classes."""
        parts = HtmlReporter._ANSI_COLOR_CODE_RE.split(s)
        ret = []
        span_depth = 0
        # Note that len(parts) is always odd: text, code, text, code, ..., text.
        for i in range(0, len(parts), 2):
            ret.append(parts[i])
            if i + 1 < len(parts):
                for code in parts[i + 1].split(';'):
                    if code == 0:  # Reset.
                        while span_depth > 0:
                            ret.append('</span>')
                            span_depth -= 1
                    else:
                        ret.append('<span class="ansi-{}">'.format(code))
                        span_depth += 1
        while span_depth > 0:
            ret.append('</span>')
            span_depth -= 1

        return ''.join(ret)
Example #18
0
 def __init__(self, **kwargs):
   dict.__init__(self, MustacheRenderer.expand(kwargs))
class EnsimeGen(IdeGen):
    """Create an Ensime project from the given targets."""
    @classmethod
    def register_options(cls, register):
        super(EnsimeGen, cls).register_options(register)
        register(
            '--excluded-deps',
            type=list,
            advanced=True,
            help=
            'Exclude these targets from dependency resolution during workspace generation'
        )
        register(
            '--template-directory',
            type=str,
            advanced=True,
            default=None,
            help=
            'Exclude these targets from dependency resolution during workspace generation'
        )

    def __init__(self, *args, **kwargs):
        super(EnsimeGen, self).__init__(*args, **kwargs)

        self._renderer = MustacheRenderer(
            self.get_options().template_directory, __name__)
        self.project_filename = os.path.join(self.cwd, '.ensime')
        self.ensime_output_dir = os.path.join(self.gen_project_workdir, 'out')

    def resolve_jars(self, targets):
        excluded_targets = set()

        for exclusion in self.get_options().excluded_deps:
            for target in self.context.resolve(exclusion):
                excluded_targets.add(target)

        synthetic_target = self.context.add_new_target(address=Address(
            '', 'exlusions'),
                                                       target_type=JavaLibrary,
                                                       dependencies=list(),
                                                       sources=list(),
                                                       excludes=[])
        filtered_targets = [
            target for target in targets if not target in excluded_targets
        ] + [synthetic_target]

        return super(EnsimeGen, self).resolve_jars(filtered_targets)

    def generate_project(self, project):
        def linked_folder_id(source_set):
            return source_set.source_base.replace(os.path.sep, '.')

        def base_path(source_set):
            return os.path.join(source_set.root_dir, source_set.source_base,
                                source_set.path)

        def create_source_base_template(source_set):
            source_base = base_path(source_set)
            return SourceBase(id=linked_folder_id(source_set),
                              path=source_base)

        source_sets = project.sources[:]
        if project.has_python:
            source_sets.extend(project.py_sources)

        source_bases = frozenset(map(create_source_base_template, source_sets))

        libs = []

        def add_jarlibs(classpath_entries):
            for classpath_entry in classpath_entries:
                libs.append((classpath_entry.jar, classpath_entry.source_jar))

        add_jarlibs(project.internal_jars)
        add_jarlibs(project.external_jars)
        scala_full_version = scala_platform.scala_build_info[
            self.context.options['scala-platform']['version']].full_version
        scala = {
            'language_level': scala_full_version,
            'compiler_classpath': project.scala_compiler_classpath
        }

        outdir = os.path.abspath(self.ensime_output_dir)
        if not os.path.exists(outdir):
            os.makedirs(outdir)

        java_platform = JvmPlatform.global_instance().default_platform
        jdk_home = JvmPlatform.preferred_jvm_distribution([java_platform],
                                                          strict=True).home

        configured_project = {
            'name':
            self.project_name,
            'java_home':
            jdk_home,
            'scala':
            scala,
            'source_bases':
            source_bases,
            'has_tests':
            project.has_tests,
            'internal_jars':
            [cp_entry.jar for cp_entry in project.internal_jars],
            'internal_source_jars': [
                cp_entry.source_jar for cp_entry in project.internal_jars
                if cp_entry.source_jar
            ],
            'external_jars':
            [cp_entry.jar for cp_entry in project.external_jars],
            'external_javadoc_jars': [
                cp_entry.javadoc_jar for cp_entry in project.external_jars
                if cp_entry.javadoc_jar
            ],
            'external_source_jars': [
                cp_entry.source_jar for cp_entry in project.external_jars
                if cp_entry.source_jar
            ],
            'libs':
            libs,
            'outdir':
            os.path.relpath(outdir, get_buildroot()),
            'root_dir':
            get_buildroot(),
            'cache_dir':
            os.path.join(self.cwd, '.ensime_cache')
        }

        with safe_open(self.project_filename, 'w') as output:
            output.write(
                self._renderer.render_name(_TEMPLATE,
                                           {'project': configured_project}))

        print('\nGenerated ensime project at {}{}'.format(
            self.gen_project_workdir, os.sep))
Example #20
0
 def __init__(self, template_text, **template_data):
     self._template = MustacheRenderer.parse_template(template_text)
     self.template_data = template_data
Example #21
0
 def __init__(self, **kwargs):
     super(TemplateData, self).__init__(MustacheRenderer.expand(kwargs))