text: # This file is part of the pyMOR project (https://www.pymor.org).
  # Copyright 2013-2021 pyMOR developers and contributors. All rights reserved.
  # License: BSD 2-Clause License (https://opensource.org/licenses/BSD-2-Clause)
  
  
  import inspect
  import textwrap
  
  import numpy as np
  
  from pymor.core.defaults import defaults
  
  
  def register_format_handler(cls, handler):
      _format_handlers[cls] = handler
  
  
  def _format_generic(obj, max_width, verbosity, override={}):
      init_sig = inspect.signature(obj.__init__)
      keys, vals = [], []
      for arg, description in init_sig.parameters.items():
          if verbosity < 2 and description.default == description.empty:
              key = ''
          else:
              key = f'{arg}='
          if arg == 'self':
              continue
          if arg in override:
              val = override[arg]
              if val is None:
                  continue
              vals.append(override[arg])
          else:
              val = getattr(obj, arg, '??')
              try:
                  if verbosity < 3 and val == description.default:
                      continue
              except (ValueError, TypeError):  # comparison of numpy arrays, NGSolve FESpaces
                  pass
              vals.append(_recurse(val, max_width - len(key) - 4, verbosity))
          keys.append(key)
  
      if verbosity > 0 and (sum(len(k) + len(v) + 2 for k, v in zip(keys, vals)) + len(type(obj).__name__) > max_width
                            or any('\n' in v for v in vals)):
          args = [f'    {k}{indent_value(v, len(k) + 4)}' for k, v in zip(keys, vals)]
          args = ",\n".join(args)
          return f'''{type(obj).__name__}(
  {args})'''
      else:
          args = [f'{k}{v}' for k, v in zip(keys, vals)]
          return f'{type(obj).__name__}({", ".join(args)})'
  
  
  def _format_list_tuple(val, max_width, verbosity):
      brackets = '()' if type(val) is tuple else '[]'
      reprs = [repr(v) for v in val]
      if verbosity > 0 and (any('\n' in r for r in reprs) or sum(len(r) + 2 for r in reprs) + 2 > max_width):
          reprs = ',\n '.join(indent_value(r, 1) for r in reprs)
          return brackets[0] + reprs + brackets[1]
      else:
          return brackets[0] + ', '.join(reprs) + brackets[1]
  
  
  def _format_dict(val, max_width, verbosity):
      if not val:
          return '{}'
      keys, vals = zip(*val.items())
      reprs = [repr(v) for v in vals]
      if verbosity > 0 and (any('\n' in r for r in reprs)
                            or sum(len(k) + len(r) + 4 for k, r in zip(keys, reprs)) + 2 > max_width):
          reprs = ',\n '.join(indent_value(f'{k}: {r}', 1) for k, r in zip(keys, reprs))
          return '{' + reprs + '}'
      else:
          return '{' + ', '.join(f'{k}: {r}' for k, r in zip(keys, reprs)) + '}'
  
  
  def _format_array(val, max_width, verbosity):
      r = repr(val)
      if len(r) <= max_width * 3 and '\n' in r:
          r_one_line = ' '.join(rr.strip() for rr in r.split('\n'))
          if len(r_one_line) <= max_width:
              return r_one_line
          else:
              return r
      else:
          return r
  
  
  _format_handlers = {}
  register_format_handler(list, _format_list_tuple)
  register_format_handler(tuple, _format_list_tuple)
  register_format_handler(dict, _format_dict)
  register_format_handler(np.ndarray, _format_array)
  
  
  def _recurse(obj, max_width, verbosity):
      if hasattr(obj, '_format_repr'):
          return obj._format_repr(max_width, verbosity)
  
      handler = None
      for cls in type(obj).__mro__:
          try:
              handler = _format_handlers[cls]
          except KeyError:
              pass
  
      if handler:
          return handler(obj, max_width, verbosity)
      else:
          return repr(obj)
  
  
  @defaults('max_width', 'verbosity')
  def format_repr(obj, max_width=120, verbosity=1):
      return _recurse(obj, max_width, verbosity)
  
  
  def indent_value(val, indent):
      if '\n' in val:
          return textwrap.indent(val, ' ' * indent)[indent:]
      else:
          return val
  