Package gavo :: Package adql :: Module annotations
[frames] | no frames]

Source Code for Module gavo.adql.annotations

  1  """ 
  2  Adding field infos to columns and other objects in an ADQL parse tree. 
  3   
  4  When we want to generate VOTables from ADQL queries, we must know types, 
  5  units, ucds, and the like, and we need to know STC information for 
  6  all columns in a query. 
  7   
  8  To do that, we traverse the parse tree postorder looking for nodes that have 
  9  an addFieldInfos method (note the plural).  These then get called, 
 10  which causes one of the classes in adql.fieldinfos to be constructed 
 11  and assigned to the node's fieldInfos attribute.  The source for 
 12  these infos is either an AnnotationContext (and thus typically 
 13  the user-supplied retrieveFieldInfos function) or derived annotations. 
 14  These are computed by the nodes themselves, using their addFieldInfo  
 15  (singular!) method. 
 16  """ 
 17   
 18  #c Copyright 2008-2019, the GAVO project 
 19  #c 
 20  #c This program is free software, covered by the GNU GPL.  See the 
 21  #c COPYING file in the source distribution. 
 22   
 23   
 24  import contextlib 
 25   
 26  from gavo import utils 
 27  from gavo import stc 
28 29 30 31 -class AnnotationContext(object):
32 """An context object for the annotation process. 33 34 It is constructed with a FieldInfoGetter implementation 35 and an equivalence policy for STC objects. 36 37 It has errors and warnings attributes consisting of user-exposable 38 error strings accrued during the annotation process. 39 40 The annotation context also manages the namespaces for column reference 41 resolution. It maintains a stack of getters; is is maintained 42 using the customResolver context manager. 43 44 withTables, if passed, must be a sequence of already annotated 45 WithQuery objects. 46 47 Finally, the annotation context provides a ancestors attribute that, 48 at any time, gives a list of the current node's ancestor nodes. 49 """
50 - def __init__(self, 51 retrieveFieldInfos, 52 equivalencePolicy=stc.defaultPolicy):
53 self.retrieveFieldInfos = retrieveFieldInfos 54 self.policy = equivalencePolicy 55 self.withTables = {} 56 self.colResolvers = [] 57 self.errors, self.warnings = [], [] 58 self.ancestors = []
59 60 @contextlib.contextmanager
61 - def customResolver(self, getter):
62 """a context manager temporarily installing a different field info 63 getter. 64 """ 65 self.colResolvers.append(getter) 66 try: 67 yield 68 finally: 69 self.colResolvers.pop()
70
71 - def addWithTable(self, withTable):
72 """adds a nodes.WithQuery to use in column resolution. 73 74 withTable must already be annotated for this to work. 75 """ 76 self.retrieveFieldInfos.addExtraFieldInfos( 77 withTable.name, 78 withTable.fieldInfos.seq)
79
80 - def getFieldInfo(self, colName, tableName):
81 """returns the (colName, fieldInfo) for colName within tableName 82 in the current context. 83 """ 84 res = self.colResolvers[-1](colName, tableName) 85 if res is None: 86 raise utils.ReportableError("Internal Error: resolver returned NULL for" 87 " %s.%s. Please report this to the gavo@ari.uni-heidelberg.de" 88 " together with the failed query."%(tableName, colName)) 89 return res
90
91 92 -def _annotateTraverse(node, context):
93 """does the real tree traversal for annotate. 94 """ 95 context.ancestors.append(node) 96 for c in node.iterNodeChildren(): 97 _annotateTraverse(c, context) 98 context.ancestors.pop() 99 if hasattr(node, "addFieldInfos"): 100 node.addFieldInfos(context)
101
102 103 -def annotate(node, context):
104 """adds annotations to all nodes wanting some. 105 106 This is done by a postorder traversal of the tree, identifying all 107 annotable objects. 108 109 context can be an AnnotationContext instance. You can also just 110 pass in a adql.FieldInfoGetter instance. In that case, annotation 111 runs with the default stc equivalence policy. 112 113 The function returns the context used in any case. 114 """ 115 if not isinstance(context, AnnotationContext): 116 context = AnnotationContext(context) 117 118 for wt in node.withTables: 119 _annotateTraverse(wt, context) 120 context.addWithTable(wt) 121 122 _annotateTraverse(node, context) 123 return context
124
125 126 -def dumpFieldInfoedTree(tree):
127 """dumps an ADQL parse tree, giving the computed annotations. 128 129 For debugging. 130 """ 131 import pprint 132 def traverse(node): 133 res = [] 134 if hasattr(node, "fieldInfo"): 135 res.append("%s <- %s"%(node.type, repr(node.fieldInfo))) 136 if hasattr(node, "fieldInfos"): 137 res.append("%s -- %s"%(node.type, repr(node.fieldInfos))) 138 res.extend(filter(None, [traverse(child) for child in 139 node.iterNodeChildren()])) 140 if len(res)==1: 141 return res[0] 142 else: 143 return res
144 pprint.pprint(traverse(tree)) 145