text: import json
  import logging
  import math
  import numbers
  import os
  import platform
  import resource
  import sys
  from collections import MutableMapping
  from contextlib import contextmanager
  from os.path import join
  
  from IPython.core.display import display, HTML
  from pyhocon import ConfigFactory
  from pyhocon import ConfigMissingException
  from pyhocon import ConfigTree
  from pyhocon import HOCONConverter
  
  from gtd.utils import NestedDict, Config
  
  
  def in_ipython():
      try:
          __IPYTHON__
          return True
      except NameError:
          return False
  
  
  def print_with_fonts(tokens, sizes, colors, background=None):
  
      def style(text, size=12, color='black'):
          return u'<span style="font-size: {}px; color: {};">{}</span>'.format(size, color, text)
  
      styled = [style(token, size, color) for token, size, color in zip(tokens, sizes, colors)]
      text = u' '.join(styled)
  
      if background:
          text = u'<span style="background-color: {};">{}</span>'.format(background, text)
  
      display(HTML(text))
  
  
  def gb_used():
      used = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
      if platform.system() != 'Darwin':
          # on Linux, used is in terms of kilobytes
          power = 2
      else:
          # on Mac, used is in terms of bytes
          power = 3
      return float(used) / math.pow(1024, power)
  
  
  class Metadata(MutableMapping):
      """A wrapper around ConfigTree.
  
      Supports a name_scope contextmanager.
      """
      def __init__(self, config_tree=None):
          if config_tree is None:
              config_tree = ConfigTree()
  
          self._config_tree = config_tree
          self._namestack = []
  
      @contextmanager
      def name_scope(self, name):
          self._namestack.append(name)
          yield
          self._namestack.pop()
  
      @contextmanager
      def name_scope_path(self, path):
          """Specify a path (list[str]) rather than a single name (str).
          
          Args:
              path (list[str])
          """
          self._namestack.extend(path)
          yield
          del self._namestack[-len(path):]
  
      def _full_key(self, key):
          return '.'.join(self._namestack + [key])
  
      def __getitem__(self, key):
          try:
              val = self._config_tree.get(self._full_key(key))
          except ConfigMissingException:
              raise KeyError(key)
  
          if isinstance(val, ConfigTree):
              return Metadata(val)
          return val
  
      def __setitem__(self, key, value):
          """Put a value (key is a dot-separated name)."""
          self._config_tree.put(self._full_key(key), value)
  
      def __delitem__(self, key):
          raise NotImplementedError()
  
      def __iter__(self):
          return iter(self._config_tree)
  
      def __len__(self):
          return len(self._config_tree)
  
      def __repr__(self):
          return self.to_str()
  
      def to_str(self, fmt='hocon'):
          return HOCONConverter.convert(self._config_tree, fmt)
  
      def to_file(self, path, fmt='hocon'):
          with open(path, 'w') as f:
              f.write(self.to_str(fmt))
  
      @classmethod
      def from_file(cls, path, fmt='hocon'):
          if fmt == 'hocon':
              config_tree = ConfigFactory.parse_file(path)
          elif fmt == 'json':
              with open(path, 'r') as f:
                  d = json.load(f)
              config_tree = ConfigFactory.from_dict(d)
          else:
              raise ValueError('Invalid format: {}'.format(fmt))
  
          return cls(config_tree)
  
  
  class SyncedMetadata(Metadata):
      """A Metadata object which writes to file after every change."""
      def __init__(self, path, fmt='hocon'):
          if os.path.exists(path):
              m = Metadata.from_file(path, fmt)
          else:
              m = Metadata()
  
          super(SyncedMetadata, self).__init__(m._config_tree)
          self._path = path
          self._fmt = fmt
  
      def __setitem__(self, key, value):
          super(SyncedMetadata, self).__setitem__(key, value)
          self.to_file(self._path, fmt=self._fmt)
  
  
  def print_list(l):
      for item in l:
          print item
  
  
  def print_no_newline(s):
      sys.stdout.write(s)
      sys.stdout.flush()
  
  
  def set_log_level(level):
      """Set the log-level of the root logger of the logging module.
  
      Args:
          level: can be an integer such as 30 (logging.WARN), or a string such as 'WARN'
      """
      if isinstance(level, str):
          level = logging._levelNames[level]
  
      logger = logging.getLogger()  # gets root logger
      logger.setLevel(level)
  
  
  def jupyter_no_margins():
      """Cause Jupyter notebook to take up 100% of window width."""
      display(HTML("<style>.container { width:100% !important; }</style>"))
  
  
  class TraceSession(object):
      def __init__(self, tracer):
          self.tracer = tracer
          self._values = {}
  
      @property
      def values(self):
          return self._values
  
      def save(self, save_path):
          with open(save_path, 'w') as f:
              json.dump(self.values, f, indent=4, sort_keys=True)
  
      def __enter__(self):
          if self.tracer._current_session:
              raise RuntimeError('Already in the middle of a TraceSession')
  
          # register as the current session
          self.tracer._current_session = self
          return self
  
      def __exit__(self, exc_type, exc_val, exc_tb):
          # un-register
          self.tracer._current_session = None
  
  
  class Tracer(object):
      """Log values computed during program execution.
      
      Values are logged to the currently active TraceSession object.
      """
      def __init__(self):
          self._current_session = None
  
      def session(self):
          return TraceSession(self)
  
      def log(self, logging_callback):
          """If we are in a TraceSession, execute the logging_callback.
          
          The logging_callback should take a `values` dict as its only argument, and modify `values` in some way.
          
          Args:
              logging_callback (Callable[dict]): a function which takes a `values` dict as its only argument.
          """
          if self._current_session is None:
              return
          logging_callback(self._current_session.values)
  
      def log_put(self, name, value):
          """Log a value.
          
          Args:
              name (str): name of the variable
              value (object)
          """
          def callback(values):
              if name in values:
                  raise RuntimeError('{} already logged'.format(name))
              values[name] = value
  
          return self.log(callback)
  
      def log_append(self, name, value):
          """Append a value.
  
          Args:
              name (str): name of the variable
              value (object): value to append
          """
          def callback(values):
              if name not in values:
                  values[name] = []
              values[name].append(value)
  
          return self.log(callback)
  
  
  def indent(s, spaces=4):
      whitespace = u' ' * spaces
      return u'\n'.join(whitespace + line for line in s.split(u'\n'))
  