Added the ability to parse specdata XML files to create Schemas.
[~jspiros/python-ebml.git] / ebml / schema / specs.py
1 from xml.etree.ElementTree import parse as parse_xml
2 from .base import INT, UINT, FLOAT, STRING, UNICODE, DATE, BINARY, CONTAINER, Element, Schema
3
4
5 SPECDATA_TYPES = {
6         'integer': INT,
7         'uinteger': UINT,
8         'float': FLOAT,
9         'string': STRING,
10         'utf-8': UNICODE,
11         'date': DATE,
12         'binary': BINARY,
13         'master': CONTAINER
14 }
15
16
17 def parse_specdata(source, schema_name):
18         """
19         
20         Reads a schema specification from a file (e.g., specdata.xml) or file-like object, and returns a tuple containing:
21         
22                 * a mapping of class names to Element subclasses
23                 * a Schema subclass
24         
25         :arg source: the file or file-like object
26         :type source: str or file-like object
27         :arg schema_name: the name of the schema
28         :type schema_name: str
29         :returns: tuple
30         
31         """
32         
33         tree = parse_xml(source)
34         elements = {}
35         parent_elements = []
36         
37         for element_element in tree.getiterator('element'):
38                 raw_attrs = element_element.attrib
39                 
40                 element_name = '%sElement' % raw_attrs.get('cppname', raw_attrs.get('name'))
41                 element_level = int(raw_attrs['level'])
42                 element_attrs = {
43                         '__module__': None,
44                         'class_id': int(raw_attrs['id'], 0),
45                         'class_name': raw_attrs['name'],
46                         'data_type': SPECDATA_TYPES[raw_attrs['type']]
47                 }
48                 
49                 while parent_elements and element_level <= parent_elements[-1][0]:
50                         parent_elements.pop()
51                 
52                 if element_level == -1:
53                         element_attrs['class_global'] = True
54                         parent_elements = []
55                 elif element_level == 0:
56                         element_attrs['class_root'] = True
57                         parent_elements = []
58                 else:
59                         if raw_attrs.get('recursive', '0') == '1':
60                                 element_attrs['class_parents'] = (parent_elements[-1][1], 'self')
61                         else:
62                                 element_attrs['class_parents'] = (parent_elements[-1][1],)
63                 
64                 element = type(element_name, (Element,), element_attrs)
65                 elements[element_name] = element
66                 parent_elements.append((element_level, element))
67         
68         schema_attrs = {
69                 '__module__': None,
70                 'elements': tuple(elements.values())
71         }
72         schema = type(schema_name, (Schema,), schema_attrs)
73         
74         return elements, schema