Module AIPSData
[hide private]
[frames] | no frames]

Source Code for Module AIPSData

  1  # Copyright (C) 2005, 2006 Joint Institute for VLBI in Europe 
  2  # 
  3  # This program is free software; you can redistribute it and/or modify 
  4  # it under the terms of the GNU General Public License as published by 
  5  # the Free Software Foundation; either version 2 of the License, or 
  6  # (at your option) any later version. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, 
  9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  # GNU General Public License for more details. 
 12   
 13  # You should have received a copy of the GNU General Public License 
 14  # along with this program; if not, write to the Free Software 
 15  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 16   
 17  """ 
 18   
 19  This module provides the AIPSImage and AIPSUVData classes.  These 
 20  classes implement most of the data oriented verb-like functionality 
 21  from classic AIPS. 
 22   
 23  Images can be manipulated by creating instances of the AIPSImage class: 
 24   
 25  >>> image = AIPSImage('NONAME', 'IMAGE', 1, 1) 
 26   
 27  For UV data, the AIPSUVData class should be used: 
 28   
 29  >>> uvdata = AIPSUVData('NONAME', 'UVDATA', 1, 1) 
 30   
 31  Checking whether the image or UV data actually exists is easy: 
 32   
 33  >>> image.exists() 
 34  False 
 35  >>> uvdata.exists() 
 36  False 
 37   
 38  >>> print uvdata 
 39  AIPSUVData('NONAME', 'UVDATA', 1, 1) 
 40   
 41  Checking whether two instance refer to the same data is fairly simple: 
 42   
 43  >>> image == uvdata 
 44  False 
 45   
 46  >>> image == AIPSImage('NONAME', 'IMAGE', 1, 1) 
 47  True 
 48   
 49  Both classes implement the copy method: 
 50   
 51  >>> uvjunk = uvdata.copy() 
 52  >>> uvjunk == uvdata 
 53  True 
 54  >>> uvjunk.name = 'GARBAGE' 
 55  >>> uvjunk != uvdata 
 56  True 
 57   
 58  """ 
 59   
 60  # Global AIPS defaults. 
 61  import AIPS 
 62   
 63  # Generic Python stuff. 
 64  import sys 
 65   
 66  # This code is way too clever.  Instead of implementing each and every 
 67  # function call provided by a proxy, class _Method implements a 
 68  # callable object that invokes a named method of a proxy, passing a 
 69  # description of the AIPS data it should operate on as the first 
 70  # argument.  To make matters worse, the same implementation is used 
 71  # for class AIPSImage and class AIPSData and dispatching is done based 
 72  # on the name of the class.  This way adding a function to the proxy 
 73  # automatically makes it callable as a method of both AIPSImage and 
 74  # AIPSUVData. 
 75   
76 -def _whoami():
77 """Return the name of the function that called us.""" 78 return sys._getframe(1).f_code.co_name
79 80
81 -class _AIPSDataMethod:
82 83 """This class implements dispatching function calls to a proxy.""" 84
85 - def __init__(self, inst, name):
86 self.inst = inst 87 self.name = name
88
89 - def __call__(self, *args):
90 func = self.inst._method(self.name) 91 return func(self.inst.desc, *args)
92 93
94 -class _AIPSDataDesc:
95 96 """This class implements the description of AIPS data that is used 97 when dispatching function calls to a proxy.""" 98
99 - def __init__(self, name, klass, disk, seq, userno):
100 self.name = name 101 self.klass = klass 102 self.disk = disk 103 self.seq = seq 104 self.userno = userno 105 return
106 107 # Provide a dictionary-like interface to deal with the 108 # idiosyncrasies of XML-RPC.
109 - def __getitem__(self, key):
110 return self.__dict__[key]
111 112 pass # class _AIPSDataDesc
113 114
115 -class _dictify(dict):
116
117 - def __getattr__(self, item):
118 return self[item]
119 120 pass # class _dictify
121 122
123 -class _AIPSDataHeader(_dictify):
124 125 """This class describes the header of an AIPS image or UV data set.""" 126 127 pass # class _AIPSDataHeader
128 129
130 -class _AIPSData(object):
131 132 """This class describes generic AIPS data.""" 133
134 - def __init__(self, *args):
135 # Instances can be created by specifying name, class, disk, 136 # sequence number and (optionally) user number explicitly, or 137 # by passing an object that has the appropriate attributes. 138 # This allows the creation of a non-Wizardry object from its 139 # Wizardry counterpart. 140 141 if len(args) not in [1, 4, 5]: 142 msg = "__init__() takes 2, 5 or 6 arguments (%d given)" \ 143 % (len(args) + 1) 144 raise TypeError, msg 145 146 if len(args) == 1: 147 name = args[0].name 148 klass = args[0].klass 149 disk = args[0].disk 150 seq = args[0].seq 151 userno = args[0].userno 152 else: 153 name = args[0] 154 klass = args[1] 155 disk = args[2] 156 seq = args[3] 157 userno = -1 158 if len(args) == 5: 159 userno = args[4] 160 pass 161 pass 162 163 if userno == -1: 164 userno = AIPS.userno 165 self._disk = disk 166 disk = AIPS.disks[disk] 167 self.desc = _AIPSDataDesc(name, klass, disk.disk, seq, userno) 168 self.proxy = disk.proxy() 169 return
170
171 - def _set_name(self, name): self.desc.name = name; pass
172 name = property(lambda self: self.desc.name, _set_name, 173 doc='Name of this data set.')
174 - def _set_klass(self, klass): self.desc.klass = klass; pass
175 klass = property(lambda self: self.desc.klass, _set_klass, 176 doc='Class of this data set.')
177 - def _set_disk(self, disk):
178 self._disk = disk 179 disk = AIPS.disks[disk] 180 self.desc.disk = disk.disk 181 self.proxy = disk.proxy() 182 pass
183 disk = property(lambda self: self._disk, _set_disk, 184 doc='Disk where this data set is stored.')
185 - def _set_seq(self, seq): self.desc.seq = seq; pass
186 seq = property(lambda self: self.desc.seq, _set_seq, 187 doc='Sequence number of this data set.')
188 - def _set_userno(self, userno): self.desc.userno = userno; pass
189 userno = property(lambda self: self.desc.userno, _set_userno, 190 doc='User number used to access this data set.') 191
192 - def __repr__(self):
193 repr = "%s('%s', '%s', %d, %d)" % \ 194 (self.__class__.__name__, 195 self.name, self.klass, self.disk, self.seq) 196 return repr
197
198 - def __eq__(self, other):
199 if self.__class__ != other.__class__: 200 return False 201 if self.desc.name != other.desc.name: 202 return False 203 if self.desc.klass != other.desc.klass: 204 return False 205 if self.desc.disk != other.desc.disk: 206 return False 207 if self.desc.seq != other.desc.seq: 208 return False 209 if self.desc.userno != other.desc.userno: 210 return False 211 return True
212
213 - def __str__(self):
214 return self.__repr__()
215
216 - def __getattr__(self, name):
217 if name in self.desc.__dict__: 218 return self.desc.__dict__[name] 219 return _AIPSDataMethod(self, name)
220
221 - def __len__(self):
222 return _AIPSDataMethod(self, '_len')()
223
224 - def copy(self):
225 return self.__class__(self.name, self.klass, self.disk, self.seq, 226 self.userno)
227
228 - def table(self, type, version):
229 return _AIPSTable(self, type, version)
230
231 - def _method(self, name):
232 return getattr(getattr(self.proxy, self.__class__.__name__), name)
233
234 - def exists(self):
235 """Check whether this image or data set exists. 236 237 Returns True if the image or data set exists, False otherwise.""" 238 return self._method(_whoami())(self.desc)
239
240 - def verify(self):
241 """Verify whether this image or data set can be accessed.""" 242 return self._method(_whoami())(self.desc)
243
244 - def _generate_header(self):
245 dict = self._method('header')(self.desc) 246 return _AIPSDataHeader(dict)
247 header = property(_generate_header, 248 doc='Header for this data set.') 249
250 - def _generate_keywords(self):
251 return _AIPSDataMethod(self, 'keywords')()
252 keywords = property(_generate_keywords, 253 doc='Keywords for this data set.') 254
255 - def _generate_tables(self):
256 dict = self._method('tables')(self.desc) 257 return dict
258 tables = property(_generate_tables, 259 doc='Extension tables for this data set.') 260 261 history = property(lambda self: _AIPSHistory(self), 262 doc='History table for this data set.') 263
264 - def table_highver(self, type):
265 """Get the highest version of an extension table. 266 267 Returns the highest available version number of the extension 268 table TYPE.""" 269 return self._method(_whoami())(self.desc, type)
270
271 - def rename(self, name=None, klass=None, seq=None, **kwds):
272 """Rename this image or data set. 273 274 NAME is the new name, KLASS is the new class and SEQ is the 275 new sequence number for the data set. Note that you can't 276 change the disk number, since that would require copying the 277 data.""" 278 if name == None: name = self.name 279 if klass == None: klass = self.klass 280 if seq == None: seq = self.seq 281 if 'name' in kwds: name = kwds['name'] 282 if 'klass' in kwds: klass = kwds['name'] 283 if 'seq' in kwds: seq = kwds['seq'] 284 result = self._method(_whoami())(self.desc, name, klass, seq) 285 self.name = result[0] 286 self.klass = result[1] 287 self.seq = result[2] 288 return result
289
290 - def zap(self, force=False):
291 """Destroy this image or data set.""" 292 return self._method(_whoami())(self.desc, force)
293
294 - def clrstat(self):
295 """Clear all read and write status flags.""" 296 return self._method(_whoami())(self.desc)
297
298 - def header_table(self, type, version):
299 """Get the header of an extension table. 300 301 Returns the header of version VERSION of the extension table 302 TYPE.""" 303 return self._method(_whoami())(self.desc, type, version)
304 305 # XXX Deprecated.
306 - def getrow_table(self, type, version, rowno):
307 """Get a row from an extension table. 308 309 Returns row ROWNO from version VERSION of extension table TYPE 310 as a dictionary.""" 311 return self._method(_whoami())(self.desc, type, version, rowno)
312
313 - def zap_table(self, type, version):
314 """Destroy an extension table. 315 316 Deletes version VERSION of the extension table TYPE. If 317 VERSION is 0, delete the highest version of table TYPE. If 318 VERSION is -1, delete all versions of table TYPE.""" 319 return self._method(_whoami())(self.desc, type, version)
320
321 - def _generate_antennas(self):
322 return self._method('antennas')(self.desc)
323 antennas = property(_generate_antennas, 324 doc = 'Antennas in this data set.') 325
326 - def _generate_polarizations(self):
327 return self._method('polarizations')(self.desc)
328 polarizations = property(_generate_polarizations, 329 doc='Polarizations in this data set.') 330
331 - def _generate_sources(self):
332 return self._method('sources')(self.desc)
333 sources = property(_generate_sources, 334 doc='Sources in this data set.') 335
336 - def _generate_stokes(self):
337 return self._method('stokes')(self.desc)
338 stokes = property(_generate_stokes, 339 doc='Stokes parameters for this data set.') 340 341 pass # class AIPSData
342 343
344 -class AIPSImage(_AIPSData):
345 346 """This class describes an AIPS image.""" 347 pass
348 349
350 -class AIPSUVData(_AIPSData):
351 352 """This class describes an AIPS UV data set.""" 353 pass
354 355
356 -class _AIPSTableMethod(_AIPSDataMethod):
357 358 """ This class implements dispatching table oriented function 359 calls to a proxy.""" 360
361 - def __init__(self, inst, name):
362 _AIPSDataMethod.__init__(self, inst, name)
363
364 - def __call__(self, *args):
365 func = self.inst._data._method(self.name + '_table') 366 return func(self.inst._data.desc, 367 self.inst._name, self.inst._version, *args)
368 369 pass # class _AIPSTableMethod
370 371
372 -class _AIPSTableRow(_dictify):
373 374 """This class describes a row of an AIPS extenstion table.""" 375 376 pass # class _AIPSTableRow
377 378
379 -class _AIPSTableIter:
380 381 """This class provides an iterator for AIPS extension tables.""" 382
383 - def __init__(self, table):
384 self._table = table 385 self._len = len(self._table) 386 self._index = 0 387 return
388
389 - def next(self):
390 if self._index >= self._len: 391 raise StopIteration 392 result = self._table[self._index] 393 self._index += 1 394 return result
395 396 pass # class _AIPSTableIter
397 398
399 -class _AIPSTable(object):
400 401 """This class describes a generic AIPS extension table.""" 402
403 - def __init__(self, data, name, version):
404 self._data = data 405 self._name = name 406 self._version = version 407 return
408
409 - def __getattr__(self, name):
410 return _AIPSTableMethod(self, name)
411
412 - def __getitem__(self, key):
413 dict = _AIPSTableMethod(self, '_getitem')(key) 414 return _AIPSTableRow(dict)
415
416 - def __iter__(self):
417 return _AIPSTableIter(self)
418
419 - def __len__(self):
420 return _AIPSTableMethod(self, '_len')()
421
422 - def _generate_keywords(self):
423 return _AIPSTableMethod(self, 'keywords')()
424 keywords = property(_generate_keywords, 425 doc='Keywords for this table.') 426
427 - def _generate_version(self):
428 return _AIPSTableMethod(self, 'version')()
429 version = property(_generate_version, doc='Table version.') 430 431 pass # class _AIPSTable
432 433
434 -class _AIPSHistoryMethod(_AIPSDataMethod):
435 436 """ This class implements dispatching history oriented function 437 calls to a proxy.""" 438
439 - def __init__(self, inst, name):
440 _AIPSDataMethod.__init__(self, inst, name)
441
442 - def __call__(self, *args):
443 func = self.inst._data._method(self.name + '_history') 444 return func(self.inst._data.desc, *args)
445 446 pass # class _AIPSHistoryMethod
447 448
449 -class _AIPSHistory(object):
450 451 """This class describes an AIPS hostory table.""" 452
453 - def __init__(self, data):
454 self._data = data 455 return
456
457 - def __getitem__(self, key):
458 return _AIPSHistoryMethod(self, '_getitem')(key)
459 460 pass # class _AIPSHistory
461 462
463 -class _AIPSCatEntry(_dictify):
464 465 """This class describes an AIPS catalog entry.""" 466 467 pass # class _AIPSCatEntry
468 469
470 -class AIPSCat(object):
471 472 """This class describes an entire AIPS catalogue.""" 473
474 - def __init__(self, disk=0):
475 disks = [disk] 476 if disk == 0: 477 disks = range(1, len(AIPS.disks)) 478 pass 479 480 self._cat = {} 481 for disk in disks: 482 proxy = AIPS.disks[disk].proxy() 483 catalog = proxy.AIPSCat.cat(AIPS.disks[disk].disk, AIPS.userno) 484 self._cat[disk] = [_AIPSCatEntry(entry) for entry in catalog] 485 continue 486 return
487
488 - def __getitem__(self, key):
489 return self._cat[key]
490
491 - def __repr__(self):
492 return repr(self._cat)
493
494 - def __iter__(self):
495 return self._cat.iterkeys()
496
497 - def __str__(self):
498 s = '' 499 for disk in self._cat: 500 s += 'Catalog on disk %2d\n' % disk 501 s += ' Cat Mapname Class Seq Pt Last access\n' 502 if len(self._cat[disk]) > 0: 503 s += ''.join([' %3d %-12.12s.%-6.6s. %4d %-2.2s %s %s\n' \ 504 % (entry.cno, entry.name, entry.klass, 505 entry.seq, entry.type, entry.date, 506 entry.time) for entry in self._cat[disk]]) 507 pass 508 continue 509 return s.strip()
510
511 - def zap(self, force=False, **kwds):
512 513 """Removes a catalogue entry.""" 514 515 name = None 516 if 'name' in kwds: name = kwds['name']; del kwds['name'] 517 klass = None 518 if 'klass' in kwds: klass = kwds['klass']; del kwds['klass'] 519 seq = None 520 if 'seq' in kwds: seq = kwds['seq']; del kwds['seq'] 521 522 # Make sure we don't zap if the user made a typo. 523 if len(kwds) > 0: 524 keys = ["'%s'" % key for key in kwds.keys()] 525 msg = "zap() got an unexpected keyword argument %s" % keys[0] 526 raise TypeError, msg 527 528 for disk in self._cat: 529 for entry in self._cat[disk]: 530 if name and not entry['name'] == name: 531 continue 532 if klass and not entry['klass'] == klass: 533 continue 534 if seq and not entry['seq'] == seq: 535 continue 536 if entry['type'] == 'MA': 537 AIPSImage(entry['name'], entry['klass'], 538 disk, entry['seq']).zap(force) 539 elif entry['type'] == 'UV': 540 AIPSUVData(entry['name'], entry['klass'], 541 disk, entry['seq']).zap(force) 542 pass 543 continue 544 continue 545 return
546 547 pass # class AIPSCat
548 549 550 # Tests. 551 if __name__ == '__main__': 552 import doctest, sys 553 results = doctest.testmod(sys.modules[__name__]) 554 sys.exit(results[0]) 555