Commit f6e415b4 authored by "Sylvain ext:(%22)'s avatar "Sylvain ext:(%22)
Browse files

* implements __cmp__ and __hash__ on entity and relation schemas to

  make them comparable to their equivalent type (eg string representation)
* major api and implementation cleanup thanks to this change
* deprecate schema_view module
* test fixes
* backward compatible with proper warnings when necessary
parent c701d62a3ec4
ChangeLog for yams
------------------
--
* implements __cmp__ and __hash__ on entity and relation schemas to
make them comparable to their equivalent type (eg string representation)
* major api cleanup thanks to this change
* deprecate schema_view
* test fixes
* backward compatible with proper warnings when necessary
2006-10-08 -- 0.8.0
* new RestrictedEntityType base class and new maxsize, vocabulary and unique
parameters on relation definition to ease schema construction
......
......@@ -143,13 +143,13 @@ class Definition(object):
return (etype,)
def _wildcard_etypes(self, schema):
for eschema in schema.entities(True):
for eschema in schema.entities():
if eschema.is_final() or eschema.meta:
continue
yield eschema.type
def _pow_etypes(self, schema):
for eschema in schema.entities(True):
for eschema in schema.entities():
if eschema.is_final():
continue
yield eschema.type
......
......@@ -221,7 +221,7 @@ class SchemaLoader(object):
if not isinstance(definition, builder.EntityType):
definition.register_relations(schema)
# set permissions on entities and relations
for erschema in schema.entities(schema=True)+schema.relations(schema=True):
for erschema in schema.entities() + schema.relations():
self._set_perms(directory, erschema)
return schema
......
This diff is collapsed.
......@@ -150,12 +150,11 @@ class SchemaVisitor:
return not (erschema.is_final() or (self.skipmeta and erschema.meta))
def display_rel(self, rschema, setype, tetype):
rtype = rschema.type
if (rtype, setype, tetype) in self._done:
if (rschema, setype, tetype) in self._done:
return False
self._done.add((rtype, setype, tetype))
self._done.add((rschema, setype, tetype))
if rschema.symetric:
self._done.add((rtype, tetype, setype))
self._done.add((rschema, tetype, setype))
return True
def nodes(self):
......@@ -273,12 +272,12 @@ class SchemaVisitor(SchemaDotPropsHandler):
def visit(self, schema, skipped_entities=(), skipped_relations=()):
"""browse schema nodes and generate dot instructions"""
entities = [eschema for eschema in schema.entities(True)
entities = [eschema for eschema in schema.entities()
if not (eschema.is_final() or eschema.type in skipped_entities)]
self._eindex = dict([(e.type, e) for e in entities])
for eschema in entities:
self.visit_entity_schema(eschema)
for rschema in schema.relations(schema=True):
for rschema in schema.relations():
if rschema.is_final() or rschema.type in skipped_relations:
continue
self.visit_relation_schema(rschema)
......@@ -298,7 +297,7 @@ class SchemaVisitor(SchemaDotPropsHandler):
return
self._processed.add(etype)
nodeprops = self.get_props_for_eschema(eschema)
self.generator.emit_node(etype, **nodeprops)
self.generator.emit_node(str(etype), **nodeprops)
def visit_relation_schema(self, rschema, comingfrom=None):
"""visit relations separately to handle easily symetric relations"""
......@@ -308,14 +307,14 @@ class SchemaVisitor(SchemaDotPropsHandler):
self._processed.add(rtype)
if comingfrom:
etype, target = comingfrom
for subjtype, objtypes in rschema.association_types():
for subjtype, objtypes in rschema.associations():
if self._eindex and not subjtype in self._eindex:
continue
for objtype in objtypes:
if self._eindex and not objtype in self._eindex:
continue
edgeprops = self.get_props_for_rschema(rschema)
self.generator.emit_edge(subjtype, objtype, **edgeprops)
self.generator.emit_edge(str(subjtype), str(objtype), **edgeprops)
......
......@@ -13,6 +13,9 @@ from logilab.common.ureports import Section, Title, Table, Link, Text, List
_ = getattr(__builtins__, '_', str)
from warnings import warn
warn('deprecated module', DeprecationWarning, stacklevel=2)
class SchemaViewer:
"""return an ureport layout for some part of a schema"""
......
......@@ -94,7 +94,7 @@ class EsqlFileReader(FileReader):
arg = [eval(val.strip()) for val in typeattrs.split(',')]
rqltype = check_choice_values(arg)
return rqltype, [CHOICE_CLASSES[sqltype](arg)], 0
self.error(sqltype)
self.error('unknown type %r' % sqltype)
def parse_suite(self, rdef, suite, etype):
......
modname varchar(30) not null
version varchar(10) not null default '0.1'
copyright text not null
license choice('')
license choice('GPL', 'ZPL')
pyversions multiplechoice('2.1', '2.2', '2.3')
short_desc varchar(80) not null
long_desc itext not null
author varchar(100) not null
author_email varchar(100) not null
mailinglist varchar(100)
debian_handler choice('')
debian_handler choice('machin', 'bidule')
......@@ -4,8 +4,6 @@
Copyright Logilab 2002-2004, all rights reserved.
"""
__revision__ = "$Id: unittest_schema.py,v 1.15 2006-04-10 14:39:03 syt Exp $"
from logilab.common.testlib import TestCase, unittest_main
from yams.builder import EntityType, RelationType, RelationDefinition
......@@ -51,13 +49,13 @@ class BaseSchemaTC(TestCase):
('Person concerne Societe'),
('Affaire concerne Societe'),
)
for rel in RELS:
for i, rel in enumerate(RELS):
_from, _type, _to = rel.split()
try:
schema.rschema(_type)
except KeyError:
schema.add_relation_type(RelationType(_type))
schema.add_relation_def(RelationDefinition(_from, _type, _to))
schema.add_relation_def(RelationDefinition(_from, _type, _to, order=i))
schema.rschema('nom').set_rproperty('Person', 'String', 'cardinality','11') # not null
enote.set_rproperty('type', 'constraints',
......@@ -130,12 +128,37 @@ RELATIONS_GOOD_VALUES = {
class EntitySchemaTC(BaseSchemaTC):
#def test_base(self):
# self.assertEquals(repr(enote), "<EntitySchema Note ['date', 'type'] - ['evaluee']>")
def test_base(self):
self.assert_(repr(eperson))
# def test_is_uid(self):
# eperson.set_uid('nom')
# self.assertEquals(eperson.is_uid('nom'), True)
def test_cmp(self):
self.failUnless(eperson == 'Person')
self.failUnless('Person' == eperson)
self.failUnless(eperson != 'Note')
self.failUnless('Note' != eperson)
self.failIf(enote == eperson)
self.failIf(eperson == enote)
self.failUnless(enote != eperson)
self.failUnless(eperson != enote)
l = [eperson, enote, eaffaire, esociete]
print 'sort'
l.sort()
self.assertListEquals(l, [eaffaire, enote, eperson, esociete])
self.assertListEquals(l, ['Affaire', 'Note', 'Person', 'Societe'])
def test_hash(self):
from copy import copy
d = {}
d[eperson] = 'p'
d[enote] = 'n'
self.failUnlessEqual(d[copy(eperson)], 'p')
self.failUnlessEqual(d[copy(enote)], 'n')
self.failUnlessEqual(d['Person'], 'p')
self.failUnlessEqual(d['Note'], 'n')
def test_is_final(self):
self.assertEquals(eperson.is_final(), False)
......@@ -183,7 +206,7 @@ class EntitySchemaTC(BaseSchemaTC):
def test_object_relations(self):
"""check object relations a returned in the same order as in the
schema definition"""
rels = eaffaire.object_relations(False)
rels = eaffaire.object_relations()
expected = ['concerne']
self.assertEquals(rels, expected)
rels = [schem.type for schem in eaffaire.object_relations()]
......@@ -202,21 +225,40 @@ class EntitySchemaTC(BaseSchemaTC):
def test_destination_type(self):
"""check subject relations a returned in the same order as in the
schema definition"""
expected = 'String'
self.assertEquals(eperson.destination_type('nom'), expected)
self.assertRaises(AssertionError,
eperson.destination_type, 'travaille')
self.assertEquals(eperson.destination('nom'), 'String')
self.assertRaises(AssertionError, eperson.destination, 'travaille')
class RelationSchemaTC(BaseSchemaTC):
def test_cmp(self):
self.failUnless(rconcerne == 'concerne')
self.failUnless('concerne' == rconcerne)
self.failUnless(rconcerne != 'nom')
self.failUnless('nom' != rconcerne)
self.failIf(rnom == rconcerne)
self.failIf(rconcerne == rnom)
self.failUnless(rnom != rconcerne)
self.failUnless(rconcerne != rnom)
def test_hash(self):
from copy import copy
d = {}
d[rconcerne] = 'p'
d[rnom] = 'n'
self.failUnlessEqual(d[copy(rconcerne)], 'p')
self.failUnlessEqual(d[copy(rnom)], 'n')
self.failUnlessEqual(d['concerne'], 'p')
self.failUnlessEqual(d['nom'], 'n')
def test_base(self):
self.assertEquals(repr(rnom), "<RelationSchema nom [('Person', ['String'])]>")
self.assert_(repr(rnom))
def test_star_types(self):
types = rconcerne.subject_types()
types = rconcerne.subjects()
types.sort()
self.assertEquals(types, ['Affaire', 'Person'])
types = rconcerne.object_types()
types = rconcerne.objects()
types.sort()
self.assertEquals(types, ['Affaire', 'Societe'])
......@@ -235,11 +277,11 @@ class RelationSchemaTC(BaseSchemaTC):
def test_association_types(self):
expected = [ ('Affaire', ['Societe']),
('Person', ['Affaire', 'Societe']) ]
assoc_types = rconcerne.association_types()
assoc_types = rconcerne.associations()
assoc_types.sort()
self.assertEquals(assoc_types, expected)
assoc_types = []
for _from, _to in rconcerne.association_types():
for _from, _to in rconcerne.associations():
assoc_types.append( (_from, _to))
#assoc_types.append( (_from.type, [s.type for s in _to]) )
assoc_types.sort()
......@@ -363,7 +405,7 @@ class SymetricTC(TestCase):
schema.add_relation_def(RelationDefinition('Project', 'see_also', 'Project'))
rsee_also = schema.rschema('see_also')
subj_types = rsee_also.association_types()
subj_types = rsee_also.associations()
subj_types.sort()
self.assertEquals(subj_types,
[('Bug', ['Bug', 'Story', 'Project']),
......@@ -375,7 +417,7 @@ class SymetricTC(TestCase):
rdef.register_relations(schema)
rsee_also = schema.rschema('see_also')
subj_types = rsee_also.association_types()
subj_types = rsee_also.associations()
subj_types.sort()
for key, vals in subj_types:
vals.sort()
......
......@@ -36,11 +36,11 @@ Person -> Societe
class MyVisitor(schema2dot.SchemaVisitor):
"""customize drawing options for better control"""
def get_props_for_eschema(self, e_schema):
return {'label' : e_schema.type}
def get_props_for_eschema(self, eschema):
return {'label' : eschema.type}
def get_props_for_rschema(self, r_schema):
return {'label' : r_schema.type}
def get_props_for_rschema(self, rschema):
return {'label' : rschema.type}
class DotTC(TestCase):
......
......@@ -29,177 +29,175 @@ class DummyDefaultHandler:
schema = SchemaLoader().load(DATADIR, default_handler=DummyDefaultHandler())
EXPECTED_DATA_DROP = """
DROP TABLE Affaire;
EXPECTED_DATA_NO_DROP = """
CREATE TABLE Affaire(
sujet varchar(128),
ref varchar(12),
inline_rel integer
sujet varchar(128),
ref varchar(12),
inline_rel integer
);
CREATE INDEX affaire_inline_rel_idx ON Affaire (inline_rel);
DROP TABLE Eetype;
CREATE TABLE Eetype(
name varchar(64) UNIQUE NOT NULL,
description text,
meta boolean,
final boolean,
initial_state integer
name varchar(64) UNIQUE NOT NULL,
description text,
meta boolean,
final boolean,
initial_state integer
);
CREATE INDEX eetype_name_idx ON Eetype (name);
CREATE INDEX eetype_initial_state_idx ON Eetype (initial_state);
DROP TABLE Note;
CREATE TABLE Note(
date varchar(10),
type varchar(1),
para varchar(512)
date varchar(10),
type varchar(1),
para varchar(512)
);
DROP TABLE Person;
CREATE TABLE Person(
nom varchar(64) NOT NULL,
prenom varchar(64),
sexe varchar(1) DEFAULT 'M',
promo text,
titre varchar(128),
adel varchar(128),
ass varchar(128),
web varchar(128),
tel integer,
fax integer,
datenaiss date,
test boolean
nom varchar(64) NOT NULL,
prenom varchar(64),
sexe varchar(1) DEFAULT 'M',
promo text,
titre varchar(128),
adel varchar(128),
ass varchar(128),
web varchar(128),
tel integer,
fax integer,
datenaiss date,
test boolean
);
DROP TABLE Societe;
CREATE TABLE Societe(
nom varchar(64),
web varchar(128),
tel integer,
fax integer,
rncs varchar(32),
ad1 varchar(128),
ad2 varchar(128),
ad3 varchar(128),
cp varchar(12),
ville varchar(32)
nom varchar(64),
web varchar(128),
tel integer,
fax integer,
rncs varchar(32),
ad1 varchar(128),
ad2 varchar(128),
ad3 varchar(128),
cp varchar(12),
ville varchar(32)
);
DROP TABLE State;
CREATE TABLE State(
eid integer PRIMARY KEY,
name varchar(256) NOT NULL,
description text
eid integer PRIMARY KEY,
name varchar(256) NOT NULL,
description text
);
CREATE INDEX state_name_idx ON State (name);
DROP TABLE pkginfo;
CREATE TABLE pkginfo(
modname varchar(30) DEFAULT 'yo' NOT NULL,
version varchar(10) NOT NULL,
copyright text NOT NULL,
license text,
pyversions text,
short_desc varchar(80) NOT NULL,
long_desc text NOT NULL,
author varchar(100) NOT NULL,
author_email varchar(100) NOT NULL,
mailinglist varchar(100),
debian_handler text
modname varchar(30) DEFAULT 'yo' NOT NULL,
version varchar(10) NOT NULL,
copyright text NOT NULL,
license text,
pyversions text,
short_desc varchar(80) NOT NULL,
long_desc text NOT NULL,
author varchar(100) NOT NULL,
author_email varchar(100) NOT NULL,
mailinglist varchar(100),
debian_handler text
);
"""
EXPECTED_DATA_NO_DROP = """
CREATE TABLE Affaire(
sujet varchar(128),
ref varchar(12),
inline_rel integer
CREATE TABLE concerne_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT concerne_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT concerne_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT concerne_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE INDEX affaire_inline_rel_idx ON Affaire (inline_rel);
CREATE TABLE Eetype(
name varchar(64) UNIQUE NOT NULL,
description text,
meta boolean,
final boolean,
initial_state integer
CREATE INDEX concerne_relation_from_idx ON concerne_relation (eid_from);
CREATE INDEX concerne_relation_to_idx ON concerne_relation (eid_to);
CREATE TABLE evaluee_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT evaluee_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT evaluee_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT evaluee_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE INDEX eetype_name_idx ON Eetype (name);
CREATE INDEX eetype_initial_state_idx ON Eetype (initial_state);
CREATE TABLE Note(
date varchar(10),
type varchar(1),
para varchar(512)
CREATE INDEX evaluee_relation_from_idx ON evaluee_relation (eid_from);
CREATE INDEX evaluee_relation_to_idx ON evaluee_relation (eid_to);
CREATE TABLE next_state_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT next_state_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT next_state_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT next_state_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE TABLE Person(
nom varchar(64) NOT NULL,
prenom varchar(64),
sexe varchar(1) DEFAULT 'M',
promo text,
titre varchar(128),
adel varchar(128),
ass varchar(128),
web varchar(128),
tel integer,
fax integer,
datenaiss date,
test boolean
CREATE INDEX next_state_relation_from_idx ON next_state_relation (eid_from);
CREATE INDEX next_state_relation_to_idx ON next_state_relation (eid_to);
CREATE TABLE obj_wildcard_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT obj_wildcard_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT obj_wildcard_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT obj_wildcard_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE TABLE Societe(
nom varchar(64),
web varchar(128),
tel integer,
fax integer,
rncs varchar(32),
ad1 varchar(128),
ad2 varchar(128),
ad3 varchar(128),
cp varchar(12),
ville varchar(32)
CREATE INDEX obj_wildcard_relation_from_idx ON obj_wildcard_relation (eid_from);
CREATE INDEX obj_wildcard_relation_to_idx ON obj_wildcard_relation (eid_to);
CREATE TABLE state_of_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT state_of_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT state_of_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT state_of_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE TABLE State(
eid integer PRIMARY KEY,
name varchar(256) NOT NULL,
description text
CREATE INDEX state_of_relation_from_idx ON state_of_relation (eid_from);
CREATE INDEX state_of_relation_to_idx ON state_of_relation (eid_to);
CREATE TABLE subj_wildcard_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT subj_wildcard_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT subj_wildcard_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT subj_wildcard_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE INDEX state_name_idx ON State (name);
CREATE TABLE pkginfo(
modname varchar(30) DEFAULT 'yo' NOT NULL,
version varchar(10) NOT NULL,
copyright text NOT NULL,
license text,
pyversions text,
short_desc varchar(80) NOT NULL,
long_desc text NOT NULL,
author varchar(100) NOT NULL,
author_email varchar(100) NOT NULL,
mailinglist varchar(100),
debian_handler text
CREATE INDEX subj_wildcard_relation_from_idx ON subj_wildcard_relation (eid_from);
CREATE INDEX subj_wildcard_relation_to_idx ON subj_wildcard_relation (eid_to);
CREATE TABLE sym_rel_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT sym_rel_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT sym_rel_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT sym_rel_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE INDEX sym_rel_relation_from_idx ON sym_rel_relation (eid_from);
CREATE INDEX sym_rel_relation_to_idx ON sym_rel_relation (eid_to);
CREATE TABLE travaille_relation (
eid_from INTEGER NOT NULL,
eid_to INTEGER NOT NULL,
CONSTRAINT travaille_relation_p_key PRIMARY KEY(eid_from, eid_to),
CONSTRAINT travaille_relation_fkey1 FOREIGN KEY (eid_from) REFERENCES entities (eid) ON DELETE CASCADE,
CONSTRAINT travaille_relation_fkey2 FOREIGN KEY (eid_to) REFERENCES entities (eid) ON DELETE CASCADE
);
CREATE INDEX travaille_relation_from_idx ON travaille_relation (eid_from);
CREATE INDEX travaille_relation_to_idx ON travaille_relation (eid_to);
"""
class SQLSchemaTC(TestCase):
def test_known_values(self):
output = StringIO()
schema2sql(output, schema)
self.assertLinesEquals(output.getvalue().strip(), EXPECTED_DATA_NO_DROP.strip())
def test_known_values_drop(self):
output = StringIO()
schema2sql(output, schema, drop=True)
self.assertLinesEquals(output.getvalue().strip(), EXPECTED_DATA_DROP.strip())
def test_known_values_no_drop(self):
output = StringIO()
schema2sql(output, schema, drop=False)
self.assertLinesEquals(output.getvalue().strip(), EXPECTED_DATA_NO_DROP.strip())
output = schema2sql(schema)
self.assertLinesEquals(output.strip(), EXPECTED_DATA_NO_DROP.strip())
if __name__ == '__main__':
......
......@@ -78,27 +78,27 @@ class SchemaLoaderTC(TestCase):
['ad1', 'ad2', 'ad3', 'cp', 'evaluee',
'fax', 'nom', 'rncs', 'subj_wildcard', 'tel', 'ville',
'web'])
self.assertListEquals(sorted(eschema.object_relations(False)),
self.assertListEquals(sorted(eschema.object_relations()),
['concerne', 'obj_wildcard', 'travaille'])
eschema = schema.eschema('Eetype')
self.assertEquals(eschema.description, 'define an entity type, used to build the application schema')
self.assertEquals(eschema.meta, True)
self.assertEquals(eschema.is_final(), False)