From 5ac0be0b2b8837a167329dd6e8d3a20f5387169a Mon Sep 17 00:00:00 2001 From: Mike Boers Date: Tue, 29 Sep 2015 11:18:05 -0700 Subject: [PATCH] Resolve structures, e.g. lists of entities --- sgschema/schema.py | 24 ++++++ tests/test_deep_fields.py | 54 ++++++++++++ tests/test_entities.py | 54 ++++++++++++ tests/test_fields.py | 71 +++++++++++++++ tests/test_load.py | 1 + tests/test_resolve.py | 177 -------------------------------------- tests/test_structures.py | 66 ++++++++++++++ 7 files changed, 270 insertions(+), 177 deletions(-) create mode 100644 tests/test_deep_fields.py create mode 100644 tests/test_entities.py create mode 100644 tests/test_fields.py delete mode 100644 tests/test_resolve.py create mode 100644 tests/test_structures.py diff --git a/sgschema/schema.py b/sgschema/schema.py index 4010aa5..b73c405 100644 --- a/sgschema/schema.py +++ b/sgschema/schema.py @@ -285,6 +285,30 @@ def resolve_field(self, entity_type, field_spec=None, auto_prefix=True, implicit resolved_fields.append(field) return resolved_fields + def resolve_structure(self, x, **kwargs): + + if isinstance(x, (list, tuple)): + return type(x)(self.resolve_structure(x, **kwargs) for x in x) + + elif isinstance(x, dict): + if 'type' in x and x['type'] in self.entities: + entity_type = x['type'] + new_values = {} + for field_spec, value in x.iteritems(): + value = self.resolve_structure(value) + for field in self.resolve_field(entity_type, field_spec, **kwargs): + new_values[field] = value + return new_values + else: + return { + k: self.resolve_structure(v, **kwargs) + for k, v in x.iteritems() + } + + else: + return x + + diff --git a/tests/test_deep_fields.py b/tests/test_deep_fields.py new file mode 100644 index 0000000..43c4233 --- /dev/null +++ b/tests/test_deep_fields.py @@ -0,0 +1,54 @@ +from . import * + + +class TestResolveDeepFields(TestCase): + + def setUp(self): + self.s = s = load_schema() + self.s.load({ + 'Task': { + 'field_aliases': { + 'status': 'sg_status_list', + 'parent': 'entity', + }, + 'field_tags': { + 'core': ['content', 'step', 'sg_status_list'], + } + }, + 'Shot': { + 'field_aliases': { + 'status': 'sg_status_list', + }, + 'field_tags': { + 'core': ['code', 'description', 'sg_sequence'], + } + }, + + 'Asset': { + 'field_aliases': { + 'status': 'sg_status_list', + }, + 'field_tags': { + 'core': ['code', 'asset_type'], + } + }, + }) + + def test_sanity(self): + self.assertEqual(self.s.resolve_field('Task', 'sg_status_list'), ['sg_status_list']) + self.assertEqual(self.s.resolve_field('Task', 'status_list'), ['sg_status_list']) + self.assertEqual(self.s.resolve_field('Task', '$status'), ['sg_status_list']) + + def test_passthrough(self): + self.assertEqual(self.s.resolve_field('Task', 'entity.Shot.sg_status_list'), ['entity.Shot.sg_status_list']) + + def test_explicit_aliases(self): + self.assertEqual(self.s.resolve_field('Task', '$parent.Shot.$status'), ['entity.Shot.sg_status_list']) + self.assertEqual(self.s.resolve_field('Task', '$parent.Shot.status_list'), ['entity.Shot.sg_status_list']) + self.assertEqual(self.s.resolve_field('Task', 'entity.Shot.$status'), ['entity.Shot.sg_status_list']) + + def test_explicit_tags(self): + self.assertEqual(self.s.resolve_field('Task', '$parent.Shot.#core'), ['entity.Shot.sg_sequence', 'entity.Shot.code', 'entity.Shot.description']) + + + diff --git a/tests/test_entities.py b/tests/test_entities.py new file mode 100644 index 0000000..441379a --- /dev/null +++ b/tests/test_entities.py @@ -0,0 +1,54 @@ +from . import * + + +class TestResolveEntities(TestCase): + + def setUp(self): + + self.s = s = Schema() + s.load({ + 'entities': { + 'Entity': { + 'aliases': ['A', 'with:Namespace'], + 'tags': ['X'], + }, + 'Another': {} + }, + 'entity_aliases': { + 'B': 'Entity', + }, + 'entity_tags': { + 'Y': ['Entity'], + 'Multiple': ['Entity', 'Another'], + } + }) + + def test_explicit(self): + self.assertEqual(self.s.resolve_entity('!Entity'), ['Entity']) + self.assertEqual(self.s.resolve_entity('$A'), ['Entity']) + self.assertEqual(self.s.resolve_entity('$B'), ['Entity']) + self.assertEqual(self.s.resolve_entity('#X'), ['Entity']) + self.assertEqual(self.s.resolve_entity('#Y'), ['Entity']) + + def test_namespace(self): + self.assertEqual(self.s.resolve_entity('$with:Namespace'), ['Entity']) + + def test_implicit(self): + self.assertEqual(self.s.resolve_entity('Entity'), ['Entity']) + self.assertEqual(self.s.resolve_entity('A'), ['Entity']) + self.assertEqual(self.s.resolve_entity('B'), ['Entity']) + + def test_missing(self): + self.assertEqual(self.s.resolve_entity('#Missing'), []) + self.assertEqual(self.s.resolve_entity('$Missing'), []) + self.assertEqual(self.s.resolve_entity('!Missing'), ['Missing']) + self.assertEqual(self.s.resolve_entity('Missing'), ['Missing']) + self.assertRaises(ValueError, self.s.resolve_entity, 'Missing', strict=True) + + def test_one(self): + self.assertEqual(self.s.resolve_one_entity('Entity'), 'Entity') + self.assertEqual(self.s.resolve_one_entity('!Entity'), 'Entity') + self.assertEqual(self.s.resolve_one_entity('$A'), 'Entity') + self.assertEqual(self.s.resolve_one_entity('#X'), 'Entity') + self.assertRaises(ValueError, self.s.resolve_one_entity, '#Missing') + self.assertRaises(ValueError, self.s.resolve_one_entity, '#Multiple') \ No newline at end of file diff --git a/tests/test_fields.py b/tests/test_fields.py new file mode 100644 index 0000000..83696e0 --- /dev/null +++ b/tests/test_fields.py @@ -0,0 +1,71 @@ +from . import * + + +class TestResolveFields(TestCase): + + def setUp(self): + + self.s = s = Schema() + s.load({ + 'entities': { + 'Entity': { + 'fields': { + 'attr': { + 'aliases': ['a', 'with:namespace'], + 'tags': ['x'], + + }, + 'sg_version': {}, + 'sg_type': {}, + 'name': {}, + 'sg_name': {}, + }, + 'field_aliases': { + 'b': 'attr', + }, + 'field_tags': { + 'y': ['attr'], + } + } + }, + }) + + def test_explicit(self): + self.assertEqual(self.s.resolve_field('Entity', '!attr'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', '$a'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', '$b'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', '#x'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', '#y'), ['attr']) + + def test_namespace(self): + self.assertEqual(self.s.resolve_field('Entity', '$with:namespace'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', 'with:namespace'), ['attr']) + + def test_implicit(self): + self.assertEqual(self.s.resolve_field('Entity', 'attr'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', 'a'), ['attr']) + self.assertEqual(self.s.resolve_field('Entity', 'b'), ['attr']) + + def test_prefix(self): + self.assertEqual(self.s.resolve_field('Entity', 'sg_version'), ['sg_version']) + self.assertEqual(self.s.resolve_field('Entity', 'version'), ['sg_version']) # Auto-prefix. + self.assertEqual(self.s.resolve_field('Entity', '!version'), ['version']) # Doesn't exist. + + self.assertEqual(self.s.resolve_field('Entity', 'sg_type'), ['sg_type']) + self.assertEqual(self.s.resolve_field('Entity', 'type'), ['type']) # This overlaps the implicit "type"! + self.assertEqual(self.s.resolve_field('Entity', '!type'), ['type']) + + self.assertEqual(self.s.resolve_field('Entity', 'sg_name'), ['sg_name']) + self.assertEqual(self.s.resolve_field('Entity', 'name'), ['name']) # different! + self.assertEqual(self.s.resolve_field('Entity', '!name'), ['name']) + + def test_missing_entity(self): + self.assertRaises(ValueError, self.s.resolve_field, 'Missing', 'field_name') + + def test_missing(self): + self.assertEqual(self.s.resolve_field('Entity', '$missing'), []) + self.assertEqual(self.s.resolve_field('Entity', '#missing'), []) + self.assertEqual(self.s.resolve_field('Entity', '!missing'), ['missing']) + self.assertEqual(self.s.resolve_field('Entity', 'missing'), ['missing']) + self.assertRaises(ValueError, self.s.resolve_field, 'Entity', 'missing', strict=True) + diff --git a/tests/test_load.py b/tests/test_load.py index 205fed4..6fa8f27 100644 --- a/tests/test_load.py +++ b/tests/test_load.py @@ -1,5 +1,6 @@ from . import * + class TestLoading(TestCase): def test_load_entity_tags(self): diff --git a/tests/test_resolve.py b/tests/test_resolve.py deleted file mode 100644 index 6d1f13c..0000000 --- a/tests/test_resolve.py +++ /dev/null @@ -1,177 +0,0 @@ -from . import * - - -class TestResolveEntities(TestCase): - - def setUp(self): - - self.s = s = Schema() - s.load({ - 'entities': { - 'Entity': { - 'aliases': ['A', 'with:Namespace'], - 'tags': ['X'], - }, - 'Another': {} - }, - 'entity_aliases': { - 'B': 'Entity', - }, - 'entity_tags': { - 'Y': ['Entity'], - 'Multiple': ['Entity', 'Another'], - } - }) - - def test_explicit(self): - self.assertEqual(self.s.resolve_entity('!Entity'), ['Entity']) - self.assertEqual(self.s.resolve_entity('$A'), ['Entity']) - self.assertEqual(self.s.resolve_entity('$B'), ['Entity']) - self.assertEqual(self.s.resolve_entity('#X'), ['Entity']) - self.assertEqual(self.s.resolve_entity('#Y'), ['Entity']) - - def test_namespace(self): - self.assertEqual(self.s.resolve_entity('$with:Namespace'), ['Entity']) - - def test_implicit(self): - self.assertEqual(self.s.resolve_entity('Entity'), ['Entity']) - self.assertEqual(self.s.resolve_entity('A'), ['Entity']) - self.assertEqual(self.s.resolve_entity('B'), ['Entity']) - - def test_missing(self): - self.assertEqual(self.s.resolve_entity('#Missing'), []) - self.assertEqual(self.s.resolve_entity('$Missing'), []) - self.assertEqual(self.s.resolve_entity('!Missing'), ['Missing']) - self.assertEqual(self.s.resolve_entity('Missing'), ['Missing']) - self.assertRaises(ValueError, self.s.resolve_entity, 'Missing', strict=True) - - def test_one(self): - self.assertEqual(self.s.resolve_one_entity('Entity'), 'Entity') - self.assertEqual(self.s.resolve_one_entity('!Entity'), 'Entity') - self.assertEqual(self.s.resolve_one_entity('$A'), 'Entity') - self.assertEqual(self.s.resolve_one_entity('#X'), 'Entity') - self.assertRaises(ValueError, self.s.resolve_one_entity, '#Missing') - self.assertRaises(ValueError, self.s.resolve_one_entity, '#Multiple') - - -class TestResolveFields(TestCase): - - def setUp(self): - - self.s = s = Schema() - s.load({ - 'entities': { - 'Entity': { - 'fields': { - 'attr': { - 'aliases': ['a', 'with:namespace'], - 'tags': ['x'], - - }, - 'sg_version': {}, - 'sg_type': {}, - 'name': {}, - 'sg_name': {}, - }, - 'field_aliases': { - 'b': 'attr', - }, - 'field_tags': { - 'y': ['attr'], - } - } - }, - }) - - def test_explicit(self): - self.assertEqual(self.s.resolve_field('Entity', '!attr'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', '$a'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', '$b'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', '#x'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', '#y'), ['attr']) - - def test_namespace(self): - self.assertEqual(self.s.resolve_field('Entity', '$with:namespace'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', 'with:namespace'), ['attr']) - - def test_implicit(self): - self.assertEqual(self.s.resolve_field('Entity', 'attr'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', 'a'), ['attr']) - self.assertEqual(self.s.resolve_field('Entity', 'b'), ['attr']) - - def test_prefix(self): - self.assertEqual(self.s.resolve_field('Entity', 'sg_version'), ['sg_version']) - self.assertEqual(self.s.resolve_field('Entity', 'version'), ['sg_version']) # Auto-prefix. - self.assertEqual(self.s.resolve_field('Entity', '!version'), ['version']) # Doesn't exist. - - self.assertEqual(self.s.resolve_field('Entity', 'sg_type'), ['sg_type']) - self.assertEqual(self.s.resolve_field('Entity', 'type'), ['type']) # This overlaps the implicit "type"! - self.assertEqual(self.s.resolve_field('Entity', '!type'), ['type']) - - self.assertEqual(self.s.resolve_field('Entity', 'sg_name'), ['sg_name']) - self.assertEqual(self.s.resolve_field('Entity', 'name'), ['name']) # different! - self.assertEqual(self.s.resolve_field('Entity', '!name'), ['name']) - - def test_missing_entity(self): - self.assertRaises(ValueError, self.s.resolve_field, 'Missing', 'field_name') - - def test_missing(self): - self.assertEqual(self.s.resolve_field('Entity', '$missing'), []) - self.assertEqual(self.s.resolve_field('Entity', '#missing'), []) - self.assertEqual(self.s.resolve_field('Entity', '!missing'), ['missing']) - self.assertEqual(self.s.resolve_field('Entity', 'missing'), ['missing']) - self.assertRaises(ValueError, self.s.resolve_field, 'Entity', 'missing', strict=True) - - - -class TestResolveDeepFields(TestCase): - - def setUp(self): - self.s = s = load_schema() - self.s.load({ - 'Task': { - 'field_aliases': { - 'status': 'sg_status_list', - 'parent': 'entity', - }, - 'field_tags': { - 'core': ['content', 'step', 'sg_status_list'], - } - }, - 'Shot': { - 'field_aliases': { - 'status': 'sg_status_list', - }, - 'field_tags': { - 'core': ['code', 'description', 'sg_sequence'], - } - }, - - 'Asset': { - 'field_aliases': { - 'status': 'sg_status_list', - }, - 'field_tags': { - 'core': ['code', 'asset_type'], - } - }, - }) - - def test_sanity(self): - self.assertEqual(self.s.resolve_field('Task', 'sg_status_list'), ['sg_status_list']) - self.assertEqual(self.s.resolve_field('Task', 'status_list'), ['sg_status_list']) - self.assertEqual(self.s.resolve_field('Task', '$status'), ['sg_status_list']) - - def test_passthrough(self): - self.assertEqual(self.s.resolve_field('Task', 'entity.Shot.sg_status_list'), ['entity.Shot.sg_status_list']) - - def test_explicit_aliases(self): - self.assertEqual(self.s.resolve_field('Task', '$parent.Shot.$status'), ['entity.Shot.sg_status_list']) - self.assertEqual(self.s.resolve_field('Task', '$parent.Shot.status_list'), ['entity.Shot.sg_status_list']) - self.assertEqual(self.s.resolve_field('Task', 'entity.Shot.$status'), ['entity.Shot.sg_status_list']) - - def test_explicit_tags(self): - self.assertEqual(self.s.resolve_field('Task', '$parent.Shot.#core'), ['entity.Shot.sg_sequence', 'entity.Shot.code', 'entity.Shot.description']) - - - diff --git a/tests/test_structures.py b/tests/test_structures.py new file mode 100644 index 0000000..7bb4124 --- /dev/null +++ b/tests/test_structures.py @@ -0,0 +1,66 @@ +from . import * + + +class TestResolveStructures(TestCase): + + def setUp(self): + + self.s = s = Schema() + s.load({ + 'entities': { + 'Entity': { + 'fields': { + 'attr': { + 'aliases': ['a', 'with:namespace'], + 'tags': ['x'], + + }, + 'sg_version': {}, + 'sg_type': {}, + 'name': {}, + 'sg_name': {}, + }, + 'field_aliases': { + 'b': 'attr', + }, + 'field_tags': { + 'y': ['attr'], + 'multi': ['multi_a', 'multi_b'] + } + } + }, + }) + + def test_single_entity(self): + self.assertEqual(self.s.resolve_structure({ + 'type': 'Entity', + 'version': 1 + }), { + 'type': 'Entity', + 'sg_version': 1, + }) + + def test_entity_list(self): + self.assertEqual(self.s.resolve_structure([ + { + 'type': 'Entity', + 'version': 1 + }, + { + 'type': 'Entity', + '$b': 'attr_value', + '#multi': 'xxx', + } + ]), [ + { + 'type': 'Entity', + 'sg_version': 1, + }, + { + 'type': 'Entity', + 'attr': 'attr_value', + 'multi_a': 'xxx', + 'multi_b': 'xxx', + } + ]) +