1 """
2 Common code for new-style Data Model support.
3
4 In particular, this defines a hierachy of Annotation objects. The annotation
5 of DaCHS tables is an ObjectAnnotation, the other Annotation classes
6 (conceptually, all are key-value pairs) make up their inner structure.
7 """
8
9
10
11
12
13
14
15 import contextlib
16 import re
17 import weakref
18
19 from gavo import utils
20 from gavo import votable
21 from gavo.votable import V
22
23
24 VODML_NAME = "vo-dml"
29 """a context manager to control the type currently serialised in a VOTable.
30
31 ctx is a VOTable serialisation context (that we liberally hack into).
32 """
33 if not hasattr(ctx, "_dml_typestack"):
34 ctx._dml_typestack = []
35 ctx._dml_typestack.append(typeName)
36 try:
37 yield
38 finally:
39 ctx._dml_typestack.pop()
40
43 """completes roleName to a full (standard) vo-dml id.
44
45 This is based on what the containerTypeSet context manager leaves
46 in the VOTable serialisation context ctx.
47 """
48 return roleName
49
50
51 if ":" in roleName:
52
53 return roleName
54 return "%s.%s"%(ctx._dml_typestack[-1], roleName)
55
58 """returns modelname, package (None for the empty package), name
59 for a VO-DML type name.
60
61 Malformed names raise a ValueError.
62
63 >>> parseTypeName("dm:type")
64 ('dm', None, 'type')
65 >>> parseTypeName("dm:pck.type")
66 ('dm', 'pck', 'type')
67 >>> parseTypeName(":malformed.typeid")
68 Traceback (most recent call last):
69 ValueError: ':malformed.typeid' is not a valid VO-DML type name
70 """
71 mat = re.match("([\w_-]+):(?:([\w_-]+)\.)?([\w_-]+)", typename)
72 if not mat:
73 raise ValueError("'%s' is not a valid VO-DML type name"%typename)
74 return mat.groups()
75
78 """A base class for of structs.
79
80 Basically, these are pairs of a role name and something else, which
81 depends on the actual subclass (e.g., an atomic value, a reference,
82 a sequence of key-value pairs, a sequence of other objects, ...).
83
84 They have a method getVOT(ctx, instance) -> xmlstan, which, using a
85 votablewrite.Context ctx, will return mapping-document conformant VOTable
86 xmlstan; instance is the rsc/rscdef structure the annotation is produced
87 for.
88
89 Use asSIL() to retrieve a simple string representation.
90
91 Compund annotations (sequences, key-value pairs) should use
92 add(thing) to build themselves up.
93
94 AnnotationBase is abstract and doesn't implement some of these methods.
95 """
97 self.name = name
98 if instance is None:
99 self.instance = instance
100 else:
101 self.instance = weakref.ref(instance)
102
103 - def getVOT(self, ctx, instance):
106
110
111 - def add(self, thing):
112 raise ValueError(
113 "%s is not a compound annotation."%self.__class__.__name__)
114
117
120 """A base class for annotations that must be adapted or discarded when
121 an annotation is copied.
122 """
123
128 """An annotation of an atomic value, i.e., a key-value pair.
129
130 These can take optional metadata.
131 """
132 - def __init__(self, name=None, value=None, unit=None,
133 ucd=None, instance=None):
136
137 - def copy(self, newInstance):
138 return self.__class__(
139 self.name, self.value, self.unit, self.ucd,
140 newInstance)
141
142 - def getVOT(self, ctx, instance):
164
165
166 - def asSIL(self, suppressType=False):
171
174 """A mixin furnishing a class with a copyWithAnnotationMap method.
175
176 The class mixing this in must provide an iterator iterChildRoles
177 yielding child annotations one by one.
178
179 Every compound annotation must mix this in in order to provide
180 halfway sane copying semantics when columns get re-mixed in new
181 tables (which we do all the time).
182
183 We also expect a method copyEmpty(i) that returns an instance of the
184 Annotation but without any child annotations.
185
186 In return, this will furnish a copy(i) method based on copyEmpty
187 and iterChildRoles.
188 """
190 """returns a copy of this annotation, with annotations mentioned
191 in annotationMap replaced.
192
193 Table-related annotations (currently, Param- and ColumnAnnotations)
194 not mentioned in annotationMap) will be discarded.
195
196 This is used when annotation tables with columns copied from
197 other tables. annotationMap, normally generated by
198 dmrd.SynthesizedRoles, then maps the old annotations to the elements
199 that should be annotated in the new table.
200 """
201 copy = self.copyEmpty(instance)
202 if instance is None:
203 instance = copy
204
205 for role in self.iterChildRoles():
206
207 if role in annotationMap:
208 copy.add(
209 annotationMap[role].getAnnotation(
210 role.name, container, instance))
211
212 elif isinstance(role, TableRelativeAnnotation):
213 pass
214
215 elif hasattr(role, "copyWithAnnotationMap"):
216 copy.add(role.copyWithAnnotationMap(
217 annotationMap, container, instance))
218
219 else:
220 copy.add(role.copy(instance))
221
222 return copy
223
224 - def copy(self, newInstance):
225 return self.copyWithAnnotationMap({}, None, newInstance)
226
229 """An internal base class for DatatypeAnnotation and ObjectAnnotation.
230 """
231 - def __init__(self, name, type, instance):
241
248
249 - def get(self, key, default=None):
253
255 return key in self.childRoles
256
258 return self.__class__(self.name, self.type, newInstance)
259
261 return self.childRoles.itervalues()
262
263 - def add(self, role):
266
267 - def asSIL(self, suppressType=False):
268 if suppressType or self.type is None:
269 typeAnn = ""
270 else:
271 typeAnn = "(%s) "%self.type
272
273 return "%s{\n %s}\n"%(typeAnn,
274 "\n ".join(r.asSIL() for r in self.childRoles.values()))
275
276 - def getVOT(self, ctx, instance):
290
293 """An annotation for a datatype.
294
295 Datatypes are essentially simple groups of attributes; they are used
296 *within* objects (e.g., to group photometry points, or positions, or
297 the like.
298 """
299
302 """An annotation for an object.
303
304 Objects are used for actual DM instances. In particular,
305 every annotation of a DaCHS table is rooted in an object.
306 """
307
310 """A collection contains 0..n things of the same type.
311 """
312 - def __init__(self, name, type, instance):
319
326
328 return len(self.children)
329
331 return iter(self.children)
332
334 return self.__class__(self.name, self.type, newInstance)
335
336 - def add(self, child):
338
340 if self.type is None:
341 opener = "["
342 else:
343 opener = "(%s) ["%(self.type,)
344
345 bodyItems = []
346 for r in self.children:
347 bodyItems.append(r.asSIL(suppressType="True"))
348
349 return "%s: \n %s%s]\n"%(
350 self.name,
351 opener,
352 "\n ".join(bodyItems))
353
354 - def getVOT(self, ctx, instance):
355
356
357
358
359
360 if self.type:
361 ctx.addVODMLPrefix(self.modelPrefix)
362 return [c.getVOT(ctx, instance) for c in self.children]
363
368
369
370 if __name__=="__main__":
371 _test()
372