content: from contextlib import closing
  from contextlib import suppress
  from io import StringIO
  from string import Template
  import uuid
  import html
  
  from sklearn import config_context
  
  
  class _VisualBlock:
      """HTML Representation of Estimator
  
      Parameters
      ----------
      kind : {'serial', 'parallel', 'single'}
          kind of HTML block
  
      estimators : list of estimators or `_VisualBlock`s or a single estimator
          If kind != 'single', then `estimators` is a list of
          estimators.
          If kind == 'single', then `estimators` is a single estimator.
  
      names : list of str, default=None
          If kind != 'single', then `names` corresponds to estimators.
          If kind == 'single', then `names` is a single string corresponding to
          the single estimator.
  
      name_details : list of str, str, or None, default=None
          If kind != 'single', then `name_details` corresponds to `names`.
          If kind == 'single', then `name_details` is a single string
          corresponding to the single estimator.
  
      dash_wrapped : bool, default=True
          If true, wrapped HTML element will be wrapped with a dashed border.
          Only active when kind != 'single'.
      """
      def __init__(self, kind, estimators, *, names=None, name_details=None,
                   dash_wrapped=True):
          self.kind = kind
          self.estimators = estimators
          self.dash_wrapped = dash_wrapped
  
          if self.kind in ('parallel', 'serial'):
              if names is None:
                  names = (None, ) * len(estimators)
              if name_details is None:
                  name_details = (None, ) * len(estimators)
  
          self.names = names
          self.name_details = name_details
  
      def _sk_visual_block_(self):
          return self
  
  
  def _write_label_html(out, name, name_details,
                        outer_class="sk-label-container",
                        inner_class="sk-label",
                        checked=False):
      """Write labeled html with or without a dropdown with named details"""
      out.write(f'<div class="{outer_class}">'
                f'<div class="{inner_class} sk-toggleable">')
      name = html.escape(name)
  
      if name_details is not None:
          checked_str = 'checked' if checked else ''
          est_id = uuid.uuid4()
          out.write(f'<input class="sk-toggleable__control sk-hidden--visually" '
                    f'id="{est_id}" type="checkbox" {checked_str}>'
                    f'<label class="sk-toggleable__label" for="{est_id}">'
                    f'{name}</label>'
                    f'<div class="sk-toggleable__content"><pre>{name_details}'
                    f'</pre></div>')
      else:
          out.write(f'<label>{name}</label>')
      out.write('</div></div>')  # outer_class inner_class
  
  
  def _get_visual_block(estimator):
      """Generate information about how to display an estimator.
      """
      with suppress(AttributeError):
          return estimator._sk_visual_block_()
  
      if isinstance(estimator, str):
          return _VisualBlock('single', estimator,
                              names=estimator, name_details=estimator)
      elif estimator is None:
          return _VisualBlock('single', estimator,
                              names='None', name_details='None')
  
      # check if estimator looks like a meta estimator wraps estimators
      if hasattr(estimator, 'get_params'):
          estimators = []
          for key, value in estimator.get_params().items():
              # Only look at the estimators in the first layer
              if '__' not in key and hasattr(value, 'get_params'):
                  estimators.append(value)
          if len(estimators):
              return _VisualBlock('parallel', estimators, names=None)
  
      return _VisualBlock('single', estimator,
                          names=estimator.__class__.__name__,
                          name_details=str(estimator))
  
  
  def _write_estimator_html(out, estimator, estimator_label,
                            estimator_label_details, first_call=False):
      """Write estimator to html in serial, parallel, or by itself (single).
      """
      if first_call:
          est_block = _get_visual_block(estimator)
      else:
          with config_context(print_changed_only=True):
              est_block = _get_visual_block(estimator)
  
      if est_block.kind in ('serial', 'parallel'):
          dashed_wrapped = first_call or est_block.dash_wrapped
          dash_cls = " sk-dashed-wrapped" if dashed_wrapped else ""
          out.write(f'<div class="sk-item{dash_cls}">')
  
          if estimator_label:
              _write_label_html(out, estimator_label, estimator_label_details)
  
          kind = est_block.kind
          out.write(f'<div class="sk-{kind}">')
          est_infos = zip(est_block.estimators, est_block.names,
                          est_block.name_details)
  
          for est, name, name_details in est_infos:
              if kind == 'serial':
                  _write_estimator_html(out, est, name, name_details)
              else:  # parallel
                  out.write('<div class="sk-parallel-item">')
                  # wrap element in a serial visualblock
                  serial_block = _VisualBlock('serial', [est],
                                              dash_wrapped=False)
                  _write_estimator_html(out, serial_block, name, name_details)
                  out.write('</div>')  # sk-parallel-item
  
          out.write('</div></div>')
      elif est_block.kind == 'single':
          _write_label_html(out, est_block.names, est_block.name_details,
                            outer_class="sk-item", inner_class="sk-estimator",
                            checked=first_call)
  
  
  _STYLE = """
  #$id {
    color: black;
    background-color: white;
  }
  #$id pre{
    padding: 0;
  }
  #$id div.sk-toggleable {
    background-color: white;
  }
  #$id label.sk-toggleable__label {
    cursor: pointer;
    display: block;
    width: 100%;
    margin-bottom: 0;
    padding: 0.2em 0.3em;
    box-sizing: border-box;
    text-align: center;
  }
  #$id div.sk-toggleable__content {
    max-height: 0;
    max-width: 0;
    overflow: hidden;
    text-align: left;
    background-color: #f0f8ff;
  }
  #$id div.sk-toggleable__content pre {
    margin: 0.2em;
    color: black;
    border-radius: 0.25em;
    background-color: #f0f8ff;
  }
  #$id input.sk-toggleable__control:checked~div.sk-toggleable__content {
    max-height: 200px;
    max-width: 100%;
    overflow: auto;
  }
  #$id div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {
    background-color: #d4ebff;
  }
  #$id div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {
    background-color: #d4ebff;
  }
  #$id input.sk-hidden--visually {
    border: 0;
    clip: rect(1px 1px 1px 1px);
    clip: rect(1px, 1px, 1px, 1px);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;
  }
  #$id div.sk-estimator {
    font-family: monospace;
    background-color: #f0f8ff;
    margin: 0.25em 0.25em;
    border: 1px dotted black;
    border-radius: 0.25em;
    box-sizing: border-box;
  }
  #$id div.sk-estimator:hover {
    background-color: #d4ebff;
  }
  #$id div.sk-parallel-item::after {
    content: "";
    width: 100%;
    border-bottom: 1px solid gray;
    flex-grow: 1;
  }
  #$id div.sk-label:hover label.sk-toggleable__label {
    background-color: #d4ebff;
  }
  #$id div.sk-serial::before {
    content: "";
    position: absolute;
    border-left: 1px solid gray;
    box-sizing: border-box;
    top: 2em;
    bottom: 0;
    left: 50%;
  }
  #$id div.sk-serial {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: white;
  }
  #$id div.sk-item {
    z-index: 1;
  }
  #$id div.sk-parallel {
    display: flex;
    align-items: stretch;
    justify-content: center;
    background-color: white;
  }
  #$id div.sk-parallel-item {
    display: flex;
    flex-direction: column;
    position: relative;
    background-color: white;
  }
  #$id div.sk-parallel-item:first-child::after {
    align-self: flex-end;
    width: 50%;
  }
  #$id div.sk-parallel-item:last-child::after {
    align-self: flex-start;
    width: 50%;
  }
  #$id div.sk-parallel-item:only-child::after {
    width: 0;
  }
  #$id div.sk-dashed-wrapped {
    border: 1px dashed gray;
    margin: 0.2em;
    box-sizing: border-box;
    padding-bottom: 0.1em;
    background-color: white;
    position: relative;
  }
  #$id div.sk-label label {
    font-family: monospace;
    font-weight: bold;
    background-color: white;
    display: inline-block;
    line-height: 1.2em;
  }
  #$id div.sk-label-container {
    position: relative;
    z-index: 2;
    text-align: center;
  }
  #$id div.sk-container {
    display: inline-block;
    position: relative;
  }
  """.replace('  ', '').replace('\n', '')  # noqa
  
  
  def estimator_html_repr(estimator):
      """Build a HTML representation of an estimator.
  
      Read more in the :ref:`User Guide <visualizing_composite_estimators>`.
  
      Parameters
      ----------
      estimator : estimator object
          The estimator to visualize.
  
      Returns
      -------
      html: str
          HTML representation of estimator.
      """
      with closing(StringIO()) as out:
          container_id = "sk-" + str(uuid.uuid4())
          style_template = Template(_STYLE)
          style_with_id = style_template.substitute(id=container_id)
          out.write(f'<style>{style_with_id}</style>'
                    f'<div id="{container_id}" class"sk-top-container">'
                    '<div class="sk-container">')
          _write_estimator_html(out, estimator, estimator.__class__.__name__,
                                str(estimator), first_call=True)
          out.write('</div></div>')
  
          html_output = out.getvalue()
          return html_output
  