[Types-sig] Some more type annotations stuff

Tim Hochberg tim.hochberg@ieee.org
Tue, 20 Mar 2001 10:59:05 -0700


This is a multi-part message in MIME format.

------=_NextPart_000_03F1_01C0B12C.C811EFF0
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit


Sorry I've been missing from the discussion. The harddrive on my laptop is
doing a slow motion meltdown, so I'm distracted.

Attached is my latest try at putting together check methods for immuttable
sequences and mappings. I also include preliminary support for anonymous
typechecking (I_getitem, I_setitem, etc). For mutable sequences and mappings
things get much murkier. I took a stab at checking for __setitem__ (see
I_setitem), but as you can see if you look at the code, the method is not
entirely satisfactory.

It sounds like Paul is making good progress on the typechecking code, so
things are looking up.

-tim

------=_NextPart_000_03F1_01C0B12C.C811EFF0
Content-Type: text/plain;
	name="typecheckstuff.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="typecheckstuff.py"

# March 14: Immutable Sequences
# March 15: Fixed(?) iterable protocol to forbid mappings without iter =
protocol
# March 16: Immutable mappings, fixes several bugs. testing.
           =20

import types, operator, typecheck, copy
   =20

class HorribleTypeError(Exception):
    pass

def _getitem(object):
    """Checks if an object supports __getitem__.

    Assumes that item state is not changed by getitem.
    """
    try:
        object[0]
        return 1
    except (IndexError, KeyError):
        return 1
    except Exception, err:
        import traceback
        traceback.print_exc()
        return 0
I_getitem =3D typecheck.Interface("I_getitem", "Any object that supports =
getitem", _getitem)

def _len(object):
    """Checks if an object supports __len__"""
    try:
        len(object)
        return 1
    except:
        return 0   =20
I_len =3D typecheck.Interface("I_len", "Any object that supports len", =
_len)

def _setitem(object):
    """Checks if an objects supports __setitem

    This is potentially dangersous. We make an effort to avoid
    modifying object, but it could happen.
    """
    try:
        object[0] =3D object[0]
        return 1
    except IndexError:
        # This is a sequence-like object
        try:
            # This should fail with an IndexError if it's setable
            object[0] =3D None
        except IndexError:
            return 1
        except:
            return 0
    except KeyError:
        # This is a mapping-like object
        # The only way I can think of to more-or-less nondestructively =
test requires delitem
        # Try and hope for the best.
        try:
            object[0] =3D None
            del object[0]
            return 1
        except:
            raise HorribleTypeError("Nondestructive setitem test failed =
object may be corrupted!")
    except:
        return 0
I_setitem =3D typecheck.Interface("I_setitem", "Any object that supports =
setitem", _setitem)



def _iterable(object):
    """An iterable implements __getitem__ or the iteration protocol =
(TBD)
    """
    # if implements_iter_protocol(object): return 1
    #
    # Exclude mappings that don't implement iteration
    if hasattr(object, "keys"): return 0
    # Anything else should be a sequence and thus iteratable.
    if hasattr(object, "__getitem__"): return 1
    # This isn't really right, what we want to do is to check whether
    # object[0] will work. However using the try: object[0] approach
    # man not work correctly. If the object uses the
    # current iteration protocol calling object[0] may
    # advance the iteration.=20
    if not type(object) =3D=3D types.InstanceType and \
       operator.isSequenceType(object): return 1
    return 0
IIterable =3D typecheck.Interface("IIterable", _iterable.__doc__, =
_iterable)


def _basicSequence(object):
    """A basic sequence implements __getitem__ and __len__ and does not =
implement keys.

    This last condition is necessary to distinguish a sequence from a =
mapping.

    This is sufficient to build a tuple, but doesn't necessarily
    support concatenation, repitition or slicing.
    """
    # Even though I refused to use the basic getitem check for the
    # iterable protocol, it's probably OK here. Objects with a length
    # attribute should not be changed by accessing an item.
    return _len(object) and _getitem(object) and not hasattr(object, =
"keys")

IBasicSequence =3D typecheck.Interface("IBasicSequence", =
_basicSequence.__doc__, _basicSequence)

def _sequence(object):
    """A full sequence implements the same methods as a tuple.

    A sequence can always be constructed from a BasicSequence,
    see SequenceWrapper below.

    """
    if not _basicSequence(object):
        return 0
    try:
        # Check for repitition and concatenation.
        # I'm hoping this is less expensive for long sequences.
        0*object + 0*object
        object[:0]
    except:
        return 0
    return 1
ISequence =3D  typecheck.Interface("ISequence", _sequence.__doc__, =
_sequence)


def _basicMapping(object):
    """A basic mapping supports getitem and keys"""
    return _getitem(object) and hasattr(object, "keys")
IBasicMapping =3D typecheck.Interface("IBasicMapping", =
_basicMapping.__doc__, _basicMapping)

def _mapping(object):
    """A mapping supports getitem, keys, len, copy, has_key, items, =
values and get .

    These are all the items a dictionary would support if it were =
immutable.

    """
    return (_basicMapping(object) and
            _len(object) and
            hasattr(object, "copy") and
            hasattr(object, "has_key") and
            hasattr(object, "items") and
            hasattr(object, "values") and
            hasattr(object, "get"))
IMapping =3D typecheck.Interface("IMapping", _mapping.__doc__, _mapping)

# It's kind of obnoxious having both BasicSequence and SequenceBase. One =
or both should
# probably be renamed.
class SequenceBase:
    """Class to simplify derivation of objects that satisfy the full =
sequence protocol.

    This class provides the same functionality as a tuple.

    At a minimum, __getitem__ and __len__ must be overridden. =
Concatenation and repition
    return tuples by default. If other behaviour is desired, override =
__add__ and __mul__
    as well.

    Subclasses may also want to override __getslice__ for efficiency.
   =20
    """
    # The following list is based on section 2.1.5 in the library =
reference
    # x in s: Should be take care of by the sequence prototcol(?)
    # x not in s: Ditto
    # s + t:
    def __add__(self, other):
        return tuple(self) + tuple(other)
    # s * n, n * s:
    def __mul__(self, n):
        return tuple(self)*n
    __rmul__ =3D __mul__
    # s[i]
    def __getitem__(self, i):
        raise NotImplementedError
    # s[i:j]
    def __getslice__(self, start, stop):
        l =3D []
        for i in range(start, stop):
            l.append(self[i])
        return tuple(l)
    # len(s)
    def __len__(self):
        raise NotImplementedError
    # min(s)
    # max(s) These two work automagically for any iterable.

class SequenceWrapper(SequenceBase):
    def __init__(self, wrapped):
        self.wrapped =3D wrapped
    def __getitem__(self, i):
        return self.wrapped[i]
    def __len__(self):
        return len(self.wrapped)

class MappingBase:
    """Class to simplify the derivation of classes that satisfy the =
Mapping protocol.

    This class supplies all the methods of a standard disctionary that
    do not require mutation.

    The user must overided __getitem__ and keys. Other methods may be
    overridden for efficiency.
   =20
    """
    def __getitem__(self, key):
        raise NotImplementedError
    def keys(self):
        raise NotImplementedError
    def __len__(self):
        return len(self.keys())
    def copy(self):
        pass
    def has_key(self,key):
        try:
            self[key]
            return 1
        except KeyError:
            return 0
    def items(self):
        return [(key, self[key]) for key in self.keys()]
    def values(self):
        return [self[key] for key in self.keys()]
    def get(self, key, default=3DNone):
        if self.has_key(key):
            return self[key]
        else:
            return default

class MappingWrapper(MappingBase):
    def __init__(self, wrapped):
        self.wrapped =3D wrapped
    def __getitem__(self, i):
        return self.wrapped[i]
    def keys(self):
        return self.wrapped.keys()



# Very basic testing.

class _aniterable:
    def __getitem__(self, i):
        if 0 <=3D i < 5:
            return i
        raise IndexError
anInterable =3D _aniterable()

class _aBasicSequence(_aniterable):
    def __len__(self):
        return 5
aBasicSequence =3D _aBasicSequence()
aSequence =3D SequenceWrapper(aBasicSequence)

class _aBasicMapping(_aniterable):
    def keys(self):
        return range(5)
aBasicMapping =3D _aBasicMapping()
aMapping =3D MappingWrapper(aBasicMapping)

for obj, isIter, isBasic, isSeq, hasGI, hasLen, hasSI, isBMap, isMap in =
[
                                     ((),1, 1, 1, 1, 1, 0, 0, 0),
                                     ([],1, 1, 1, 1, 1, 1, 0, 0),
                                     (anInterable, 1, 0, 0, 1, 0, 0, 0, =
0),
                                     (aBasicSequence, 1, 1, 0, 1, 1, 0, =
0, 0),
                                     (aSequence, 1, 1, 1, 1, 1, 0, 0, =
0),
                                     (aBasicMapping, 0, 0, 0, 1, 0, 0, =
1, 0),
                                     (aMapping, 0, 0, 0, 1, 1, 0, 1, 1), =
=20
                                     ({}, 0, 0, 0, 1, 1, 1, 1, 1)]:
    orig =3D copy.deepcopy(obj)
    assert IIterable.__check__(obj) =3D=3D isIter, "%s failed (expected =
%s" % (obj,isIter)
    assert IBasicSequence.__check__(obj) =3D=3D isBasic, "%s failed =
(expected %s)" % (obj,isBasic)
    assert ISequence.__check__(obj) =3D=3D isSeq, "%s failed" % (obj,)
    assert I_getitem.__check__(obj) =3D=3D hasGI, "%s failed" % (obj,)=20
    assert I_len.__check__(obj) =3D=3D hasLen, "%s failed (expected %s)" =
% (obj,hasLen)=20
    assert I_setitem.__check__(obj) =3D=3D hasSI, "%s failed" % (obj,)
    assert IBasicMapping.__check__(obj) =3D=3D isBMap, "%s failed =
(expected %s)" % (obj,isBMap)   =20
    assert IMapping.__check__(obj) =3D=3D isMap, "%s failed" % (obj,)    =

    if _iterable(obj):
        for a, b in zip(obj, orig):
            assert a =3D=3D b, "%s changed (%s !=3D %s)" % (obj, a, b)
    elif type(obj) =3D=3D type({}):
        assert obj =3D=3D orig, "%s !=3D %s" % (obj, orig)
    print obj, "(", isIter, isBasic, isSeq, hasGI, ") passed"






------=_NextPart_000_03F1_01C0B12C.C811EFF0--