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

Source Code for Module gavo.votable.simple

  1  """ 
  2  A simplified API to single-table VOTables. 
  3   
  4  The basic idea is: open(...) -> (table, metadata), 
  5   
  6  where table is a numpy array. 
  7  """ 
  8   
  9  #c Copyright 2008-2019, the GAVO project 
 10  #c 
 11  #c This program is free software, covered by the GNU GPL.  See the 
 12  #c COPYING file in the source distribution. 
 13   
 14   
 15  from cStringIO import StringIO 
 16   
 17  try: 
 18          import numpy 
 19   
 20          # map from VOTable datatypes to numpy type designators: 
 21          numpyType = { 
 22                  "short": numpy.int16, 
 23                  "int": numpy.int32, 
 24                  "long": numpy.int64, 
 25                  "float": numpy.float32, 
 26                  "double": numpy.float64, 
 27                  "boolean": numpy.bool, 
 28                  "char": numpy.str_, 
 29                  "floatComplex": numpy.complex64, 
 30                  "doubleComplex": numpy.complex128, 
 31                  "unsignedByte": numpy.uint8, 
 32                  "unicodeChar": numpy.unicode_ 
 33          } 
 34  except ImportError: 
 35          # keep numpy optional 
 36          pass 
 37   
 38  from gavo.votable import votparse 
 39  from gavo.votable import tablewriter 
 40  from gavo.votable.model import VOTable as V 
 41   
 42   
 43   
44 -class TableMetadata(object):
45 """Metadata for a VOTable table instance, i.e., column definitions, groups, 46 etc. 47 48 These are constructed with a VOTable TABLE instance and infos, a dictionary 49 mapping info names to lists of V.INFO items. 50 """
51 - def __init__(self, tableElement, infos):
52 self.votTable = tableElement 53 self.infos = infos 54 self.fields = list(tableElement.iterChildrenOfType(V.FIELD))
55
56 - def __iter__(self):
57 return iter(self.fields)
58
59 - def __len__(self):
60 return len(self.fields)
61
62 - def __getitem__(self, index):
63 return self.fields[index]
64
65 - def getFields(self):
66 return self.votTable.getFields()
67
68 - def iterDicts(self, data):
69 """iterates over data, but returns each row as a dict. 70 71 data is a result set as returned by load. 72 """ 73 names = [f.name for f in self] 74 for row in data: 75 yield dict(zip(names, row))
76 77
78 -def makeDtype(tableMetadata, defaultStringLength=20):
79 """returns an record array datatype for a given table metadata. 80 81 defaultStringLength lets you specify a length for char(*) fields; 82 since makeDtype has no access to the tabular data and numpy insists on 83 having string length, DaCHS needs to guess here. 84 85 If this isn't fine-grained enough for you, you can always path tableMetadata, 86 replacing * arraysizes with more appropriate values in individual 87 cases. 88 """ 89 dtypes = [] 90 seen = set() 91 for f in tableMetadata: 92 name = f.getDesignation().encode('ascii', 'ignore') 93 while name in seen: 94 name = name+"_" 95 seen.add(name) 96 shape = f.getShape() 97 if shape is None: 98 99 # Perhaps stupidly, the library interprets 1D char array as 100 # atoms; numpy doesn't, so we have a special case. 101 if f.datatype=="char": 102 # as far as numpy recarray are concerned; TODO: unicodeChar? 103 if f.arraysize=="*": 104 dtypes.append((name, "a", defaultStringLength)) 105 elif f.arraysize is None: 106 dtypes.append((name, "a", 1)) 107 else: 108 # anything weird leads to non-NULL shape 109 dtypes.append((name, "a", int(f.arraysize))) 110 111 else: 112 dtypes.append((name, numpyType[f.datatype])) 113 114 else: 115 dtypes.append(( 116 name, 117 numpyType[f.datatype], 118 shape)) 119 return dtypes
120 121
122 -def load(source, raiseOnInvalid=True):
123 """returns (data, metadata) from the first table of a VOTable. 124 125 data is a list of records (as a list), metadata a TableMetadata instance. 126 127 source can be a string that is then interpreted as a local file name, 128 or it can be a file-like object. 129 """ 130 if isinstance(source, basestring): 131 source = file(source) 132 infos = {} 133 134 # the following loop is a bit weird since we want to catch info items 135 # after the table and thus only exit the loop when the next table starts 136 # or the iterator is exhausted. 137 rows = None 138 for element in votparse.parse(source, [V.INFO], raiseOnInvalid): 139 if isinstance(element, V.INFO): 140 infos.setdefault(element.name, []).append(element) 141 else: 142 if rows is not None: 143 break 144 fields = TableMetadata(element.tableDefinition, infos) 145 rows = list(element) 146 if rows is None: # No table included 147 return None, None 148 return rows, fields
149 150
151 -def loads(stuff, raiseOnInvalid=True):
152 """returns data,metadata for a VOTable literal in stuff. 153 """ 154 return load(StringIO(stuff), raiseOnInvalid)
155 156
157 -def save(data, tableDef, destF):
158 """saves (data, tableDef) in VOTable format to destF. 159 160 data is a sequence of tuples, tableDef V.TABLE instance as, for example, 161 obtainable from metadata.votTable as returned by load. data must contain 162 type-right python values that match the table definition. 163 164 A load-save cycle loses all top-level and resource level metadata in the 165 simplified interface. Use the full interface if that hurts you. 166 """ 167 root = V.VOTABLE[ 168 V.RESOURCE[ 169 tablewriter.DelayedTable(tableDef, data, V.BINARY)]] 170 tablewriter.write(root, destF)
171