|
| 1 | +class XmlParser(object): |
| 2 | + """Parses a classes_def.xml file looking for class declarations that contain |
| 3 | + ClassVersion attributes. Once found looks for sub-elements named 'version' |
| 4 | + which contain the ClassVersion to checksum mappings. |
| 5 | + """ |
| 6 | + |
| 7 | + #The following are constants used to describe what data is kept |
| 8 | + # in which index in the 'classes' member data |
| 9 | + originalNameIndex=0 |
| 10 | + classVersionIndex=1 |
| 11 | + versionsToChecksumIndex = 2 |
| 12 | + |
| 13 | + def __init__(self, filename, includeNonVersionedClasses=False, normalizeClassNames=True): |
| 14 | + self._file = filename |
| 15 | + self.classes = dict() |
| 16 | + self._presentClass = None |
| 17 | + self._presentClassForVersion = None |
| 18 | + self._includeNonVersionedClasses = includeNonVersionedClasses |
| 19 | + self._normalizeClassNames = normalizeClassNames |
| 20 | + self.readClassesDefXML() |
| 21 | + def readClassesDefXML(self): |
| 22 | + import xml.parsers.expat |
| 23 | + p = xml.parsers.expat.ParserCreate() |
| 24 | + p.StartElementHandler = self.start_element |
| 25 | + p.EndElementHandler = self.end_element |
| 26 | + f = open(self._file) |
| 27 | + # Replace any occurence of <>& in the attribute values by the xml parameter |
| 28 | + rxml, nxml = f.read(), '' |
| 29 | + q1,q2 = 0,0 |
| 30 | + for c in rxml : |
| 31 | + if (q1 or q2) and c == '<' : nxml += '<' |
| 32 | + elif (q1 or q2) and c == '>' : nxml += '>' |
| 33 | + # elif (q1 or q2) and c == '&' : nxml += '&' |
| 34 | + else : nxml += c |
| 35 | + if c == '"' : q1 = not q1 |
| 36 | + if c == "'" : q2 = not q2 |
| 37 | + try : p.Parse(nxml) |
| 38 | + except xml.parsers.expat.ExpatError as e : |
| 39 | + print ('--->> edmCheckClassVersion: ERROR: parsing selection file ',self._file) |
| 40 | + print ('--->> edmCheckClassVersion: ERROR: Error is:', e) |
| 41 | + raise |
| 42 | + f.close() |
| 43 | + def start_element(self,name,attrs): |
| 44 | + if name in ('class','struct'): |
| 45 | + if 'name' in attrs: |
| 46 | + self._presentClass=attrs['name'] |
| 47 | + normalizedName = self.genNName(attrs['name']) |
| 48 | + if 'ClassVersion' in attrs: |
| 49 | + self.classes[normalizedName]=[attrs['name'],int(attrs['ClassVersion']),[]] |
| 50 | + self._presentClassForVersion=normalizedName |
| 51 | + elif self._includeNonVersionedClasses: |
| 52 | + # skip transient data products |
| 53 | + if not ('persistent' in attrs and attrs['persistent'] == "false"): |
| 54 | + self.classes[normalizedName]=[attrs['name'],-1,[]] |
| 55 | + else: |
| 56 | + raise RuntimeError(f"There is an element '{name}' without 'name' attribute.") |
| 57 | + if name == 'version': |
| 58 | + if self._presentClassForVersion is None: |
| 59 | + raise RuntimeError(f"Class element for type '{self._presentClass}' contains a 'version' element, but 'ClassVersion' attribute is missing from the 'class' element") |
| 60 | + try: |
| 61 | + classVersion = int(attrs['ClassVersion']) |
| 62 | + except KeyError: |
| 63 | + raise RuntimeError(f"Version element for type '{self._presentClass}' is missing 'ClassVersion' attribute") |
| 64 | + try: |
| 65 | + checksum = int(attrs['checksum']) |
| 66 | + except KeyError: |
| 67 | + raise RuntimeError(f"Version element for type '{self._presentClass}' is missing 'checksum' attribute") |
| 68 | + self.classes[self._presentClassForVersion][XmlParser.versionsToChecksumIndex].append([classVersion, checksum]) |
| 69 | + pass |
| 70 | + def end_element(self,name): |
| 71 | + if name in ('class','struct'): |
| 72 | + self._presentClass = None |
| 73 | + self._presentClassForVersion = None |
| 74 | + def genNName(self, name ): |
| 75 | + if not self._normalizeClassNames: |
| 76 | + return name |
| 77 | + n_name = " ".join(name.split()) |
| 78 | + for e in [ ['long long unsigned int', 'unsigned long long'], |
| 79 | + ['long long int', 'long long'], |
| 80 | + ['unsigned short int', 'unsigned short'], |
| 81 | + ['short unsigned int', 'unsigned short'], |
| 82 | + ['short int', 'short'], |
| 83 | + ['long unsigned int', 'unsigned long'], |
| 84 | + ['unsigned long int', 'unsigned long'], |
| 85 | + ['long int', 'long'], |
| 86 | + ['std::string', 'std::basic_string<char>']] : |
| 87 | + n_name = n_name.replace(e[0],e[1]) |
| 88 | + n_name = n_name.replace(' ','') |
| 89 | + return n_name |
| 90 | + |
| 91 | +def initROOT(library): |
| 92 | + #Need to not have ROOT load .rootlogon.(C|py) since it can cause interference. |
| 93 | + import ROOT |
| 94 | + ROOT.PyConfig.DisableRootLogon = True |
| 95 | + |
| 96 | + #Keep ROOT from trying to use X11 |
| 97 | + ROOT.gROOT.SetBatch(True) |
| 98 | + ROOT.gROOT.ProcessLine(".autodict") |
| 99 | + if library is not None: |
| 100 | + if ROOT.gSystem.Load(library) < 0 : |
| 101 | + raise RuntimeError("failed to load library '"+library+"'") |
| 102 | + |
| 103 | +def initCheckClass(): |
| 104 | + """Must be called before checkClass()""" |
| 105 | + import ROOT |
| 106 | + ROOT.gROOT.ProcessLine("class checkclass {public: int f(char const* name) {TClass* cl = TClass::GetClass(name); bool b = false; cl->GetCheckSum(b); return (int)b;} };") |
| 107 | + ROOT.gROOT.ProcessLine("checkclass checkTheClass;") |
| 108 | + |
| 109 | + |
| 110 | +#The following are error codes returned from checkClass |
| 111 | +noError = 0 |
| 112 | +errorRootDoesNotMatchClassDef =1 |
| 113 | +errorMustUpdateClassVersion=2 |
| 114 | +errorMustAddChecksum=3 |
| 115 | + |
| 116 | +def checkClass(name,version,versionsToChecksums): |
| 117 | + import ROOT |
| 118 | + c = ROOT.TClass.GetClass(name) |
| 119 | + if not c: |
| 120 | + raise RuntimeError("failed to load dictionary for class '"+name+"'") |
| 121 | + temp = "checkTheClass.f(" + '"' + name + '"' + ");" |
| 122 | + retval = ROOT.gROOT.ProcessLine(temp) |
| 123 | + if retval == 0 : |
| 124 | + raise RuntimeError("TClass::GetCheckSum: Failed to load dictionary for base class. See previous Error message") |
| 125 | + classChecksum = c.GetCheckSum() |
| 126 | + classVersion = c.GetClassVersion() |
| 127 | + |
| 128 | + #does this version match what is in the file? |
| 129 | + if version != classVersion: |
| 130 | + return (errorRootDoesNotMatchClassDef,classVersion,classChecksum) |
| 131 | + |
| 132 | + #is the version already in our list? |
| 133 | + found = False |
| 134 | + |
| 135 | + for v,cs in versionsToChecksums: |
| 136 | + if v == version: |
| 137 | + found = True |
| 138 | + if classChecksum != cs: |
| 139 | + return (errorMustUpdateClassVersion,classVersion,classChecksum) |
| 140 | + break |
| 141 | + if not found and classVersion != 0: |
| 142 | + return (errorMustAddChecksum,classVersion,classChecksum) |
| 143 | + return (noError,classVersion,classChecksum) |
0 commit comments