blob: 22b521441a4cf824bcb056dff8a0899fefc1974a [file] [log] [blame]
Dan Talaycof75360a2010-02-05 22:22:54 -08001"""This module generate Python code for C structs.
2
3Date January 2010
4Created by ykk
5"""
Dan Talayco11e598b2010-03-09 21:46:19 -08006import sys
Dan Talaycof75360a2010-02-05 22:22:54 -08007import cheader
8import c2py
9import datetime
10import struct
11import re
12from config import *
13
14def _space_to(n, str):
15 """
16 Generate a string of spaces to achieve width n given string str
17 If length of str >= n, return one space
18 """
19 spaces = n - len(str)
20 if spaces > 0:
21 return " " * spaces
22 return " "
23
24class rules:
25 """Class that specify rules for pythonization
26
27 Date January 2010
28 Created by ykk
29 """
30 def __init__(self):
31 """Initialize rules
32 """
33 ##Default values for members
34 self.default_values = {}
35 #Default values for struct
36 self.struct_default = {}
37 ##What is a tab
38 self.tab = " "
39 ##Macros to exclude
40 self.excluded_macros = []
41 ##Enforce mapping
42 self.enforced_maps = {}
43
44 def get_enforced_map(self, structname):
45 """Get code to enforce mapping
46 """
47 code = []
48 try:
49 mapping = self.enforced_maps[structname]
50 except KeyError:
51 return None
52 for (x,xlist) in mapping:
53 code.append("if (not (self."+x+" in "+xlist+")):")
54 code.append(self.tab+"return (False, \""+x+" must have values from "+xlist+"\")")
55 return code
56
57
58 def get_struct_default(self, structname, fieldname):
59 """Get code to set defaults for member struct
60 """
61 try:
62 return "."+fieldname+self.struct_default[(structname, fieldname)]
63 except KeyError:
64 return None
65
66 def get_default_value(self, structname, fieldname):
67 """Get default value for struct's field
68 """
69 try:
70 return self.default_values[(structname, fieldname)]
71 except KeyError:
72 return 0
73
74 def include_macro(self, name):
75 """Check if macro should be included
76 """
77 return not (name in self.excluded_macros)
78
79class pythonizer:
80 """Class that pythonize C structures
81
82 Date January 2010
83 Created by ykk
84 """
85 def __init__(self, cheaderfile, pyrules = None, tab=" "):
86 """Initialize
87 """
88 ##Rules
89 if (pyrules == None):
90 self.rules = rules()
91 else:
92 self.rules = pyrules
93 ##What is a tab (same as rules)
94 self.tab = str(tab)
95 self.rules.tab = self.tab
96 ##Reference to C header file
97 self.cheader = cheaderfile
98 ##Reference to cstruct2py
99 self.__c2py = c2py.cstruct2py()
100 ##Code for assertion
101 self.__assertcode = []
102
103 def pycode(self,preamble=None):
104 """Return pythonized code
105 """
106 code = []
107 code.append("import struct")
108 code.append("")
109 if (preamble != None):
110 fileRef = open(preamble,"r")
111 for l in fileRef:
112 code.append(l[:-1])
113 fileRef.close()
114 code.append("# Structure definitions")
115 for name,struct in self.cheader.structs.items():
116 code.extend(self.pycode_struct(struct))
117 code.append("")
118 code.append("# Enumerated type definitions")
119 for name,enum in self.cheader.enums.items():
120 code.extend(self.pycode_enum(name,enum))
121 if GEN_ENUM_DICTIONARY:
122 code.extend(self.pycode_enum_map(name,enum))
123 code.append("")
124 code.append("# Values from macro definitions")
125 for name,macro in self.cheader.macros.items():
126 code.extend(self.pycode_macro(name))
127 code.append("")
128 code.append("# Basic structure size definitions.")
129 if IGNORE_OFP_HEADER:
130 code.append("# Does not include ofp_header members.")
131 if IGNORE_ZERO_ARRAYS:
132 code.append("# Does not include variable length arrays.")
133 struct_keys = self.cheader.structs.keys()
134 struct_keys.sort()
135 for name in struct_keys:
136 struct = self.cheader.structs[name]
137 code.append(self.pycode_struct_size(name, struct))
Dan Talaycoac1cb812010-02-06 20:34:18 -0800138 if GEN_AUX_INFO:
139 self.gen_struct_map()
Dan Talaycof75360a2010-02-05 22:22:54 -0800140
141 return code
142
143 def pycode_enum(self, name, enum):
144 """Return Python array for enum
145 """
146 code=[]
147 code.append(name+" = "+str(enum))
148 ev = []
149 for e in enum:
150 v = self.cheader.get_value(e)
151 ev.append(v)
152 code.append(e+"%s= "%_space_to(36,e)+str(v))
153 if GEN_ENUM_VALUES_LIST:
154 code.append(name+"_values = "+str(ev))
155 return code
156
157 def pycode_enum_map(self, name, enum):
158 """Return Python dictionary for enum
159 """
160 code = []
161 code.append(name+"_map = {")
162 first = 1
163 for e in enum:
164 v = self.cheader.get_value(e)
165 if first:
166 prev_e = e
167 prev_v = v
168 first = 0
169 else:
Dan Talayco5c99b322010-02-13 21:52:07 -0800170 code.append(self.tab + "%s%s: '%s'," %
171 (prev_v, _space_to(32, str(prev_v)), prev_e))
Dan Talaycof75360a2010-02-05 22:22:54 -0800172 prev_e = e
173 prev_v = v
Dan Talayco5c99b322010-02-13 21:52:07 -0800174 code.append(self.tab + "%s%s: '%s'" %
175 (prev_v, _space_to(32, str(prev_v)), prev_e))
Dan Talaycof75360a2010-02-05 22:22:54 -0800176 code.append("}")
177 return code
178
179 def pycode_macro(self,name):
180 """Return Python dict for macro
181 """
182 code = []
183 if (self.rules.include_macro(name)):
184 code.append(name+" = "+str(self.cheader.get_value(name)))
185 return code
186
187 def pycode_struct_size(self, name, struct):
188 """Return one liner giving the structure size in bytes
189 """
190 pattern = '!' + self.__c2py.get_pattern(struct)
191 bytes = self.__c2py.get_size(pattern)
192 code = name.upper() + "_BYTES = " + str(bytes)
193 return code
194
195 def pycode_struct(self, struct_in):
196 """Return Python class code given C struct.
197
198 Returns None if struct_in is not cheader.cstruct.
199 Else return list of strings that codes Python class.
200 """
201 if (not isinstance(struct_in, cheader.cstruct)):
202 return None
203
204 code=[]
205 self.__assertcode = []
206 code.extend(self.codeheader(struct_in))
207 code.extend(self.codeinit(struct_in))
208 code.append("")
209 code.extend(self.codeassert(struct_in))
210 code.append("")
211 code.extend(self.codepack(struct_in))
212 code.append("")
213 code.extend(self.codeunpack(struct_in))
214 code.append("")
215 code.extend(self.codelen(struct_in))
216 code.append("")
217 if GEN_OBJ_EQUALITY:
218 code.extend(self.codeeq(struct_in))
219 code.append("")
220 if GEN_OBJ_SHOW:
221 code.extend(self.codeshow(struct_in))
222 code.append("")
223 return code
224
225 def codeheader(self, struct_in):
226 """Return Python code for header
227 """
228 code=[]
229 code.append("class "+struct_in.typename+":")
230 code.append(self.tab+"\"\"\"Automatically generated Python class for "+struct_in.typename)
231 code.append("")
232 code.append(self.tab+"Date "+str(datetime.date.today()))
233 code.append(self.tab+"Created by "+self.__module__+"."+self.__class__.__name__)
234 if IGNORE_OFP_HEADER:
235 code.append(self.tab+"Core structure: Messages do not include ofp_header")
236 if IGNORE_ZERO_ARRAYS:
237 code.append(self.tab+"Does not include var-length arrays")
238 code.append(self.tab+"\"\"\"")
239 return code
240
241 def codeinit(self, struct_in):
242 """Return Python code for init function
243 """
244 code = []
245 code.append(self.tab+"def __init__(self):")
246 code.append(self.tab*2+"\"\"\"Initialize")
247 code.append(self.tab*2+"Declare members and default values")
248 code.append(self.tab*2+"\"\"\"")
249 code.extend(self.codemembers(struct_in,self.tab*2+"self"))
250 return code
251
252 def codemembers(self, struct_in, prepend=""):
253 """Return members of class
254 """
255 code = []
256 for member in struct_in.members:
257 if (isinstance(member, cheader.cstruct)):
258 code.append(prepend+"."+member.name+" = "+member.typename+"()")
259 struct_default = self.rules.get_struct_default(struct_in.typename, member.name)
260 if (struct_default != None):
261 code.append(prepend+struct_default)
262 self.__structassert(member, (prepend+"."+member.name).strip())
263 elif (isinstance(member, cheader.carray)):
264 if (member.typename == "char"):
265 initvalue = "\"\""
266 self.__stringassert(member, (prepend+"."+member.name).strip())
267 else:
268 if (isinstance(member.object, cheader.cprimitive)):
269 initvalue="0"
270 else:
271 initvalue="None"
272 initvalue=(initvalue+",")*member.size
273 initvalue="["+initvalue[:-1]+"]"
274 self.__arrayassert(member, (prepend+"."+member.name).strip())
275 code.append(prepend+"."+member.name+"= "+initvalue)
276 else:
277 code.append(prepend+"."+member.name+" = "+
278 str(self.rules.get_default_value(struct_in.typename, member.name)))
279 return code
280
Dan Talayco11e598b2010-03-09 21:46:19 -0800281 def gen_struct_map(self, file=None):
282 if not file:
283 file = sys.stdout
284 print >> file
285 print >> file, "# Class to array member map"
286 print >> file, "class_to_members_map = {"
Dan Talaycoac1cb812010-02-06 20:34:18 -0800287 for name, struct in self.cheader.structs.items():
288 if not len(struct.members):
289 continue
290 s = " '" + name + "'"
Dan Talayco11e598b2010-03-09 21:46:19 -0800291 print >> file, s + _space_to(36, s) + ": ["
Dan Talaycoac1cb812010-02-06 20:34:18 -0800292 prev = None
293 for member in struct.members:
294 if re.search('pad', member.name):
295 continue
296 if prev:
297 print _space_to(39, "") + "'" + prev + "',"
298 prev = member.name
Dan Talayco11e598b2010-03-09 21:46:19 -0800299 print >> file, _space_to(39, "") + "'" + prev + "'"
300 print >> file, _space_to(38, "") + "],"
301 print >> file, " '_ignore' : []"
302 print >> file, "}"
Dan Talaycoac1cb812010-02-06 20:34:18 -0800303
Dan Talaycof75360a2010-02-05 22:22:54 -0800304 def __structassert(self, cstruct, cstructname):
305 """Return code to check for C array
306 """
307 self.__assertcode.append(self.tab*2+"if(not isinstance("+cstructname+", "+cstruct.typename+")):")
308 self.__assertcode.append(self.tab*3+"return (False, \""+cstructname+" is not class "+cstruct.typename+" as expected.\")")
309
310 def __addassert(self, prefix):
311 code = []
312 code.append(prefix+"if(not self.__assert()[0]):")
313 code.append(prefix+self.tab+"return None")
314 return code
315
316 def __stringassert(self, carray, carrayname):
317 """Return code to check for C array
318 """
319 self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", str)):")
320 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not string as expected.\")")
321 self.__assertcode.append(self.tab*2+"if(len("+carrayname+") > "+str(carray.size)+"):")
322 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
323
324 def __arrayassert(self, carray, carrayname):
325 """Return code to check for C array
326 """
327 if (carray.size == 0):
328 return
329 self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", list)):")
330 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not list as expected.\")")
331 self.__assertcode.append(self.tab*2+"if(len("+carrayname+") != "+str(carray.size)+"):")
332 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
333
334 def codeassert(self, struct_in):
335 """Return code for sanity checking
336 """
337 code = []
338 code.append(self.tab+"def __assert(self):")
339 code.append(self.tab*2+"\"\"\"Sanity check")
340 code.append(self.tab*2+"\"\"\"")
341 enforce = self.rules.get_enforced_map(struct_in.typename)
342 if (enforce != None):
343 for line in enforce:
344 code.append(self.tab*2+line)
345 code.extend(self.__assertcode)
346 code.append(self.tab*2+"return (True, None)")
347 return code
348
349 def codepack(self, struct_in, prefix="!"):
350 """Return code that pack struct
351 """
352 code = []
353 code.append(self.tab+"def pack(self, assertstruct=True):")
354 code.append(self.tab*2+"\"\"\"Pack message")
355 code.append(self.tab*2+"Packs empty array used as placeholder")
356 code.append(self.tab*2+"\"\"\"")
357 code.append(self.tab*2+"if(assertstruct):")
358 code.extend(self.__addassert(self.tab*3))
359 code.append(self.tab*2+"packed = \"\"")
360 primPattern = ""
361 primMemberNames = []
362 for member in struct_in.members:
363 if (isinstance(member, cheader.cprimitive)):
364 #Primitives
365 primPattern += self.__c2py.structmap[member.typename]
366 primMemberNames.append("self."+member.name)
367 else:
368 (primPattern, primMemberNames) = \
369 self.__codepackprimitive(code, primPattern,
370 primMemberNames, prefix)
371 if (isinstance(member, cheader.cstruct)):
372 #Struct
373 code.append(self.tab*2+"packed += self."+member.name+".pack()")
374 elif (isinstance(member, cheader.carray) and member.typename == "char"):
375 #String
376 code.append(self.tab*2+"packed += self."+member.name+".ljust("+\
377 str(member.size)+",'\\0')")
378 elif (isinstance(member, cheader.carray) and \
379 isinstance(member.object, cheader.cprimitive)):
380 #Array of Primitives
381 expandedarr = ""
382 if (member.size != 0):
383 for x in range(0, member.size):
384 expandedarr += ", self."+member.name+"["+\
385 str(x).strip()+"]"
386 code.append(self.tab*2+"packed += struct.pack(\""+prefix+\
387 self.__c2py.structmap[member.object.typename]*member.size+\
388 "\""+expandedarr+")")
389 else:
390 code.append(self.tab*2+"for i in self."+member.name+":")
391 code.append(self.tab*3+"packed += struct.pack(\""+\
392 prefix+self.__c2py.get_pattern(member.object)+\
393 "\",i)")
394 elif (isinstance(member, cheader.carray) and \
395 isinstance(member.object, cheader.cstruct)):
396 #Array of struct
397 if (member.size != 0):
398 for x in range(0, member.size):
399 code.append(self.tab*2+"packed += self."+member.name+"["+\
400 str(x).strip()+"].pack()")
401 else:
402 code.append(self.tab*2+"for i in self."+member.name+":")
403 code.append(self.tab*3+"packed += i.pack(assertstruct)")
404 #Clear remaining fields
405 (primPattern, primMemberNames) = \
406 self.__codepackprimitive(code, primPattern,
407 primMemberNames, prefix)
408 code.append(self.tab*2+"return packed")
409 return code
410
411 def __codepackprimitive(self, code, primPattern, primMemberNames, prefix):
412 """Return code for packing primitives
413 """
414 if (primPattern != ""):
415 #Clear prior primitives
416 code.append(self.tab*2+"packed += struct.pack(\""+\
417 prefix+primPattern+"\", "+\
418 str(primMemberNames).replace("'","")[1:-1]+")")
419 return ("",[])
420
421 def codelen(self, struct_in):
422 """Return code to return length
423 """
424 pattern = "!" + self.__c2py.get_pattern(struct_in)
425 code = []
426 code.append(self.tab+"def __len__(self):")
427 code.append(self.tab*2+"\"\"\"Return length of message")
428 code.append(self.tab*2+"\"\"\"")
429 code.append(self.tab*2+"l = "+str(self.__c2py.get_size(pattern)))
430 for member in struct_in.members:
431 if (isinstance(member, cheader.carray) and member.size == 0):
432 if (isinstance(member.object, cheader.cstruct)):
433 code.append(self.tab*2+"for i in self."+member.name+":")
Dan Talayco10fe5c22010-02-07 23:05:29 -0800434 # FIXME: Is this right? Doesn't seem to be called
Dan Talaycof75360a2010-02-05 22:22:54 -0800435 code.append(self.tab*3+"l += i.length()")
436 else:
437 pattern="!"+self.__c2py.get_pattern(member.object)
438 size=self.__c2py.get_size(pattern)
439 code.append(self.tab*2+"l += len(self."+member.name+")*"+str(size))
440 code.append(self.tab*2+"return l")
441 return code
442
443 def codeeq(self, struct_in):
444 """Return code to return equality comparisons
445 """
446 code = []
447 code.append(self.tab+"def __eq__(self, other):")
448 code.append(self.tab*2+"\"\"\"Return True if self and other have same values")
449 code.append(self.tab*2+"\"\"\"")
450 code.append(self.tab*2+"if type(self) != type(other): return False")
451 for member in struct_in.members:
452 code.append(self.tab*2 + "if self." + member.name + " != other." +
453 member.name + ": return False")
454 code.append(self.tab*2+"return True")
455 code.append("")
456 code.append(self.tab+"def __ne__(self, other): return not self.__eq__(other)")
457 return code
458
459 def codeshow(self, struct_in):
460 """Return code to print basic members of structure
461 """
462 code = []
463 code.append(self.tab+"def show(self, prefix=''):")
Dan Talayco11e598b2010-03-09 21:46:19 -0800464 code.append(self.tab*2+"\"\"\"" + "Generate string showing basic members of structure")
Dan Talaycof75360a2010-02-05 22:22:54 -0800465 code.append(self.tab*2+"\"\"\"")
Dan Talayco11e598b2010-03-09 21:46:19 -0800466 code.append(self.tab*2+"outstr = ''")
Dan Talaycof75360a2010-02-05 22:22:54 -0800467 for member in struct_in.members:
468 if re.search('pad', member.name):
469 continue
470 elif (isinstance(member, cheader.cstruct)):
Dan Talayco11e598b2010-03-09 21:46:19 -0800471 code.append(self.tab*2 + "outstr += prefix + '" +
472 member.name + ": \\n' ")
Dan Talayco7f0d5452010-06-08 10:33:30 -0700473 code.append(self.tab*2 + "outstr += self." + member.name +
Dan Talaycof75360a2010-02-05 22:22:54 -0800474 ".show(prefix + ' ')")
475 elif (isinstance(member, cheader.carray) and
476 not isinstance(member.object, cheader.cprimitive)):
Dan Talayco11e598b2010-03-09 21:46:19 -0800477 code.append(self.tab*2 + "outstr += prefix + '" + member.name +
478 ": \\n' ")
Dan Talaycof75360a2010-02-05 22:22:54 -0800479 code.append(self.tab*2 + "for obj in self." + member.name + ":")
Dan Talayco11e598b2010-03-09 21:46:19 -0800480 code.append(self.tab*3 + "outstr += obj.show(prefix + ' ')")
Dan Talaycof75360a2010-02-05 22:22:54 -0800481 else:
Dan Talayco11e598b2010-03-09 21:46:19 -0800482 code.append(self.tab*2 + "outstr += prefix + '" + member.name +
483 ": ' + str(self." + member.name + ") + '\\n'")
484 code.append(self.tab*2+"return outstr")
Dan Talaycof75360a2010-02-05 22:22:54 -0800485 return code
486
487 def codeunpack(self, struct_in, prefix="!"):
488 """Return code that unpack struct
489 """
490 pattern = self.__c2py.get_pattern(struct_in)
491 structlen = self.__c2py.get_size(prefix + pattern)
492 code = []
493 code.append(self.tab+"def unpack(self, binaryString):")
494 code.append(self.tab*2+"\"\"\"Unpack message")
495 code.append(self.tab*2+"Do not unpack empty array used as placeholder")
496 code.append(self.tab*2+"since they can contain heterogeneous type")
497 code.append(self.tab*2+"\"\"\"")
498 code.append(self.tab*2+"if (len(binaryString) < "+str(structlen)+"):")
499 code.append(self.tab*3+"return binaryString")
500 offset = 0
501 primPattern = ""
502 primMemberNames = []
503 for member in struct_in.members:
504 if (isinstance(member, cheader.cprimitive)):
505 #Primitives
506 primPattern += self.__c2py.structmap[member.typename]
507 primMemberNames.append("self."+member.name)
508 else:
509 (primPattern, primMemberNames, offset) = \
510 self.__codeunpackprimitive(code, offset, primPattern,
511 primMemberNames, prefix)
512 if (isinstance(member, cheader.cstruct)):
513 #Struct
514 code.append(self.tab*2+"self."+member.name+\
515 ".unpack(binaryString["+str(offset)+":])")
516 pattern = self.__c2py.get_pattern(member)
517 offset += self.__c2py.get_size(prefix+pattern)
518 elif (isinstance(member, cheader.carray) and member.typename == "char"):
519 #String
520 code.append(self.tab*2+"self."+member.name+\
521 " = binaryString["+str(offset)+":"+\
522 str(offset+member.size)+"].replace(\"\\0\",\"\")")
523 offset += member.size
524 elif (isinstance(member, cheader.carray) and \
525 isinstance(member.object, cheader.cprimitive)):
526 #Array of Primitives
527 expandedarr = ""
528 if (member.size != 0):
529 arrpattern = self.__c2py.structmap[member.object.typename]*member.size
530 for x in range(0, member.size):
531 expandedarr += "self."+member.name+"["+\
532 str(x).strip()+"], "
Dan Talayco6f223c82010-03-12 13:56:27 -0800533 code.append(self.tab*2 + "fmt = '" + prefix+arrpattern + "'")
534 code.append(self.tab*2 + "start = " + str(offset))
535 code.append(self.tab*2 + "end = start + struct.calcsize(fmt)")
536 code.append(self.tab*2 + "("+expandedarr[:-2] +
537 ") = struct.unpack(fmt, binaryString[start:end])")
Dan Talaycof75360a2010-02-05 22:22:54 -0800538 offset += struct.calcsize(prefix + arrpattern)
539 elif (isinstance(member, cheader.carray) and \
540 isinstance(member.object, cheader.cstruct)):
541 #Array of struct
542 astructlen = self.__c2py.get_size("!"+self.__c2py.get_pattern(member.object))
543 for x in range(0, member.size):
544 code.append(self.tab*2+"self."+member.name+"["+str(x)+"]"+\
545 ".unpack(binaryString["+str(offset)+":])")
546 offset += astructlen
547 #Clear remaining fields
548 (primPattern, primMemberNames, offset) = \
549 self.__codeunpackprimitive(code, offset, primPattern,
550 primMemberNames, prefix)
551 code.append(self.tab*2+"return binaryString["+str(structlen)+":]");
552 return code
553
554 def __codeunpackprimitive(self, code, offset, primPattern,
555 primMemberNames, prefix):
556 """Return code for unpacking primitives
557 """
558 if (primPattern != ""):
559 #Clear prior primitives
Dan Talayco6f223c82010-03-12 13:56:27 -0800560 code.append(self.tab*2 + "fmt = '" + prefix + primPattern + "'")
561 code.append(self.tab*2 + "start = " + str(offset))
562 code.append(self.tab*2 + "end = start + struct.calcsize(fmt)")
Dan Talayco10fe5c22010-02-07 23:05:29 -0800563 if len(primMemberNames) == 1:
564 code.append(self.tab*2 + "(" + str(primMemberNames[0]) +
Dan Talayco6f223c82010-03-12 13:56:27 -0800565 ",) = struct.unpack(fmt, binaryString[start:end])")
Dan Talayco10fe5c22010-02-07 23:05:29 -0800566 else:
567 code.append(self.tab*2+"("+str(primMemberNames).replace("'","")[1:-1]+
Dan Talayco6f223c82010-03-12 13:56:27 -0800568 ") = struct.unpack(fmt, binaryString[start:end])")
Dan Talayco10fe5c22010-02-07 23:05:29 -0800569
Dan Talaycof75360a2010-02-05 22:22:54 -0800570 return ("",[], offset+struct.calcsize(prefix+primPattern))
571