Package gavo :: Package votable :: Module paramval
[frames] | no frames]

Source Code for Module gavo.votable.paramval

  1  """ 
  2  Serialisation of python values to VOTable PARAM values. 
  3   
  4  This has two aspects: 
  5   
  6  - Guessing proper VOTable type descriptors for python values 
  7    (use guessParamAttrsForValue) 
  8  - Serialising the python values to strings suitable for the PARAM. 
  9    (use serializeToParam) 
 10  """ 
 11   
 12  #c Copyright 2008-2019, the GAVO project 
 13  #c 
 14  #c This program is free software, covered by the GNU GPL.  See the 
 15  #c COPYING file in the source distribution. 
 16   
 17   
 18  import datetime 
 19   
 20  from gavo import utils 
 21  from gavo.utils import pgsphere 
 22  from gavo.utils import serializers 
 23  from gavo.votable import coding 
 24  from gavo.votable import common 
 25  from gavo.votable import enc_tabledata 
 26  from gavo.votable import dec_tabledata 
 27  from gavo.votable.model import VOTable as V 
 28   
 29   
 30  _SEQUENCE_TYPES = (tuple, list) 
 31  _ATOMIC_TYPES = [ 
 32          (long, {"datatype": "long"}), 
 33          (int, {"datatype": "int"}), 
 34          (str, {"datatype": "char", "arraysize": "*"}), 
 35          (basestring, {"datatype": "unicodeChar", "arraysize": "*"}), 
 36          (float, {"datatype": "double"}), 
 37          (type(None), {"datatype": "double"}), 
 38          (complex, {"datatype": "doubleComplex"}), 
 39          (datetime.datetime, {"datatype": "char",  
 40                  "arraysize": "20", 
 41                  "xtype": "timestamp"}), 
 42          (datetime.date, {"datatype": "char",  
 43                  "arraysize": "20", 
 44                  "xtype": "dachs:DATE"}), 
 45          (pgsphere.SPoint, {"datatype": "double precision",  
 46                  "arraysize": "2", 
 47                  "xtype": "point"}), 
 48          (pgsphere.SCircle, {"datatype": "double precision",  
 49                  "arraysize": "3", 
 50                  "xtype": "circle"}), 
 51          (pgsphere.SPoly, {"datatype": "double precision",  
 52                  "arraysize": "*", 
 53                  "xtype": "polygon"}),] 
54 55 56 -def _combineArraysize(arraysize, attrs):
57 """makes an arraysize attribute for a value with attrs. 58 59 This will in particular check that any existing arraysize in 60 attrs does not end with a star (as variable length is only allowed 61 in the slowest coordinate). 62 63 attrs is changed in place. 64 """ 65 if "arraysize" in attrs: 66 if attrs["arraysize"].endswith("*"): 67 raise ValueError("Arrays of variable-length arrays are not allowed.") 68 attrs["arraysize"] = "%sx%s"%(attrs["arraysize"], arraysize) 69 else: 70 attrs["arraysize"] = arraysize
71
72 73 -def _guessParamAttrsForSequence(pythonVal):
74 """helps guessParamAttrsForValue when the value is a sequence. 75 """ 76 arraysize = str(len(pythonVal)) 77 if len(pythonVal)==0: 78 return { 79 "datatype": "char", 80 "arraysize": "0"} 81 82 elementVal = pythonVal[0] 83 84 if isinstance(elementVal, basestring): 85 # special case as this may become common 86 attrs = { 87 "arraysize": "%sx%s"%( 88 max(len(s) for s in pythonVal), arraysize), 89 "datatype": "char"} 90 91 elif isinstance(elementVal, _SEQUENCE_TYPES): 92 attrs = _guessParamAttrsForSequence(elementVal) 93 _combineArraysize(arraysize, attrs) 94 95 else: 96 attrs = _guessParamAttrsForAtom(elementVal) 97 _combineArraysize(arraysize, attrs) 98 99 return attrs
100
101 102 -def _guessParamAttrsForAtom(pythonVal):
103 """helps guessParamAttrsForValue when the value is atomic. 104 105 (where "atomic" includes string, and other things that actually 106 have non-1 arraysize). 107 """ 108 for type, attrs in _ATOMIC_TYPES: 109 if isinstance(pythonVal, type): 110 return attrs.copy() 111 112 raise utils.NotFoundError(repr(pythonVal), 113 "VOTable type code for", "paramval.py predefined types")
114
115 116 -def guessParamAttrsForValue(pythonVal):
117 """returns a dict of proposed attributes for a PARAM to keep pythonVal. 118 119 There is, of course, quite a bit of heuristics involved. For instance, 120 we assume sequences are homogeneous. 121 """ 122 if isinstance(pythonVal, _SEQUENCE_TYPES): 123 return _guessParamAttrsForSequence(pythonVal) 124 125 else: 126 return _guessParamAttrsForAtom(pythonVal)
127
128 129 -def _setNULLValue(param, val):
130 """sets the null literal of param to val. 131 """ 132 valEls = list(param.iterChildrenWithName("VALUES")) 133 if valEls: 134 valEls[0](null=val) 135 else: 136 param[V.VALUES(null=val)]
137
138 139 -def _serializeNULL(param):
140 """changes the VOTable PARAM param so it evaluates to NULL. 141 """ 142 if param.datatype in ["float", "double"]: 143 element = "NaN " 144 elif param.datatype in ["unsignedByte", "short", "int", "long"]: 145 element = "99 " 146 _setNULLValue(param, element) 147 elif param.datatype in ["char", "unicodeChar"]: 148 element = "x" 149 _setNULLValue(param, element) 150 else: 151 raise ValueError("No recipe for %s null values"%param.datatype) 152 153 if param.isScalar(): 154 param.value = element.strip() 155 elif param.hasVarLength(): 156 param.value = "" 157 else: 158 param.value = (element*param.getLength()).strip()
159
160 161 -class PrimitiveAnnotatedColumn(dict):
162 """A stand-in for serializers.AnnotatedColumn. 163 164 We don't want to use the full thing as it's too fat here, and 165 getVOTSerializer doesn't have the original param anyway (as 166 it shouldn't, as that would break memoization). 167 """ 168
169 - class original(object):
170 stc = None 171 xtype = None
172
173 - def __init__(self, datatype, arraysize, xtype):
174 dict.__init__(self, { 175 "nullvalue": "", 176 "name": "anonymous", 177 "dbtype": None, 178 "displayHint": {}, 179 "note": None, 180 "ucd": None, 181 "utype": None, 182 "unit": None, 183 "description": None, 184 "id": None, 185 "datatype": datatype, 186 "arraysize": arraysize, 187 "xtype": xtype})
188
189 190 @utils.memoized 191 -def getVOTSerializer(datatype, arraysize, xtype):
192 """returns a function serializing for values of params with the 193 attributes given. 194 """ 195 lines = "\n".join([ 196 "def codec(val):"]+ 197 common.indentList([ 198 "val = mapper(val)", 199 "tokens = []"]+ 200 enc_tabledata.getLinesFor(V.PARAM(**locals()))+[ 201 "return tokens[0]"], " ")) 202 203 mapper = serializers.defaultMFRegistry.getMapper(PrimitiveAnnotatedColumn( 204 datatype, arraysize, xtype)) 205 env = enc_tabledata.getGlobals(None).copy() 206 env["mapper"] = mapper 207 208 return coding.buildCodec(lines, env)
209
210 211 -def serializeToParam(param, val):
212 """changes the VOTable PARAM param such that val is represented. 213 214 This may involve adding a null value. 215 """ 216 if val is None: 217 _serializeNULL(param) 218 else: 219 param.value = getVOTSerializer( 220 param.datatype, param.arraysize, param.xtype)(val)
221
222 223 @utils.memoized 224 -def getVOTParser(datatype, arraysize, xtype):
225 """returns a function deserializing values in a param with datatype, 226 arraysize, and xtype. 227 """ 228 p = V.PARAM(name="anonymous", datatype=datatype, arraysize=arraysize, 229 xtype=xtype) 230 231 lines = "\n".join([ 232 "def codec(val):"] 233 +common.indentList([ 234 "row = []"] 235 +dec_tabledata.getLinesFor(p) 236 +[ 237 "return row[0]"], " ")) 238 239 return coding.buildCodec(lines, dec_tabledata.getGlobals(None))
240