_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'

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