code: import base64
  from contextlib import contextmanager
  import csv
  import datetime
  from enum import Enum
  import json
  import os
  import os.path as osp
  import pickle
  import sys
  import types
  
  import dateutil.tz
  import joblib
  import numpy as np
  
  from sawyer.garage.misc.autoargs import get_all_parameters
  from sawyer.garage.misc.console import colorize, mkdir_p
  from sawyer.garage.misc.tabulate import tabulate
  from sawyer.garage.misc.tensorboard_output import TensorBoardOutput
  
  _prefixes = []
  _prefix_str = ''
  
  _tabular_prefixes = []
  _tabular_prefix_str = ''
  
  _tabular = []
  
  _text_outputs = []
  _tabular_outputs = []
  
  _text_fds = {}
  _tabular_fds = {}
  _tabular_header_written = set()
  
  _snapshot_dir = None
  _snapshot_mode = 'all'
  _snapshot_gap = 1
  
  _log_tabular_only = False
  _header_printed = False
  
  _tensorboard_step_key = None
  
  _tensorboard = TensorBoardOutput()
  
  
  def _add_output(file_name, arr, fds, mode='a'):
      if file_name not in arr:
          mkdir_p(os.path.dirname(file_name))
          arr.append(file_name)
          fds[file_name] = open(file_name, mode)
  
  
  def _remove_output(file_name, arr, fds):
      if file_name in arr:
          fds[file_name].close()
          del fds[file_name]
          arr.remove(file_name)
  
  
  def push_prefix(prefix):
      _prefixes.append(prefix)
      global _prefix_str
      _prefix_str = ''.join(_prefixes)
  
  
  def add_text_output(file_name):
      _add_output(file_name, _text_outputs, _text_fds, mode='a')
  
  
  def remove_text_output(file_name):
      _remove_output(file_name, _text_outputs, _text_fds)
  
  
  def add_tabular_output(file_name):
      _add_output(file_name, _tabular_outputs, _tabular_fds, mode='w')
  
  
  def remove_tabular_output(file_name):
      if _tabular_fds[file_name] in _tabular_header_written:
          _tabular_header_written.remove(_tabular_fds[file_name])
      _remove_output(file_name, _tabular_outputs, _tabular_fds)
  
  
  def set_tensorboard_dir(dir_name):
      _tensorboard.set_dir(dir_name)
      log("tensorboard data will be logged into:" + dir_name)
  
  
  def set_snapshot_dir(dir_name):
      global _snapshot_dir
      _snapshot_dir = dir_name
  
  
  def get_snapshot_dir():
      return _snapshot_dir
  
  
  def get_snapshot_mode():
      return _snapshot_mode
  
  
  def set_snapshot_mode(mode):
      global _snapshot_mode
      _snapshot_mode = mode
  
  
  def get_snapshot_gap():
      return _snapshot_gap
  
  
  def set_snapshot_gap(gap):
      global _snapshot_gap
      _snapshot_gap = gap
  
  
  def set_log_tabular_only(log_tabular_only):
      global _log_tabular_only
      _log_tabular_only = log_tabular_only
  
  
  def set_tensorboard_step_key(key):
      global _tensorboard_step_key
      _tensorboard_step_key = key
  
  
  def get_log_tabular_only():
      return _log_tabular_only
  
  
  def log(s, with_prefix=True, with_timestamp=True, color=None):
      out = s
      if with_prefix:
          out = _prefix_str + out
      # out_basic holds output with a simpler timestamp for stdout
      out_basic = out
      if with_timestamp:
          now = datetime.datetime.now(dateutil.tz.tzlocal())
          timestamp_basic = now.strftime('%Y-%m-%d %H:%M:%S')
          timestamp = now.strftime('%Y-%m-%d %H:%M:%S.%f %Z')
          out_basic = "%s | %s" % (timestamp_basic, out_basic)
          out = "%s | %s" % (timestamp, out)
      if color is not None:
          out = colorize(out, color)
          out_basic = colorize(out_basic, color)
      if not _log_tabular_only:
          # Also log to stdout
          print(out_basic)
          for fd in list(_text_fds.values()):
              fd.write(out + '\n')
              fd.flush()
          sys.stdout.flush()
  
  
  def record_tabular(key, val):
      _tensorboard.record_scalar(str(key), val)
      _tabular.append((_tabular_prefix_str + str(key), str(val)))
  
  
  def record_tensor(key, val):
      """Record tf.Tensor into tensorboard with Tensor.name and its value."""
      _tensorboard.record_tensor(key, val)
  
  
  def record_histogram(key, val):
      _tensorboard.record_histogram(str(key), val)
  
  
  def record_histogram_by_type(histogram_type, key=None, shape=[1000], **kwargs):
      _tensorboard.record_histogram_by_type(histogram_type, key, shape, **kwargs)
  
  
  def push_tabular_prefix(key):
      _tabular_prefixes.append(key)
      global _tabular_prefix_str
      _tabular_prefix_str = ''.join(_tabular_prefixes)
  
  
  def pop_tabular_prefix():
      del _tabular_prefixes[-1]
      global _tabular_prefix_str
      _tabular_prefix_str = ''.join(_tabular_prefixes)
  
  
  @contextmanager
  def prefix(key):
      push_prefix(key)
      try:
          yield
      finally:
          pop_prefix()
  
  
  @contextmanager
  def tabular_prefix(key):
      push_tabular_prefix(key)
      yield
      pop_tabular_prefix()
  
  
  class TerminalTablePrinter:
      def __init__(self):
          self.headers = None
          self.tabulars = []
  
      def print_tabular(self, new_tabular):
          if self.headers is None:
              self.headers = [x[0] for x in new_tabular]
          else:
              assert len(self.headers) == len(new_tabular)
          self.tabulars.append([x[1] for x in new_tabular])
          self.refresh()
  
      def refresh(self):
          import os
          rows, columns = os.popen('stty size', 'r').read().split()
          tabulars = self.tabulars[-(int(rows) - 3):]
          sys.stdout.write("\x1b[2J\x1b[H")
          sys.stdout.write(tabulate(tabulars, self.headers))
          sys.stdout.write("\n")
  
  
  table_printer = TerminalTablePrinter()
  
  
  def dump_tensorboard(*args, **kwargs):
      if _tabular:
          tabular_dict = dict(_tabular)
      step = None
      if _tensorboard_step_key and _tensorboard_step_key in tabular_dict:
          step = tabular_dict[_tensorboard_step_key]
      _tensorboard.dump_tensorboard(step)
  
  
  def dump_tabular(*args, **kwargs):
      wh = kwargs.pop("write_header", None)
      if _tabular:
          if _log_tabular_only:
              table_printer.print_tabular(_tabular)
          else:
              for line in tabulate(_tabular).split('\n'):
                  log(line, *args, **kwargs)
          tabular_dict = dict(_tabular)
  
          # Also write to the csv files
          # This assumes that the keys in each iteration won't change!
          for tabular_fd in list(_tabular_fds.values()):
              writer = csv.DictWriter(
                  tabular_fd, fieldnames=list(tabular_dict.keys()))
              if wh or (wh is None
                        and tabular_fd not in _tabular_header_written):
                  writer.writeheader()
                  _tabular_header_written.add(tabular_fd)
              writer.writerow(tabular_dict)
              tabular_fd.flush()
          del _tabular[:]
  
      # write to the tensorboard folder
      # This assumes that the keys in each iteration won't change!
      dump_tensorboard(args, kwargs)
  
  
  def pop_prefix():
      del _prefixes[-1]
      global _prefix_str
      _prefix_str = ''.join(_prefixes)
  
  
  def save_itr_params(itr, params):
      if _snapshot_dir:
          if _snapshot_mode == 'all':
              file_name = osp.join(_snapshot_dir, 'itr_%d.pkl' % itr)
              joblib.dump(params, file_name, compress=3)
          elif _snapshot_mode == 'last':
              # override previous params
              file_name = osp.join(_snapshot_dir, 'params.pkl')
              joblib.dump(params, file_name, compress=3)
          elif _snapshot_mode == "gap":
              if itr % _snapshot_gap == 0:
                  file_name = osp.join(_snapshot_dir, 'itr_%d.pkl' % itr)
                  joblib.dump(params, file_name, compress=3)
          elif _snapshot_mode == 'none':
              pass
          else:
              raise NotImplementedError
  
  
  def log_parameters(log_file, args, classes):
      log_params = {}
      for param_name, param_value in args.__dict__.items():
          if any([param_name.startswith(x) for x in list(classes.keys())]):
              continue
          log_params[param_name] = param_value
      for name, cls in classes.items():
          if isinstance(cls, type):
              params = get_all_parameters(cls, args)
              params["_name"] = getattr(args, name)
              log_params[name] = params
          else:
              log_params[name] = getattr(cls, "__kwargs", dict())
              log_params[name][
                  "_name"] = cls.__module__ + "." + cls.__class__.__name__
      mkdir_p(os.path.dirname(log_file))
      with open(log_file, "w") as f:
          json.dump(log_params, f, indent=2, sort_keys=True)
  
  
  def stub_to_json(stub_sth):
      from sawyer.garage.misc import instrument
      if isinstance(stub_sth, instrument.StubObject):
          assert not stub_sth.args
          data = dict()
          for k, v in stub_sth.kwargs.items():
              data[k] = stub_to_json(v)
          data["_name"] = stub_sth.proxy_class.__module__ + \
                          "." + stub_sth.proxy_class.__name__
          return data
      elif isinstance(stub_sth, instrument.StubAttr):
          return dict(
              obj=stub_to_json(stub_sth.obj),
              attr=stub_to_json(stub_sth.attr_name))
      elif isinstance(stub_sth, instrument.StubMethodCall):
          return dict(
              obj=stub_to_json(stub_sth.obj),
              method_name=stub_to_json(stub_sth.method_name),
              args=stub_to_json(stub_sth.args),
              kwargs=stub_to_json(stub_sth.kwargs),
          )
      elif isinstance(stub_sth, instrument.BinaryOp):
          return "binary_op"
      elif isinstance(stub_sth, instrument.StubClass):
          return stub_sth.proxy_class.__module__ + "." + \
                 stub_sth.proxy_class.__name__
      elif isinstance(stub_sth, dict):
          return {stub_to_json(k): stub_to_json(v) for k, v in stub_sth.items()}
      elif isinstance(stub_sth, (list, tuple)):
          return list(map(stub_to_json, stub_sth))
      elif isinstance(stub_sth, types.LambdaType):
          if stub_sth.__module__ is not None:
              return stub_sth.__module__ + "." + stub_sth.__name__
          return stub_sth.__name__
      elif "theano" in str(type(stub_sth)):
          return repr(stub_sth)
      return stub_sth
  
  
  class MyEncoder(json.JSONEncoder):
      def default(self, o):
          if isinstance(o, type):
              return {'$class': o.__module__ + "." + o.__name__}
          elif isinstance(o, Enum):
              return {
                  '$enum':
                  o.__module__ + "." + o.__class__.__name__ + '.' + o.name
              }
          return json.JSONEncoder.default(self, o)
  
  
  def log_parameters_lite(log_file, args):
      log_params = {}
      for param_name, param_value in args.__dict__.items():
          log_params[param_name] = param_value
      if args.args_data is not None:
          stub_method = pickle.loads(base64.b64decode(args.args_data))
          method_args = stub_method.kwargs
          log_params["json_args"] = dict()
          for k, v in list(method_args.items()):
              log_params["json_args"][k] = stub_to_json(v)
          kwargs = stub_method.obj.kwargs
          for k in ["baseline", "env", "policy"]:
              if k in kwargs:
                  log_params["json_args"][k] = stub_to_json(kwargs.pop(k))
          log_params["json_args"]["algo"] = stub_to_json(stub_method.obj)
      mkdir_p(os.path.dirname(log_file))
      with open(log_file, "w") as f:
          json.dump(log_params, f, indent=2, sort_keys=True, cls=MyEncoder)
  
  
  def log_variant(log_file, variant_data):
      mkdir_p(os.path.dirname(log_file))
      if hasattr(variant_data, "dump"):
          variant_data = variant_data.dump()
      variant_json = stub_to_json(variant_data)
      with open(log_file, "w") as f:
          json.dump(variant_json, f, indent=2, sort_keys=True, cls=MyEncoder)
  
  
  def record_tabular_misc_stat(key, values, placement='back'):
      if placement == 'front':
          prefix = ""
          suffix = key
      else:
          prefix = key
          suffix = ""
      if values:
          record_tabular(prefix + "Average" + suffix, np.average(values))
          record_tabular(prefix + "Std" + suffix, np.std(values))
          record_tabular(prefix + "Median" + suffix, np.median(values))
          record_tabular(prefix + "Min" + suffix, np.min(values))
          record_tabular(prefix + "Max" + suffix, np.max(values))
      else:
          record_tabular(prefix + "Average" + suffix, np.nan)
          record_tabular(prefix + "Std" + suffix, np.nan)
          record_tabular(prefix + "Median" + suffix, np.nan)
          record_tabular(prefix + "Min" + suffix, np.nan)
          record_tabular(prefix + "Max" + suffix, np.nan)
  
apis: numpy.median numpy.max numpy.min numpy.std numpy.average