Package solvcon :: Package io :: Module core
[hide private]
[frames] | no frames]

Source Code for Module solvcon.io.core

  1  # -*- coding: UTF-8 -*- 
  2  # 
  3  # Copyright (C) 2008-2010 Yung-Yu Chen <yyc@solvcon.net>. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License along 
 16  # with this program; if not, write to the Free Software Foundation, Inc., 
 17  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 18   
 19  """ 
 20  Core I/O facilities for SOLVCON intrinsic constructs. 
 21  """ 
 22   
 23  from ..gendata import SingleAssignDict, AttributeDict 
24 25 -class FormatRegistry(SingleAssignDict, AttributeDict):
26 """ 27 Registry for a certain class of formats. 28 """
29 - def register(self, ftype):
30 name = ftype.__name__ 31 rev = ftype.FORMAT_REV 32 self[name] = self[rev] = ftype 33 return ftype
34
35 -class FormatMeta(type):
36 """ 37 Sum the length of all META_ entries of bases and derived classes; only 38 collect from 1 level of parents. 39 """
40 - def __new__(cls, name, bases, namespace):
41 mldict = dict() 42 # collect length from base classes. later overrides former. 43 # NOTE: only 1 level of parents is considered. 44 for base in bases: 45 for key in base.__dict__: 46 if key.startswith('META_'): 47 mldict[key] = len(getattr(base, key)) 48 # collect length from derived class, and override bases. 49 for key in namespace: 50 if key.startswith('META_'): 51 mldict[key] = len(namespace[key]) 52 # sum them all. 53 namespace['meta_length'] = sum([mldict[k] for k in mldict]) 54 # recreate and return the class. 55 return super(FormatMeta, cls).__new__(cls, name, bases, namespace)
56
57 -class Format(object):
58 """ 59 Abstract class for SOLVCON intrinsic I/O format Each of the concrete 60 derived classes represents a version of format. Public interface method is 61 read_meta(). read_meta() method uses _parse_meta() private method to 62 report the meta-data of the format, which is defined in META_* class 63 attributes (as tuples). The default _parse_meta() and _save_meta() use 64 SPEC_OF_META to determine the order and converter the each field of the 65 META section. 66 67 The supported intrisic formats must consist of two parts: (i) text part and 68 (ii) binary part. Compression is OK for the binary part. The text part 69 starts at the beginneg of the file and ends after BINARY_MARKER, which the 70 binary part starts after BINARY_MARKER and ends with the file. 71 72 @cvar READ_BLOCK: length per reading from file. 73 @ctype READ_BLOCK: int 74 @cvar FILE_HEADER: header of the format; must be overridden. 75 @ctype FILE_HEADER: str 76 @cvar BINARY_MARKER: a string to mark the starting of binary data. 77 @ctype BINARY_MARKER: str 78 @cvar FORMAT_REV: revision of format; must be overridden. 79 @ctype FORMAT_REV: str 80 @cvar SPEC_OF_META: the order and converter of each meta-data section 81 occured in the text part; must be overridden. 82 @ctype SPEC_OF_META: tuple of (str, callable) 83 84 @cvar meta_length: length of all META_ entries; calculated by FormatMeta. 85 @ctype meta_length: int 86 """ 87 88 __metaclass__ = FormatMeta 89 90 READ_BLOCK = 1024 91 FILE_HEADER = None 92 BINARY_MARKER = '-*- start of binary data -*-' 93 FORMAT_REV = None 94 SPEC_OF_META = None 95
96 - def read_meta(self, stream):
97 """ 98 Read meta-data from stream. 99 100 @param stream: file object or file name to be read. 101 @type stream: file or str 102 @return: meta-data, raw text lines of meta-data, and the length of 103 meta-data in bytes. 104 @rtype: solvcon.gendata.AttributeDict 105 """ 106 return self._parse_meta(self._get_textpart(stream)[0])
107 108 ############################################################################ 109 # Facilities for writing. 110 ############################################################################ 111 @staticmethod
112 - def _write_array(compressor, arr, stream):
113 """ 114 @param compressor: how to compress data arrays. 115 @type compressor: str 116 @param arr: the array to be written. 117 @type arr: numpy.ndarray 118 @param stream: output stream. 119 @type stream: file 120 @return: nothing. 121 """ 122 import bz2, zlib, struct 123 if compressor == 'bz2': 124 data = bz2.compress(arr.data, 9) 125 stream.write(struct.pack('q', len(data))) 126 elif compressor == 'gz': 127 data = zlib.compress(arr.data, 9) 128 stream.write(struct.pack('q', len(data))) 129 else: 130 data = arr.data 131 stream.write(data)
132 ############################################################################ 133 # Facilities for reading. 134 ############################################################################ 135 @classmethod
136 - def _get_textpart(cls, stream):
137 """ 138 Retrieve the text part from the stream. 139 140 @param stream: input stream. 141 @type stream: file 142 @return: text lines and length of text part (including separator). 143 @rtype: tuple 144 """ 145 buf = '' 146 while cls.BINARY_MARKER not in buf: 147 buf += stream.read(cls.READ_BLOCK) 148 buf = buf.split(cls.BINARY_MARKER)[0] 149 lines = buf.split('\n')[:-1] 150 textlen = len(buf) + len(cls.BINARY_MARKER) + 1 151 # assert text format. 152 assert lines[0].strip()[:3] == '-*-' 153 assert lines[0].strip()[-3:] == '-*-' 154 # assert text end location. 155 stream.seek(0) 156 assert stream.read(textlen)[-1] == '\n' 157 stream.seek(0) 158 return lines, textlen
159 @classmethod
160 - def _parse_meta(cls, lines):
161 """ 162 Parse meta information from the file. 163 164 @param lines: text part of the file. 165 @type lines: list 166 @return: meta information dictionary. 167 @rtype: solvcon.gendata.AttributeDict 168 """ 169 import numpy as np 170 from ..gendata import AttributeDict 171 meta = AttributeDict() 172 end = 1 173 for mname, mfunc in cls.SPEC_OF_META: 174 # skip everything after the first section with None converter. 175 if mfunc == None: break 176 # process. 177 msec = getattr(cls, 'META_'+mname) 178 begin = end 179 end = begin + len(msec) 180 for line in lines[begin:end]: 181 key, val = [to.strip() for to in line.split('=')] 182 try: 183 meta[key] = mfunc(val) 184 except ValueError: 185 meta[key] = None 186 return meta
187 @staticmethod
188 - def _read_array(compressor, shape, dtype, stream, seek_only=False):
189 """ 190 Read data from the input stream and convert it to ndarray with given 191 shape and dtype. 192 193 @param compressor: how to compress data arrays. 194 @type compressor: str 195 @param shape: ndarray shape. 196 @type shape: tuple 197 @param dtype: ndarray dtype. 198 @type dtype: numpy.dtype or str 199 @param stream: input stream. 200 @type stream: file 201 @keyword seek_only: do not really read, only seek; default False. 202 @type seek_only: bool 203 @return: resulted array. 204 @rtype: numpy.ndarray 205 """ 206 import bz2 207 import zlib 208 import numpy as np 209 length = shape[0] 210 for dim in shape[1:]: 211 length *= dim 212 if isinstance(dtype, basestring): 213 dtype = getattr(np, dtype) 214 dobj = dtype() 215 if compressor == 'bz2': 216 buflen = np.frombuffer(stream.read(8), dtype=np.int64)[0] 217 if seek_only: 218 stream.seek(stream.tell() + buflen) 219 else: 220 buf = stream.read(buflen) 221 buf = bz2.decompress(buf) 222 elif compressor == 'gz': 223 buflen = np.frombuffer(stream.read(8), dtype=np.int64)[0] 224 if seek_only: 225 stream.seek(stream.tell() + buflen) 226 else: 227 buf = stream.read(buflen) 228 buf = zlib.decompress(buf) 229 else: 230 buflen = length * dobj.itemsize 231 if seek_only: 232 stream.seek(stream.tell() + buflen) 233 else: 234 buf = stream.read(buflen) 235 if seek_only: 236 arr = None 237 else: 238 arr = np.frombuffer(buf, dtype=dtype).reshape(shape).copy() 239 return arr
240
241 -class FormatIORegistry(SingleAssignDict, AttributeDict):
242 """ 243 Registry for all FormatIO classes. 244 """
245 - def register(self, ftype):
246 name = ftype.__name__ 247 self[name] = ftype 248 return ftype
249 fioregy = FormatIORegistry() # registry singleton.
250 -class FormatIOMeta(type):
251 """ 252 Metaclass for FormatIO. 253 """
254 - def __new__(cls, name, bases, namespace):
255 # recreate the class. 256 newcls = super(FormatIOMeta, cls).__new__( 257 cls, name, bases, namespace) 258 # register. 259 fioregy.register(newcls) 260 return newcls
261 -class FormatIO(object):
262 """ 263 Proxy to mesh format object. 264 """ 265 __metaclass__ = FormatIOMeta
266 - def save(self):
267 """ 268 Save an object for mesh. 269 """ 270 raise NotImplementedError
271 - def load(self, bcmapper=None):
272 """ 273 Load and return an object for mesh. 274 275 @keyword bcmapper: BC type mapper. 276 @type bcmapper: dict 277 @return: the loaded object for mesh. 278 """ 279 raise NotImplementedError
280
281 ################################################################################ 282 # Utility 283 ################################################################################ 284 -def strbool(val):
285 """ 286 Create bool object from string. None is returned if input is not 287 derterminable. 288 289 @param val: the string to check. 290 @type val: str 291 @return: True/False/None. 292 @rtype: bool 293 """ 294 try: 295 return bool(int(val)) 296 except: 297 val = val.lower() 298 if val == 'true' or val == 'on': 299 return True 300 elif val == 'false' or val == 'off': 301 return False 302 else: 303 return None
304