Source

mementos / test / test.py

Full commit
from mementos import *
import sys, pytest

def with_metaclass(meta, base=object):
    """Create a base class with a metaclass."""
    return meta("NewBase", (base,), {})

def test_one():    
    class Thing(with_metaclass(MementoMetaclass, object)):
                
        def __init__(self, name):
            self.name = name
        

    t1 = Thing("one")
    t2 = Thing("one")
    assert t1 is t2
    
    o1 = Thing("lovely")
    o2 = Thing(name="lovely")
    assert o1 is not o2   # because the call signature is different
    
    class OtherThing(with_metaclass(MementoMetaclass, object)):
        
        def __init__(self, name):
            self.name = name
            self.color = None
            self.weight = None
            
        def set(self, color=None, weight=None):
            self.color = color or self.color
            self.weight = weight or self.weight
            return self
    
    ot1 = OtherThing("one").set(color='blue')
    ot2 = OtherThing("one").set(weight='light')
    assert ot1 is ot2
    assert ot1.color == ot2.color == 'blue'
    assert ot1.weight == ot2.weight == 'light'
    
def test_inline_with_metaclass():
    
    # Make sure you can do the metaclass specification directly.
    
    class Thing23(MementoMetaclass("NewBase", (object,), {})):
                
        def __init__(self, name):
            self.name = name
        

    t1 = Thing23("one")
    t2 = Thing23("one")
    assert t1 is t2
    
    o1 = Thing23("lovely")
    o2 = Thing23(name="lovely")
    assert o1 is not o2   # because the call signature is different
    
def test_id_metaclass():
    
    # Test the metaclass that uses the id of the first arg
    # but really as much a test of memento_factory, since IdMementoMetaclass not
    # part of base module
       
    IdMementoMetaclass = memento_factory("IdMementoMetaclass",
                                         lambda cls, args, kwargs: (cls, id(args[0])) )

    class IdTrack(with_metaclass(IdMementoMetaclass, object)):
        def __init__(self, name, *args):
            self.name = name
            self.args = args
            
    id1 = IdTrack("joe")
    id2 = IdTrack("joe")
    assert id1 is id2
    
    id3 = IdTrack("joe", 1, 4)
    assert id3 is id2
    
    id4 = IdTrack("Joe", 1, 4)
    id5 = IdTrack("Joe")
    assert id4 is id5
    assert id4 is not id3
    assert id5 is not id3
    
def test_id_metaclass_primitive():
    
    # Test the metaclass that uses the id of the first arg
    # but really as much a test of memento_factory, since IdMementoMetaclass not
    # part of base module
       
    IdMementoPrimitive = memento_factory("IdMementoMetaclass",
                                         lambda cls, args, kwargs: id(args[0]) )

    class IdTrackPrim(with_metaclass(IdMementoPrimitive, object)):
        def __init__(self, name, *args):
            self.name = name
            self.args = args
            
    id1 = IdTrackPrim("joe")
    id2 = IdTrackPrim("joe")
    assert id1 is id2
    
    id3 = IdTrackPrim("joe", 1, 4)
    assert id3 is id2
    
    id4 = IdTrackPrim("Joe", 1, 4)
    id5 = IdTrackPrim("Joe")
    assert id4 is id5
    assert id4 is not id3
    assert id5 is not id3
    
def test_metaclass_factory():
    
    mymeta = memento_factory("mymeta", lambda cls, args, kwargs: (cls, kwargs['b']))
    
    class BTrack(with_metaclass(mymeta, object)):
        def __init__(self, name, **kwargs):
            assert 'b' in kwargs
            self.name = name
            self.b = kwargs['b']
            
    b1 = BTrack('andy', b=1)
    b2 = BTrack('dave', b=1)
    assert b1 is b2
    assert b1.name == 'andy'  # because b1 got there first, defined object such that b=1
    
    b3 = BTrack('andy', b=2)
    assert b3 is not b1
    assert b3 is not b2
    assert b3.name == 'andy'
    
@pytest.mark.skipif("sys.version_info < (2,7)")
def test_perfect_signatures():
    # use inspect.getcallargs to make signatures that dont vary
    
    from inspect import getcallargs
    import hashlib
    import six
    
    def call_fingerprint(cls, args, kwargs):
        """
        Given a complex __init__ call with varied positional, keyward, variable, and
        variable keyword arguments, canonicalize the argument values and return a
        suitable hash key.
        """
        callitems = list(getcallargs(cls.__init__, None, *args, **kwargs).items())
        callitems.sort()
        h = hashlib.md5()
        h.update(repr(callitems).encode('utf-8'))
        return h.hexdigest()
        
    Perfect = memento_factory("Perfect", call_fingerprint)
    
    class PerfectCall(with_metaclass(Perfect, object)):
        def __init__(self, name, a=1, b=2, c=3, *args, **kwargs):
            self.vector = (name, a, b, c)
    
    p1 = PerfectCall('amy')
    p2 = PerfectCall('amy', b=2)
    p3 = PerfectCall('amy', c=3)
    p4 = PerfectCall('amy', c=3, a=1)
    p5 = PerfectCall(c=3, a=1, b=2, name='amy')
    assert p1 is p2
    assert p2 is p3
    assert p3 is p4
    assert p4 is p5
    
    p6 = PerfectCall('amy', b=33)
    assert p1 is not p6
    
    p7 = PerfectCall(**{'name': 'amy', 'b': 33})
    assert p6 is p7