def test_add_class_attrs_does_not_create_duplicate_types(manager): with collector() as classes: class DynamicThing(Entity): id = Uuid() manager.save_collected_classes(classes) del DynamicThing manager.reload_types() DynamicThing = manager.type_registry.get_class_by_id( 'DynamicThing') DynamicThing.cls_attr = "ham" manager.save(DynamicThing) rows = manager.query( ''' START base = node(*) MATCH tpe -[r:ISA]-> base RETURN tpe.id , r.__type__, base.id ORDER BY tpe.id, base.id ''') result = list(rows) assert result == [ ('DynamicThing', 'IsA', 'Entity'), ]
def test_class_attr_class_serialization(manager): with collector() as classes: class A(Entity): id = Uuid() cls_attr = "spam" class B(A): cls_attr = "ham" class C(B): pass manager.save_collected_classes(classes) # we want inherited attributes when we serialize assert manager.serialize(C) == { '__type__': 'PersistableType', 'id': 'C', 'cls_attr': 'ham', } # we don't want inherited attributes in the db query_str = join_lines( "START", get_start_clause(C, 'C', manager.type_registry), """ RETURN C """ ) (db_attrs,) = next(manager._execute(query_str)) properties = db_attrs.get_properties() assert 'cls_attr' not in properties
def test_delete_class_attrs(manager): with collector() as classes: class DynamicThing(Entity): id = Uuid() cls_attr = "spam" manager.save_collected_classes(classes) del DynamicThing # this is the same as creating a new manager using the same URL manager.reload_types() DynamicThing = manager.type_registry.get_class_by_id( 'DynamicThing') assert DynamicThing.cls_attr == "spam" delattr(DynamicThing, 'cls_attr') manager.save(DynamicThing) del DynamicThing manager.reload_types() DynamicThing = manager.type_registry.get_class_by_id( 'DynamicThing') assert not(hasattr(DynamicThing, 'cls_attr'))
def test_cannot_reparent_with_incompatible_attributes(manager, static_types): Cetacean = static_types['Cetacean'] WaterBound = static_types['WaterBound'] Whale = manager.create_type('Whale', (WaterBound,), {}) manager.save(Whale) with pytest.raises(CannotUpdateType) as e: manager.update_type(Whale, (Cetacean,)) assert e.value.message == "Inherited attributes are not identical" with collector() as collected: class A(Entity): foo = String() class B(Entity): foo = Bool() manager.save_collected_classes(collected) C = manager.create_type('C', (A,), {}) manager.save(C) with pytest.raises(CannotUpdateType) as e: manager.update_type(C, (B,)) assert e.value.message == "Inherited attributes are not identical"
def test_basic(manager): with collector() as collected: class A(Entity): pass class A2(A): pass class B(Entity): pass class B2(B): pass class AB(A, B): pass manager.save_collected_classes(collected) amended_registry = get_type_registry_with_base_change( manager, 'AB', ('A2', 'B2')) AmendedAB = amended_registry.get_class_by_id('AB') assert [c.__name__ for c in AmendedAB.mro()] == [ 'AB', 'A2', 'A', 'B2', 'B', 'Entity', 'AttributedBase', 'Persistable', 'object' ] # check original registry is unchanged OriginalAB = manager.type_registry.get_class_by_id('AB') assert [c.__name__ for c in OriginalAB.mro()] == [ 'AB', 'A', 'B', 'Entity', 'AttributedBase', 'Persistable', 'object' ]
def test_changing_bases_does_not_create_duplicate_types(manager): with collector() as classes: class ShrubBaseA(Entity): id = Uuid() class ShrubBaseB(Entity): pass class Shrub(ShrubBaseA): pass manager.save_collected_classes(classes) del Shrub manager.reload_types() with collector() as classes: class Shrub(ShrubBaseB, ShrubBaseA): pass class SubShrub(Shrub): pass manager.type_registry.register(SubShrub) manager.save(SubShrub) rows = manager.query( ''' START base = node(*) MATCH tpe -[r:ISA]-> base RETURN tpe.id , r.__type__, r.base_index, base.id ORDER BY tpe.id, r.base_index, base.id ''') result = list(rows) assert result == [ ('Shrub', 'IsA', 0, 'ShrubBaseB'), ('Shrub', 'IsA', 1, 'ShrubBaseA'), ('ShrubBaseA', 'IsA', 0, 'Entity'), ('ShrubBaseB', 'IsA', 0, 'Entity'), ('SubShrub', 'IsA', 0, 'Shrub'), ]
def test_unknown_types(manager): with collector() as collected: class A(Entity): pass manager.save_collected_classes(collected) with pytest.raises(UnknownType): get_type_registry_with_base_change(manager, 'A', ['Invalid']) with pytest.raises(UnknownType): get_type_registry_with_base_change(manager, 'Invalid', ['A'])
def test_class_name_escaping(manager): with collector() as classes: class Match(Entity): id = Uuid() where = Uuid() class Set(Match): pass class Return(Set): pass manager.save_collected_classes(classes)
def test_dynamic_unique_attr_adds_label(manager): with collector() as collected: class Foo(Entity): attr = Integer(unique=False) class Bar(Entity): attr = Integer(unique=True) manager.save_collected_classes(collected) assert manager.type_registry.get_labels_for_type(Foo) == set() assert manager.type_registry.get_labels_for_type(Bar) == set(["Bar"])
def test_duplicate_base_class(manager): with collector() as collected: class A(Entity): pass class B(Entity): pass class C(A, B): pass manager.save_collected_classes(collected) with pytest.raises(ValueError) as ex: get_type_registry_with_base_change(manager, 'C', ('A', 'B', 'A')) assert "duplicate base class" in str(ex)
def test_load_class_attr(manager): with collector() as classes: class DynamicThing(Entity): id = Uuid(unique=True) cls_attr = "spam" manager.save_collected_classes(classes) manager.reload_types() data = { '__type__': 'PersistableType', 'id': 'DynamicThing', 'cls_attr': 'ham' } cls = manager.deserialize(data) assert cls.cls_attr == 'ham'
def test_become_your_own_ancestor(manager): with collector() as collected: class A(Entity): pass class A2(A): pass class A3(A2): pass manager.save_collected_classes(collected) # become your own parent with pytest.raises(ValueError) as ex: get_type_registry_with_base_change(manager, 'A', ('A3',)) assert "inheritance cycle" in str(ex)
def test_amended_indexes(manager): with collector() as collected: class A(Entity): id = Integer(unique=True) class B(Entity): code = String(unique=True) manager.save_collected_classes(collected) amended_registry = get_type_registry_with_base_change( manager, 'B', ('A',)) # confirm that the "amended" indexes of B are now using both id and code amended_indexes = amended_registry.get_unique_attrs(B) assert {(get_type_id(cls), attr) for cls, attr in amended_indexes} == { ('A', 'id'), ('B', 'code') }
def test_class_attr_inheritence(manager): with collector() as classes: class A(Entity): attr = True class B(A): pass class C(B): attr = False class D(C): pass manager.save_collected_classes(classes) assert A().attr is True assert B().attr is True assert C().attr is False assert D().attr is False
def test_reparent_with_matching_attributes(manager): """ Reparenting with different but identical attributes is allowed. """ with collector() as collected: class C(Entity): foo = String() class D(Entity): foo = String() manager.save_collected_classes(collected) E = manager.create_type('E', (C,), {}) manager.save(E) manager.update_type(E, (D,)) UpdatedD = manager.type_registry.get_class_by_id(get_type_id(D)) UpdatedE = manager.type_registry.get_class_by_id(get_type_id(E)) assert UpdatedE.__bases__ == (UpdatedD,) assert issubclass(UpdatedE, UpdatedD)
def test_amended_indexes_same_attr_name(manager): with collector() as collected: class A(Entity): id = Integer(unique=True) class B(Entity): id = String(unique=True) class C(A): pass manager.save_collected_classes(collected) amended_registry = get_type_registry_with_base_change( manager, 'C', ('A', 'B')) # confirm that the "amended" indexes of C are still just A.id amended_indexes = amended_registry.get_unique_attrs(C) assert {(get_type_id(cls), attr) for cls, attr in amended_indexes} == { ('A', 'id'), }
def test_class_att_overriding(manager): with collector() as classes: class A(Entity): id = Uuid() cls_attr = "spam" class B(A): cls_attr = "ham" class C(B): pass manager.save_collected_classes(classes) manager.reload_types() a = A() b = B() c = C() assert a.cls_attr == "spam" assert b.cls_attr == "ham" assert c.cls_attr == "ham" manager.save(a) manager.save(b) manager.save(c) query_str = join_lines( "START", get_start_clause(A, 'A', manager.type_registry), """ MATCH node -[:INSTANCEOF]-> () -[:ISA*0..]-> A return node """ ) results = list(manager.query(query_str)) for col, in results: assert col.cls_attr == col.__class__.cls_attr
def test_true_class_attr(manager): with collector() as classes: class DynamicThing(Entity): id = Uuid() cls_attr = True manager.save_collected_classes(classes) # we want inherited attributes when we serialize assert manager.serialize(DynamicThing) == { '__type__': 'PersistableType', 'id': 'DynamicThing', 'cls_attr': True, } manager.reload_types() DynamicThing = manager.type_registry.get_class_by_id( 'DynamicThing') thing = DynamicThing() assert DynamicThing.cls_attr is True assert thing.cls_attr is True
def test_class_attrs(manager): with collector() as collected: class A(Entity): cls_attr = 'fromA' cls_attr_A = 'A' class B(Entity): cls_attr = 'fromB' cls_attr_B = 'B' class C(A): cls_attr = 'fromC' cls_attr_C = 'C' manager.save_collected_classes(collected) def get_cls_attrs(cls): attr_names = ['cls_attr', 'cls_attr_A', 'cls_attr_B', 'cls_attr_C'] return {a: getattr(cls, a, None) for a in attr_names} # check initial state assert get_cls_attrs(C) == { 'cls_attr': 'fromC', 'cls_attr_A': 'A', 'cls_attr_B': None, 'cls_attr_C': 'C' } amended_registry = get_type_registry_with_base_change( manager, 'C', ('A', 'B')) amended_C = amended_registry.get_class_by_id('C') assert get_cls_attrs(amended_C) == { 'cls_attr': 'fromC', 'cls_attr_A': 'A', 'cls_attr_B': 'B', 'cls_attr_C': 'C' }
def test_static_descriptor_caching(manager): with collector(): class Thing(Entity): prop_x = String(required=True) class That(Entity): prop_y = String(unique=True) type_registry = TypeRegistry() thing_descriptor1 = type_registry.get_descriptor(Thing) thing_descriptor2 = type_registry.get_descriptor(Thing) that_descriptor1 = type_registry.get_descriptor(That) that_descriptor2 = type_registry.get_descriptor(That) # assert repeated calls return the same objects assert thing_descriptor1 is thing_descriptor2 assert that_descriptor1 is that_descriptor2 # check that different types still get different descriptors assert thing_descriptor1 is not that_descriptor1
def test_bad_mro(manager): """ from http://www.python.org/download/releases/2.3/mro/ ----------- | | | O | | / \ | - X Y / | / | / | / |/ A B \ / ? """ with collector() as collected: class X(Entity): pass class Y(Entity): pass class A(X, Y): pass class B(Y): # to become B(Y, X) pass class AB(A, B): pass manager.save_collected_classes(collected) with pytest.raises(ValueError) as ex: get_type_registry_with_base_change(manager, 'B', ('Y', 'X')) assert "Cannot create a consistent method resolution" in str(ex)
def test_descriptor_property_caching(manager): with collector(): class Thing(Entity): prop_x = String(required=True) class That(Entity): prop_y = String(unique=True) type_registry = TypeRegistry() thing_descriptor = type_registry.get_descriptor(Thing) that_descriptor = type_registry.get_descriptor(That) property_names = [ 'attributes', 'relationships', 'declared_attributes', 'class_attributes', 'declared_class_attributes' ] for name in property_names: thing_val1 = getattr(thing_descriptor, name) thing_val2 = getattr(thing_descriptor, name) that_val = getattr(that_descriptor, name) assert thing_val1 is thing_val2, name assert thing_val2 is not that_val, name
def test_move_down_the_hieararchy(manager): with collector() as collected: class A(Entity): pass class A2(A): pass class A3(A2): pass class A4(A3): pass class B(A): pass class C(B): pass manager.save_collected_classes(collected) get_type_registry_with_base_change(manager, 'B', ('A4',))
def test_class_and_attr_name_clash(manager): with collector() as classes: class Foo(Entity): Foo = Uuid() manager.save_collected_classes(classes)
def test_invalidate_type_system(manager, static_types): Related = static_types['Related'] with collector(): class TypeA(Entity): id = Uuid(unique=True) class TypeB(Entity): attr = String(unique=True) class BaseType(Entity): pass manager.type_registry.register(TypeA) manager.type_registry.register(TypeB) manager.type_registry.register(BaseType) versions = [] def is_distinct_version(v): distinct = v not in versions versions.append(v) return distinct v0 = manager._type_system_version() assert is_distinct_version(v0) manager.save(TypeA) # create type manager.save(BaseType) v1 = manager._type_system_version() assert is_distinct_version(v1) manager.save(TypeA) # save unchanged type assert manager._type_system_version() == v1 type_a = TypeA() manager.save(type_a) # create instance assert manager._type_system_version() == v1 type_b = TypeB(attr="value") manager.save(TypeB) manager.save(type_b) # create instance & type v3 = manager._type_system_version() assert is_distinct_version(v3) type_b.new_attr = "value" manager.save(type_b) # add instance attribute assert manager._type_system_version() == v3 type_b.new_attr = "new_value" manager.save(type_b) # modify instance attribute assert manager._type_system_version() == v3 del type_b.new_attr manager.save(type_b) # delete instance attribute assert manager._type_system_version() == v3 isa = IsA(type_a, BaseType) isa.base_index = 1 manager.save(isa) # create a type-hierarchy relationship v4 = manager._type_system_version() assert is_distinct_version(v4) rel = Related(type_a, type_b) manager.save(rel) # create a non-type-hierarchy relationship assert manager._type_system_version() == v4 manager.update_type(TypeA, (BaseType,)) # reparent v5 = manager._type_system_version() assert is_distinct_version(v5) # update_type reloads the type hierarchy, so refresh references TypeA = manager.type_registry.get_class_by_id('TypeA') TypeB = manager.type_registry.get_class_by_id('TypeB') BaseType = manager.type_registry.get_class_by_id('BaseType') manager.delete(isa) # delete a type-hierarchy relationship v6 = manager._type_system_version() assert is_distinct_version(v6) manager.delete(rel) # delete a non-type-hierarchy relationship assert manager._type_system_version() == v6 manager.delete(TypeA) # delete type v7 = manager._type_system_version() assert is_distinct_version(v7) manager.delete(type_a) # delete instance assert manager._type_system_version() == v7 TypeB.cls_attr = "value" manager.save(TypeB) # add class attribute v8 = manager._type_system_version() assert is_distinct_version(v8) TypeB.cls_attr = "new_value" manager.save(TypeB) # modify class attribute v9 = manager._type_system_version() assert is_distinct_version(v9) del TypeB.cls_attr manager.save(TypeB) # delete class attribute v10 = manager._type_system_version() assert is_distinct_version(v10)