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