docs: def _annotate_imm(cls):
      '''
      _annotate_imm(cls) crawls the class members and adds the _pimms_immutable_data_ attribute
      (a dict) with the following items to the class's attributes:
        * 'params'
        * 'values'
        * 'checks'
      '''
      dat = {}
      cls._pimms_immutable_data_ = dat
      # First, crawl this class's static members to see what has been added...
      mems = inspect.getmembers(cls)
      immmems = [m for m in mems
                 if isinstance(m[1], types.FunctionType)
                 if hasattr(m[1], '_pimms_immutable_data_')]
      dat['params'] = {}
      dat['values'] = {}
      dat['checks'] = {}
      dat['consts'] = {}
      for (fname,f) in immmems:
          if 'is_param' in f._pimms_immutable_data_:
              dat['params'][f.__name__] = (
                  None if 'default_value' not in dat else (dat['default_value'],),
                  f, [], [], [])
          elif 'is_value' in f._pimms_immutable_data_:
              inputs = f._pimms_immutable_data_['inputs']
              dat['values'][f.__name__] = (inputs, f, [])
              if len(inputs) == 0:
                  dat['consts'][f.__name__] = ([], [])
          elif 'is_check' in f._pimms_immutable_data_:
              dat['checks'][f.__name__] = (f._pimms_immutable_data_['inputs'], f)
      # That grabs all the relevant immutable data; we now have two tasks:
      # (1) merge with parent immutable classes to generate final value/param/check lists
      # --- to do this, we start by getting the class hierarchy
      mro = [c for c in inspect.getmro(cls) if c is not cls and is_imm_type(c)]
      # --- now walk through, merging each
      for parent in mro: _imm_merge_class(cls, parent)
      # (2) resolve dependencies
      _imm_resolve_deps(cls)
      return cls