Package Ganga :: Package Core :: Package GangaRepository :: Module VStreamer
[hide private]
[frames] | no frames]

Source Code for Module Ganga.Core.GangaRepository.VStreamer

  1  ################################################################################ 
  2  # Ganga Project. http://cern.ch/ganga 
  3  # 
  4  # $Id: VStreamer.py,v 1.1.2.2 2009-07-14 09:20:22 ebke Exp $ 
  5  ################################################################################ 
  6   
  7  # dump object (job) to file f (or stdout) while ignoring the attribute 'ignore_subs' 
8 -def to_file(j,f=None,ignore_subs=''):
9 vstreamer = VStreamer(out=f,selection=ignore_subs) 10 vstreamer.begin_root() 11 j.accept(vstreamer) 12 vstreamer.end_root()
13 14 # Faster, but experimental version of to_file without accept() 15 #def to_file(j,f=None,ignore_subs=''): 16 # sl = ["<root>"] 17 # sl.extend(fastXML(j,' ',ignore_subs=ignore_subs)) 18 # sl.append("</root>\n") 19 # f.write("".join(sl)) 20 21 # load object (job) from file f 22 # if len(errors) > 0 the object was not loaded correctly. 23 # Typical exceptions are: 24 # * SchemaVersionError (incompatible schema version) 25 # * PluginManagerError (necessary plugin not loaded) 26 # * IOError (problem on file reading) 27 # * AssertionError (corruption: multiple objects in <root>...</root> 28 # * Exception (probably corrupted data problem) 29
30 -def from_file(f):
31 ###logger.debug('----------------------------') 32 ###logger.debug('Parsing file: %s',f.name) 33 obj,errors = Loader().parse(f.read()) 34 return obj,errors
35 36 ################################################################################ 37 # utilities 38 39 import xml.sax.saxutils 40 #import escape, unescape 41
42 -def escape(s):
43 return xml.sax.saxutils.escape(s) #s.replace('"', '\\"').replace("'", "\\'"))
44
45 -def unescape(s):
46 return xml.sax.saxutils.unescape(s)
47 48 from Ganga.GPIDev.Lib.GangaList.GangaList import makeGangaListByRef 49 50 # config_scope is namespace used for evaluating simple objects (e.g. File) 51 from Ganga.Utility.Config import config_scope 52 53 # An experimental, fast way to print a tree of Ganga Objects to file 54 # Unused at the moment
55 -def fastXML(obj,indent='',ignore_subs=''):
56 if hasattr(obj,"__iter__") and not hasattr(obj,"iteritems"): 57 sl = ["\n",indent,"<sequence>","\n"] 58 for so in obj: 59 sl.append(indent) 60 sl.extend(fastXML(so,indent+' ',ignore_subs)) 61 sl.append(indent) 62 sl.append("</sequence>") 63 return sl 64 elif hasattr(obj,'_data'): 65 v = obj._schema.version 66 sl = ['\n',indent,'<class name="%s" version="%i.%i" category="%s">\n' % (obj._name, v.major, v.minor, obj._category)] 67 for k,o in obj._data.iteritems(): 68 if k != ignore_subs: 69 try: 70 if not obj._schema[k]._meta["transient"]: 71 sl.append(indent) 72 sl.append('<attribute name="%s">' % k) 73 sl.extend(fastXML(o,indent+' ',ignore_subs)) 74 sl.append('</attribute>\n') 75 except KeyError: 76 pass 77 sl.append(indent) 78 sl.append('</class>') 79 return sl 80 else: 81 return ["<value>",escape(repr(obj)),"</value>"]
82 83 ################################################################################ 84 # A visitor to print the object tree into XML. 85 #
86 -class VStreamer(object):
87 # Arguments: 88 # out: file-like output stream where to print, default sys.stdout 89 # selection: string specifying the name of properties which should not be printed 90 # e.g. 'subjobs' - will not print subjobs
91 - def __init__(self,out=None,selection=''):
92 self.level = 0 93 self.selection = selection 94 if out: 95 self.out = out 96 else: 97 import sys 98 self.out = sys.stdout
99
100 - def begin_root(self):
101 print >> self.out,'<root>'
102
103 - def end_root(self):
104 print >> self.out,'</root>'
105
106 - def indent(self):
107 return ' '*(self.level-1)*3
108
109 - def nodeBegin(self,node):
110 self.level += 1 111 s = node._schema 112 print >> self.out,self.indent(),'<class name="%s" version="%d.%d" category="%s">'%(s.name,s.version.major,s.version.minor,s.category)
113
114 - def nodeEnd(self,node):
115 print >> self.out,self.indent(),'</class>' 116 self.level -= 1 117 return
118
119 - def print_value(self,x):
120 #FIXME: also quote % characters (to allow % operator later) 121 print >> self.out,'<value>%s</value>' % escape(repr(x))
122
123 - def showAttribute( self, node, name ):
124 return not node._schema.getItem(name)['transient'] and (self.level > 1 or name!=self.selection)
125
126 - def simpleAttribute(self,node,name, value,sequence):
127 if self.showAttribute( node, name ): 128 self.level+=1 129 print >> self.out, self.indent(), 130 print >> self.out, '<attribute name="%s">'%name, 131 if sequence: 132 self.level+=1 133 print >> self.out 134 print >> self.out, self.indent(),'<sequence>' 135 for v in value: 136 self.level+=1 137 print >> self.out, self.indent(), 138 self.print_value(v) 139 print >> self.out 140 self.level-=1 141 print >> self.out, self.indent(), '</sequence>' 142 self.level-=1 143 print >> self.out, self.indent(), '</attribute>' 144 else: 145 self.level+=1 146 self.print_value(value) 147 self.level-=1 148 print >> self.out,'</attribute>' 149 self.level-=1
150
151 - def acceptOptional(self,s):
152 self.level+=1 153 if s is None: 154 print >> self.out,self.indent(), '<value>None</value>' 155 else: 156 s.accept(self) 157 self.level-=1
158
159 - def componentAttribute(self,node,name,subnode,sequence):
160 if self.showAttribute( node, name ): 161 self.level+=1 162 print >> self.out, self.indent(), '<attribute name="%s">'%name 163 if sequence: 164 self.level+=1 165 print >> self.out, self.indent(), '<sequence>' 166 for s in subnode: 167 self.acceptOptional(s) 168 print >> self.out,self.indent(), '</sequence>' 169 self.level-=1 170 else: 171 self.acceptOptional(subnode) 172 print >> self.out,self.indent(), '</attribute>' 173 self.level-=1
174 175 176 177 178 179 ################################################################################ 180 # XML Parser. 181 182 from Ganga.GPIDev.Base.Objects import GangaObject 183 from Ganga.Utility.Plugin import PluginManagerError, allPlugins 184 from Ganga.GPIDev.Schema import Schema, Version 185 186 from Ganga.Utility.logging import getLogger 187 logger = getLogger() 188 189 # Empty Ganga Object 190 from GangaRepository import EmptyGangaObject, SchemaVersionError 191
192 -class Loader:
193 """ Job object tree loader. 194 """ 195
196 - def __init__(self):
197 self.stack = None # we construct object tree using this stack 198 self.ignore_count = 0 # ignore nested XML elements in case of data errors at a higher level 199 self.errors = [] # list of exception objects in case of data errors 200 self.value_construct = None #buffer for <value> elements (evaled as python expressions) 201 self.sequence_start = [] # buffer for building sequences (FIXME: what about nested sequences?)
202
203 - def parse(self,s):
204 """ Parse and load object from string s using internal XML parser (expat). 205 """ 206 import xml.parsers.expat 207 208 # 3 handler functions 209 def start_element(name, attrs): 210 ###logger.debug('Start element: name=%s attrs=%s', name, attrs) #FIXME: for 2.4 use CurrentColumnNumber and CurrentLineNumber 211 # if higher level element had error, ignore the corresponding part of the XML tree as we go down 212 if self.ignore_count: 213 self.ignore_count += 1 214 return 215 216 # initialize object stack 217 if name == 'root': 218 assert self.stack is None,"duplicated <root> element" 219 self.stack = [] 220 return 221 222 assert not self.stack is None,"missing <root> element" 223 224 # load a class, make empty object and push it as the current object on the stack 225 if name == 'class': 226 try: 227 cls = allPlugins.find(attrs['category'],attrs['name']) 228 except PluginManagerError,e: 229 self.errors.append(e) 230 #self.errors.append('Unknown class: %(name)s'%attrs) 231 obj = EmptyGangaObject() 232 self.ignore_count = 1 # ignore all elemenents until the corresponding ending element (</class>) is reached 233 else: 234 version = Version(*[int(v) for v in attrs['version'].split('.')]) 235 if not cls._schema.version.isCompatible(version): 236 attrs['currversion'] = '%s.%s'%(cls._schema.version.major,cls._schema.version.minor) 237 self.errors.append(SchemaVersionError('Incompatible schema of %(name)s, repository is %(version)s currently in use is %(currversion)s'%attrs)) 238 obj = EmptyGangaObject() 239 self.ignore_count = 1 # ignore all elemenents until the corresponding ending element (</class>) is reached 240 else: 241 # make a new ganga object 242 obj = super(cls, cls).__new__(cls) 243 obj._data = {} 244 self.stack.append(obj) 245 246 # push the attribute name on the stack 247 if name == 'attribute': 248 self.stack.append(attrs['name']) 249 250 # start value_contruct mode and initialize the value buffer 251 if name == 'value': 252 self.value_construct = '' 253 254 # save a marker where the sequence begins on the stack 255 if name == 'sequence': 256 self.sequence_start.append(len(self.stack))
257 258 def end_element(name): 259 ###logger.debug('End element: name=%s', name) 260 261 # if higher level element had error, ignore the corresponding part of the XML tree as we go up 262 if self.ignore_count: 263 self.ignore_count -= 1 264 return 265 266 # when </attribute> is seen the current object, attribute name and value should be on top of the stack 267 if name == 'attribute': 268 value = self.stack.pop() 269 aname = self.stack.pop() 270 obj = self.stack[-1] 271 # update the object's attribute 272 obj._data[aname] = value 273 274 # when </value> is seen the value_construct buffer (CDATA) should be a python expression (e.g. quoted string) 275 if name == 'value': 276 # unescape the special characters 277 s = unescape(self.value_construct) 278 ###logger.debug('string value: %s',s) 279 val = eval(s,config_scope) 280 ###logger.debug('evaled value: %s type=%s',repr(val),type(val)) 281 self.stack.append(val) 282 self.value_construct = None 283 284 # when </sequence> is seen we remove last items from stack (as indicated by sequence_start) 285 # we make a GangaList from these items and put it on stack 286 if name == 'sequence': 287 pos = self.sequence_start.pop() 288 alist = makeGangaListByRef(self.stack[pos:]) 289 del self.stack[pos:] 290 self.stack.append(alist) 291 292 # when </class> is seen we finish initializing the new object 293 # by setting remaining attributes to their default values 294 # the object stays on the stack (will be removed by </attribute> or is a root object) 295 if name == 'class': 296 obj = self.stack[-1] 297 for attr, item in obj._schema.allItems(): 298 if not attr in obj._data: 299 if item._meta["sequence"] == 1: 300 obj._data[attr] = makeGangaListByRef(obj._schema.getDefaultValue(attr)) 301 else: 302 obj._data[attr] = obj._schema.getDefaultValue(attr) 303 304 obj.__setstate__(obj.__dict__) # this sets the parent
305 306 def char_data(data): 307 # char_data may be called many times in one CDATA section so we need to build up 308 # the full buffer for <value>CDATA</value> section incrementally 309 if self.value_construct is not None: 310 ###logger.debug('char_data: append=%s',data) 311 #FIXME: decode data 312 self.value_construct += data 313 314 #start parsing using callbacks 315 p = xml.parsers.expat.ParserCreate() 316 317 p.StartElementHandler = start_element 318 p.EndElementHandler = end_element 319 p.CharacterDataHandler = char_data 320 321 p.Parse(s) 322 323 if len(self.stack)!=1: 324 self.errors.append(AssertionError('multiple objects inside <root> element')) 325 326 obj = self.stack[-1] 327 # Raise Exception if object is incomplete 328 for attr, item in obj._schema.allItems(): 329 if not attr in obj._data: 330 raise AssertionError("incomplete XML file") 331 return obj,self.errors 332