| """This module generate Python code for C structs. |
| |
| Date January 2010 |
| Created by ykk |
| """ |
| import cheader |
| import c2py |
| import datetime |
| import struct |
| import re |
| from config import * |
| |
| def _space_to(n, str): |
| """ |
| Generate a string of spaces to achieve width n given string str |
| If length of str >= n, return one space |
| """ |
| spaces = n - len(str) |
| if spaces > 0: |
| return " " * spaces |
| return " " |
| |
| class rules: |
| """Class that specify rules for pythonization |
| |
| Date January 2010 |
| Created by ykk |
| """ |
| def __init__(self): |
| """Initialize rules |
| """ |
| ##Default values for members |
| self.default_values = {} |
| #Default values for struct |
| self.struct_default = {} |
| ##What is a tab |
| self.tab = " " |
| ##Macros to exclude |
| self.excluded_macros = [] |
| ##Enforce mapping |
| self.enforced_maps = {} |
| |
| def get_enforced_map(self, structname): |
| """Get code to enforce mapping |
| """ |
| code = [] |
| try: |
| mapping = self.enforced_maps[structname] |
| except KeyError: |
| return None |
| for (x,xlist) in mapping: |
| code.append("if (not (self."+x+" in "+xlist+")):") |
| code.append(self.tab+"return (False, \""+x+" must have values from "+xlist+"\")") |
| return code |
| |
| |
| def get_struct_default(self, structname, fieldname): |
| """Get code to set defaults for member struct |
| """ |
| try: |
| return "."+fieldname+self.struct_default[(structname, fieldname)] |
| except KeyError: |
| return None |
| |
| def get_default_value(self, structname, fieldname): |
| """Get default value for struct's field |
| """ |
| try: |
| return self.default_values[(structname, fieldname)] |
| except KeyError: |
| return 0 |
| |
| def include_macro(self, name): |
| """Check if macro should be included |
| """ |
| return not (name in self.excluded_macros) |
| |
| class pythonizer: |
| """Class that pythonize C structures |
| |
| Date January 2010 |
| Created by ykk |
| """ |
| def __init__(self, cheaderfile, pyrules = None, tab=" "): |
| """Initialize |
| """ |
| ##Rules |
| if (pyrules == None): |
| self.rules = rules() |
| else: |
| self.rules = pyrules |
| ##What is a tab (same as rules) |
| self.tab = str(tab) |
| self.rules.tab = self.tab |
| ##Reference to C header file |
| self.cheader = cheaderfile |
| ##Reference to cstruct2py |
| self.__c2py = c2py.cstruct2py() |
| ##Code for assertion |
| self.__assertcode = [] |
| |
| def pycode(self,preamble=None): |
| """Return pythonized code |
| """ |
| code = [] |
| code.append("import struct") |
| code.append("") |
| if (preamble != None): |
| fileRef = open(preamble,"r") |
| for l in fileRef: |
| code.append(l[:-1]) |
| fileRef.close() |
| code.append("# Structure definitions") |
| for name,struct in self.cheader.structs.items(): |
| code.extend(self.pycode_struct(struct)) |
| code.append("") |
| code.append("# Enumerated type definitions") |
| for name,enum in self.cheader.enums.items(): |
| code.extend(self.pycode_enum(name,enum)) |
| if GEN_ENUM_DICTIONARY: |
| code.extend(self.pycode_enum_map(name,enum)) |
| code.append("") |
| code.append("# Values from macro definitions") |
| for name,macro in self.cheader.macros.items(): |
| code.extend(self.pycode_macro(name)) |
| code.append("") |
| code.append("# Basic structure size definitions.") |
| if IGNORE_OFP_HEADER: |
| code.append("# Does not include ofp_header members.") |
| if IGNORE_ZERO_ARRAYS: |
| code.append("# Does not include variable length arrays.") |
| struct_keys = self.cheader.structs.keys() |
| struct_keys.sort() |
| for name in struct_keys: |
| struct = self.cheader.structs[name] |
| code.append(self.pycode_struct_size(name, struct)) |
| if GEN_AUX_INFO: |
| self.gen_struct_map() |
| |
| return code |
| |
| def pycode_enum(self, name, enum): |
| """Return Python array for enum |
| """ |
| code=[] |
| code.append(name+" = "+str(enum)) |
| ev = [] |
| for e in enum: |
| v = self.cheader.get_value(e) |
| ev.append(v) |
| code.append(e+"%s= "%_space_to(36,e)+str(v)) |
| if GEN_ENUM_VALUES_LIST: |
| code.append(name+"_values = "+str(ev)) |
| return code |
| |
| def pycode_enum_map(self, name, enum): |
| """Return Python dictionary for enum |
| """ |
| code = [] |
| code.append(name+"_map = {") |
| first = 1 |
| for e in enum: |
| v = self.cheader.get_value(e) |
| if first: |
| prev_e = e |
| prev_v = v |
| first = 0 |
| else: |
| code.append(self.tab + "'%s'%s: %s," % |
| (prev_e, _space_to(30, prev_e), prev_v)) |
| prev_e = e |
| prev_v = v |
| code.append(self.tab + "'%s'%s: %s" % |
| (prev_e, _space_to(30, prev_e), prev_v)) |
| code.append("}") |
| return code |
| |
| def pycode_macro(self,name): |
| """Return Python dict for macro |
| """ |
| code = [] |
| if (self.rules.include_macro(name)): |
| code.append(name+" = "+str(self.cheader.get_value(name))) |
| return code |
| |
| def pycode_struct_size(self, name, struct): |
| """Return one liner giving the structure size in bytes |
| """ |
| pattern = '!' + self.__c2py.get_pattern(struct) |
| bytes = self.__c2py.get_size(pattern) |
| code = name.upper() + "_BYTES = " + str(bytes) |
| return code |
| |
| def pycode_struct(self, struct_in): |
| """Return Python class code given C struct. |
| |
| Returns None if struct_in is not cheader.cstruct. |
| Else return list of strings that codes Python class. |
| """ |
| if (not isinstance(struct_in, cheader.cstruct)): |
| return None |
| |
| code=[] |
| self.__assertcode = [] |
| code.extend(self.codeheader(struct_in)) |
| code.extend(self.codeinit(struct_in)) |
| code.append("") |
| code.extend(self.codeassert(struct_in)) |
| code.append("") |
| code.extend(self.codepack(struct_in)) |
| code.append("") |
| code.extend(self.codeunpack(struct_in)) |
| code.append("") |
| code.extend(self.codelen(struct_in)) |
| code.append("") |
| if GEN_OBJ_EQUALITY: |
| code.extend(self.codeeq(struct_in)) |
| code.append("") |
| if GEN_OBJ_SHOW: |
| code.extend(self.codeshow(struct_in)) |
| code.append("") |
| return code |
| |
| def codeheader(self, struct_in): |
| """Return Python code for header |
| """ |
| code=[] |
| code.append("class "+struct_in.typename+":") |
| code.append(self.tab+"\"\"\"Automatically generated Python class for "+struct_in.typename) |
| code.append("") |
| code.append(self.tab+"Date "+str(datetime.date.today())) |
| code.append(self.tab+"Created by "+self.__module__+"."+self.__class__.__name__) |
| if IGNORE_OFP_HEADER: |
| code.append(self.tab+"Core structure: Messages do not include ofp_header") |
| if IGNORE_ZERO_ARRAYS: |
| code.append(self.tab+"Does not include var-length arrays") |
| code.append(self.tab+"\"\"\"") |
| return code |
| |
| def codeinit(self, struct_in): |
| """Return Python code for init function |
| """ |
| code = [] |
| code.append(self.tab+"def __init__(self):") |
| code.append(self.tab*2+"\"\"\"Initialize") |
| code.append(self.tab*2+"Declare members and default values") |
| code.append(self.tab*2+"\"\"\"") |
| code.extend(self.codemembers(struct_in,self.tab*2+"self")) |
| return code |
| |
| def codemembers(self, struct_in, prepend=""): |
| """Return members of class |
| """ |
| code = [] |
| for member in struct_in.members: |
| if (isinstance(member, cheader.cstruct)): |
| code.append(prepend+"."+member.name+" = "+member.typename+"()") |
| struct_default = self.rules.get_struct_default(struct_in.typename, member.name) |
| if (struct_default != None): |
| code.append(prepend+struct_default) |
| self.__structassert(member, (prepend+"."+member.name).strip()) |
| elif (isinstance(member, cheader.carray)): |
| if (member.typename == "char"): |
| initvalue = "\"\"" |
| self.__stringassert(member, (prepend+"."+member.name).strip()) |
| else: |
| if (isinstance(member.object, cheader.cprimitive)): |
| initvalue="0" |
| else: |
| initvalue="None" |
| initvalue=(initvalue+",")*member.size |
| initvalue="["+initvalue[:-1]+"]" |
| self.__arrayassert(member, (prepend+"."+member.name).strip()) |
| code.append(prepend+"."+member.name+"= "+initvalue) |
| else: |
| code.append(prepend+"."+member.name+" = "+ |
| str(self.rules.get_default_value(struct_in.typename, member.name))) |
| return code |
| |
| def gen_struct_map(self): |
| print |
| print "# Class to array member map" |
| print "class_to_members_map = {" |
| for name, struct in self.cheader.structs.items(): |
| if not len(struct.members): |
| continue |
| s = " '" + name + "'" |
| print s + _space_to(36, s) + ": [" |
| prev = None |
| for member in struct.members: |
| if re.search('pad', member.name): |
| continue |
| if prev: |
| print _space_to(39, "") + "'" + prev + "'," |
| prev = member.name |
| print _space_to(39, "") + "'" + prev + "'" |
| print _space_to(38, "") + "]," |
| print " '_ignore' : []" |
| print "}" |
| |
| def __structassert(self, cstruct, cstructname): |
| """Return code to check for C array |
| """ |
| self.__assertcode.append(self.tab*2+"if(not isinstance("+cstructname+", "+cstruct.typename+")):") |
| self.__assertcode.append(self.tab*3+"return (False, \""+cstructname+" is not class "+cstruct.typename+" as expected.\")") |
| |
| def __addassert(self, prefix): |
| code = [] |
| code.append(prefix+"if(not self.__assert()[0]):") |
| code.append(prefix+self.tab+"return None") |
| return code |
| |
| def __stringassert(self, carray, carrayname): |
| """Return code to check for C array |
| """ |
| self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", str)):") |
| self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not string as expected.\")") |
| self.__assertcode.append(self.tab*2+"if(len("+carrayname+") > "+str(carray.size)+"):") |
| self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")") |
| |
| def __arrayassert(self, carray, carrayname): |
| """Return code to check for C array |
| """ |
| if (carray.size == 0): |
| return |
| self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", list)):") |
| self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not list as expected.\")") |
| self.__assertcode.append(self.tab*2+"if(len("+carrayname+") != "+str(carray.size)+"):") |
| self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")") |
| |
| def codeassert(self, struct_in): |
| """Return code for sanity checking |
| """ |
| code = [] |
| code.append(self.tab+"def __assert(self):") |
| code.append(self.tab*2+"\"\"\"Sanity check") |
| code.append(self.tab*2+"\"\"\"") |
| enforce = self.rules.get_enforced_map(struct_in.typename) |
| if (enforce != None): |
| for line in enforce: |
| code.append(self.tab*2+line) |
| code.extend(self.__assertcode) |
| code.append(self.tab*2+"return (True, None)") |
| return code |
| |
| def codepack(self, struct_in, prefix="!"): |
| """Return code that pack struct |
| """ |
| code = [] |
| code.append(self.tab+"def pack(self, assertstruct=True):") |
| code.append(self.tab*2+"\"\"\"Pack message") |
| code.append(self.tab*2+"Packs empty array used as placeholder") |
| code.append(self.tab*2+"\"\"\"") |
| code.append(self.tab*2+"if(assertstruct):") |
| code.extend(self.__addassert(self.tab*3)) |
| code.append(self.tab*2+"packed = \"\"") |
| primPattern = "" |
| primMemberNames = [] |
| for member in struct_in.members: |
| if (isinstance(member, cheader.cprimitive)): |
| #Primitives |
| primPattern += self.__c2py.structmap[member.typename] |
| primMemberNames.append("self."+member.name) |
| else: |
| (primPattern, primMemberNames) = \ |
| self.__codepackprimitive(code, primPattern, |
| primMemberNames, prefix) |
| if (isinstance(member, cheader.cstruct)): |
| #Struct |
| code.append(self.tab*2+"packed += self."+member.name+".pack()") |
| elif (isinstance(member, cheader.carray) and member.typename == "char"): |
| #String |
| code.append(self.tab*2+"packed += self."+member.name+".ljust("+\ |
| str(member.size)+",'\\0')") |
| elif (isinstance(member, cheader.carray) and \ |
| isinstance(member.object, cheader.cprimitive)): |
| #Array of Primitives |
| expandedarr = "" |
| if (member.size != 0): |
| for x in range(0, member.size): |
| expandedarr += ", self."+member.name+"["+\ |
| str(x).strip()+"]" |
| code.append(self.tab*2+"packed += struct.pack(\""+prefix+\ |
| self.__c2py.structmap[member.object.typename]*member.size+\ |
| "\""+expandedarr+")") |
| else: |
| code.append(self.tab*2+"for i in self."+member.name+":") |
| code.append(self.tab*3+"packed += struct.pack(\""+\ |
| prefix+self.__c2py.get_pattern(member.object)+\ |
| "\",i)") |
| elif (isinstance(member, cheader.carray) and \ |
| isinstance(member.object, cheader.cstruct)): |
| #Array of struct |
| if (member.size != 0): |
| for x in range(0, member.size): |
| code.append(self.tab*2+"packed += self."+member.name+"["+\ |
| str(x).strip()+"].pack()") |
| else: |
| code.append(self.tab*2+"for i in self."+member.name+":") |
| code.append(self.tab*3+"packed += i.pack(assertstruct)") |
| #Clear remaining fields |
| (primPattern, primMemberNames) = \ |
| self.__codepackprimitive(code, primPattern, |
| primMemberNames, prefix) |
| code.append(self.tab*2+"return packed") |
| return code |
| |
| def __codepackprimitive(self, code, primPattern, primMemberNames, prefix): |
| """Return code for packing primitives |
| """ |
| if (primPattern != ""): |
| #Clear prior primitives |
| code.append(self.tab*2+"packed += struct.pack(\""+\ |
| prefix+primPattern+"\", "+\ |
| str(primMemberNames).replace("'","")[1:-1]+")") |
| return ("",[]) |
| |
| def codelen(self, struct_in): |
| """Return code to return length |
| """ |
| pattern = "!" + self.__c2py.get_pattern(struct_in) |
| code = [] |
| code.append(self.tab+"def __len__(self):") |
| code.append(self.tab*2+"\"\"\"Return length of message") |
| code.append(self.tab*2+"\"\"\"") |
| code.append(self.tab*2+"l = "+str(self.__c2py.get_size(pattern))) |
| for member in struct_in.members: |
| if (isinstance(member, cheader.carray) and member.size == 0): |
| if (isinstance(member.object, cheader.cstruct)): |
| code.append(self.tab*2+"for i in self."+member.name+":") |
| code.append(self.tab*3+"l += i.length()") |
| else: |
| pattern="!"+self.__c2py.get_pattern(member.object) |
| size=self.__c2py.get_size(pattern) |
| code.append(self.tab*2+"l += len(self."+member.name+")*"+str(size)) |
| code.append(self.tab*2+"return l") |
| return code |
| |
| def codeeq(self, struct_in): |
| """Return code to return equality comparisons |
| """ |
| code = [] |
| code.append(self.tab+"def __eq__(self, other):") |
| code.append(self.tab*2+"\"\"\"Return True if self and other have same values") |
| code.append(self.tab*2+"\"\"\"") |
| code.append(self.tab*2+"if type(self) != type(other): return False") |
| for member in struct_in.members: |
| code.append(self.tab*2 + "if self." + member.name + " != other." + |
| member.name + ": return False") |
| code.append(self.tab*2+"return True") |
| code.append("") |
| code.append(self.tab+"def __ne__(self, other): return not self.__eq__(other)") |
| return code |
| |
| def codeshow(self, struct_in): |
| """Return code to print basic members of structure |
| """ |
| code = [] |
| code.append(self.tab+"def show(self, prefix=''):") |
| code.append(self.tab*2+"\"\"\"" + "Print basic members of structure") |
| code.append(self.tab*2+"\"\"\"") |
| for member in struct_in.members: |
| if re.search('pad', member.name): |
| continue |
| elif (isinstance(member, cheader.cstruct)): |
| code.append(self.tab*2 + "print prefix + '" + |
| member.name + ": ' ") |
| code.append(self.tab*2 + "self." + member.name + |
| ".show(prefix + ' ')") |
| elif (isinstance(member, cheader.carray) and |
| not isinstance(member.object, cheader.cprimitive)): |
| code.append(self.tab*2 + "print prefix + '" + member.name + |
| ": ' ") |
| code.append(self.tab*2 + "for obj in self." + member.name + ":") |
| code.append(self.tab*3 + "obj.show(prefix + ' ')") |
| else: |
| code.append(self.tab*2 + "print prefix + '" + member.name + |
| ": ' + str(self." + member.name + ")") |
| return code |
| |
| def codeunpack(self, struct_in, prefix="!"): |
| """Return code that unpack struct |
| """ |
| pattern = self.__c2py.get_pattern(struct_in) |
| structlen = self.__c2py.get_size(prefix + pattern) |
| code = [] |
| code.append(self.tab+"def unpack(self, binaryString):") |
| code.append(self.tab*2+"\"\"\"Unpack message") |
| code.append(self.tab*2+"Do not unpack empty array used as placeholder") |
| code.append(self.tab*2+"since they can contain heterogeneous type") |
| code.append(self.tab*2+"\"\"\"") |
| code.append(self.tab*2+"if (len(binaryString) < "+str(structlen)+"):") |
| code.append(self.tab*3+"return binaryString") |
| offset = 0 |
| primPattern = "" |
| primMemberNames = [] |
| for member in struct_in.members: |
| if (isinstance(member, cheader.cprimitive)): |
| #Primitives |
| primPattern += self.__c2py.structmap[member.typename] |
| primMemberNames.append("self."+member.name) |
| else: |
| (primPattern, primMemberNames, offset) = \ |
| self.__codeunpackprimitive(code, offset, primPattern, |
| primMemberNames, prefix) |
| if (isinstance(member, cheader.cstruct)): |
| #Struct |
| code.append(self.tab*2+"self."+member.name+\ |
| ".unpack(binaryString["+str(offset)+":])") |
| pattern = self.__c2py.get_pattern(member) |
| offset += self.__c2py.get_size(prefix+pattern) |
| elif (isinstance(member, cheader.carray) and member.typename == "char"): |
| #String |
| code.append(self.tab*2+"self."+member.name+\ |
| " = binaryString["+str(offset)+":"+\ |
| str(offset+member.size)+"].replace(\"\\0\",\"\")") |
| offset += member.size |
| elif (isinstance(member, cheader.carray) and \ |
| isinstance(member.object, cheader.cprimitive)): |
| #Array of Primitives |
| expandedarr = "" |
| if (member.size != 0): |
| arrpattern = self.__c2py.structmap[member.object.typename]*member.size |
| for x in range(0, member.size): |
| expandedarr += "self."+member.name+"["+\ |
| str(x).strip()+"], " |
| code.append(self.tab*2+"("+expandedarr[:-2]+") = struct.unpack_from(\""+\ |
| prefix+arrpattern+\ |
| "\", binaryString, "+str(offset)+")") |
| offset += struct.calcsize(prefix + arrpattern) |
| elif (isinstance(member, cheader.carray) and \ |
| isinstance(member.object, cheader.cstruct)): |
| #Array of struct |
| astructlen = self.__c2py.get_size("!"+self.__c2py.get_pattern(member.object)) |
| for x in range(0, member.size): |
| code.append(self.tab*2+"self."+member.name+"["+str(x)+"]"+\ |
| ".unpack(binaryString["+str(offset)+":])") |
| offset += astructlen |
| #Clear remaining fields |
| (primPattern, primMemberNames, offset) = \ |
| self.__codeunpackprimitive(code, offset, primPattern, |
| primMemberNames, prefix) |
| code.append(self.tab*2+"return binaryString["+str(structlen)+":]"); |
| return code |
| |
| def __codeunpackprimitive(self, code, offset, primPattern, |
| primMemberNames, prefix): |
| """Return code for unpacking primitives |
| """ |
| if (primPattern != ""): |
| #Clear prior primitives |
| code.append(self.tab*2+"("+str(primMemberNames).replace("'","")[1:-1]+\ |
| ") = struct.unpack_from(\""+\ |
| prefix+primPattern+"\", binaryString, "+str(offset)+")") |
| return ("",[], offset+struct.calcsize(prefix+primPattern)) |
| |