blob: 6a66d7c232a1431dd95e20fc934ed5ed33ee873b [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))
137
138 return code
139
140 def pycode_enum(self, name, enum):
141 """Return Python array for enum
142 """
143 code=[]
144 code.append(name+" = "+str(enum))
145 ev = []
146 for e in enum:
147 v = self.cheader.get_value(e)
148 ev.append(v)
149 code.append(e+"%s= "%_space_to(36,e)+str(v))
150 if GEN_ENUM_VALUES_LIST:
151 code.append(name+"_values = "+str(ev))
152 return code
153
154 def pycode_enum_map(self, name, enum):
155 """Return Python dictionary for enum
156 """
157 code = []
158 code.append(name+"_map = {")
159 first = 1
160 for e in enum:
161 v = self.cheader.get_value(e)
162 if first:
163 prev_e = e
164 prev_v = v
165 first = 0
166 else:
167 code.append(self.tab + "'%s'%s: %s," %
168 (prev_e, _space_to(30, prev_e), prev_v))
169 prev_e = e
170 prev_v = v
171 code.append(self.tab + "'%s'%s: %s" %
172 (prev_e, _space_to(30, prev_e), prev_v))
173 code.append("}")
174 return code
175
176 def pycode_macro(self,name):
177 """Return Python dict for macro
178 """
179 code = []
180 if (self.rules.include_macro(name)):
181 code.append(name+" = "+str(self.cheader.get_value(name)))
182 return code
183
184 def pycode_struct_size(self, name, struct):
185 """Return one liner giving the structure size in bytes
186 """
187 pattern = '!' + self.__c2py.get_pattern(struct)
188 bytes = self.__c2py.get_size(pattern)
189 code = name.upper() + "_BYTES = " + str(bytes)
190 return code
191
192 def pycode_struct(self, struct_in):
193 """Return Python class code given C struct.
194
195 Returns None if struct_in is not cheader.cstruct.
196 Else return list of strings that codes Python class.
197 """
198 if (not isinstance(struct_in, cheader.cstruct)):
199 return None
200
201 code=[]
202 self.__assertcode = []
203 code.extend(self.codeheader(struct_in))
204 code.extend(self.codeinit(struct_in))
205 code.append("")
206 code.extend(self.codeassert(struct_in))
207 code.append("")
208 code.extend(self.codepack(struct_in))
209 code.append("")
210 code.extend(self.codeunpack(struct_in))
211 code.append("")
212 code.extend(self.codelen(struct_in))
213 code.append("")
214 if GEN_OBJ_EQUALITY:
215 code.extend(self.codeeq(struct_in))
216 code.append("")
217 if GEN_OBJ_SHOW:
218 code.extend(self.codeshow(struct_in))
219 code.append("")
220 return code
221
222 def codeheader(self, struct_in):
223 """Return Python code for header
224 """
225 code=[]
226 code.append("class "+struct_in.typename+":")
227 code.append(self.tab+"\"\"\"Automatically generated Python class for "+struct_in.typename)
228 code.append("")
229 code.append(self.tab+"Date "+str(datetime.date.today()))
230 code.append(self.tab+"Created by "+self.__module__+"."+self.__class__.__name__)
231 if IGNORE_OFP_HEADER:
232 code.append(self.tab+"Core structure: Messages do not include ofp_header")
233 if IGNORE_ZERO_ARRAYS:
234 code.append(self.tab+"Does not include var-length arrays")
235 code.append(self.tab+"\"\"\"")
236 return code
237
238 def codeinit(self, struct_in):
239 """Return Python code for init function
240 """
241 code = []
242 code.append(self.tab+"def __init__(self):")
243 code.append(self.tab*2+"\"\"\"Initialize")
244 code.append(self.tab*2+"Declare members and default values")
245 code.append(self.tab*2+"\"\"\"")
246 code.extend(self.codemembers(struct_in,self.tab*2+"self"))
247 return code
248
249 def codemembers(self, struct_in, prepend=""):
250 """Return members of class
251 """
252 code = []
253 for member in struct_in.members:
254 if (isinstance(member, cheader.cstruct)):
255 code.append(prepend+"."+member.name+" = "+member.typename+"()")
256 struct_default = self.rules.get_struct_default(struct_in.typename, member.name)
257 if (struct_default != None):
258 code.append(prepend+struct_default)
259 self.__structassert(member, (prepend+"."+member.name).strip())
260 elif (isinstance(member, cheader.carray)):
261 if (member.typename == "char"):
262 initvalue = "\"\""
263 self.__stringassert(member, (prepend+"."+member.name).strip())
264 else:
265 if (isinstance(member.object, cheader.cprimitive)):
266 initvalue="0"
267 else:
268 initvalue="None"
269 initvalue=(initvalue+",")*member.size
270 initvalue="["+initvalue[:-1]+"]"
271 self.__arrayassert(member, (prepend+"."+member.name).strip())
272 code.append(prepend+"."+member.name+"= "+initvalue)
273 else:
274 code.append(prepend+"."+member.name+" = "+
275 str(self.rules.get_default_value(struct_in.typename, member.name)))
276 return code
277
278 def __structassert(self, cstruct, cstructname):
279 """Return code to check for C array
280 """
281 self.__assertcode.append(self.tab*2+"if(not isinstance("+cstructname+", "+cstruct.typename+")):")
282 self.__assertcode.append(self.tab*3+"return (False, \""+cstructname+" is not class "+cstruct.typename+" as expected.\")")
283
284 def __addassert(self, prefix):
285 code = []
286 code.append(prefix+"if(not self.__assert()[0]):")
287 code.append(prefix+self.tab+"return None")
288 return code
289
290 def __stringassert(self, carray, carrayname):
291 """Return code to check for C array
292 """
293 self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", str)):")
294 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not string as expected.\")")
295 self.__assertcode.append(self.tab*2+"if(len("+carrayname+") > "+str(carray.size)+"):")
296 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
297
298 def __arrayassert(self, carray, carrayname):
299 """Return code to check for C array
300 """
301 if (carray.size == 0):
302 return
303 self.__assertcode.append(self.tab*2+"if(not isinstance("+carrayname+", list)):")
304 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not list as expected.\")")
305 self.__assertcode.append(self.tab*2+"if(len("+carrayname+") != "+str(carray.size)+"):")
306 self.__assertcode.append(self.tab*3+"return (False, \""+carrayname+" is not of size "+str(carray.size)+" as expected.\")")
307
308 def codeassert(self, struct_in):
309 """Return code for sanity checking
310 """
311 code = []
312 code.append(self.tab+"def __assert(self):")
313 code.append(self.tab*2+"\"\"\"Sanity check")
314 code.append(self.tab*2+"\"\"\"")
315 enforce = self.rules.get_enforced_map(struct_in.typename)
316 if (enforce != None):
317 for line in enforce:
318 code.append(self.tab*2+line)
319 code.extend(self.__assertcode)
320 code.append(self.tab*2+"return (True, None)")
321 return code
322
323 def codepack(self, struct_in, prefix="!"):
324 """Return code that pack struct
325 """
326 code = []
327 code.append(self.tab+"def pack(self, assertstruct=True):")
328 code.append(self.tab*2+"\"\"\"Pack message")
329 code.append(self.tab*2+"Packs empty array used as placeholder")
330 code.append(self.tab*2+"\"\"\"")
331 code.append(self.tab*2+"if(assertstruct):")
332 code.extend(self.__addassert(self.tab*3))
333 code.append(self.tab*2+"packed = \"\"")
334 primPattern = ""
335 primMemberNames = []
336 for member in struct_in.members:
337 if (isinstance(member, cheader.cprimitive)):
338 #Primitives
339 primPattern += self.__c2py.structmap[member.typename]
340 primMemberNames.append("self."+member.name)
341 else:
342 (primPattern, primMemberNames) = \
343 self.__codepackprimitive(code, primPattern,
344 primMemberNames, prefix)
345 if (isinstance(member, cheader.cstruct)):
346 #Struct
347 code.append(self.tab*2+"packed += self."+member.name+".pack()")
348 elif (isinstance(member, cheader.carray) and member.typename == "char"):
349 #String
350 code.append(self.tab*2+"packed += self."+member.name+".ljust("+\
351 str(member.size)+",'\\0')")
352 elif (isinstance(member, cheader.carray) and \
353 isinstance(member.object, cheader.cprimitive)):
354 #Array of Primitives
355 expandedarr = ""
356 if (member.size != 0):
357 for x in range(0, member.size):
358 expandedarr += ", self."+member.name+"["+\
359 str(x).strip()+"]"
360 code.append(self.tab*2+"packed += struct.pack(\""+prefix+\
361 self.__c2py.structmap[member.object.typename]*member.size+\
362 "\""+expandedarr+")")
363 else:
364 code.append(self.tab*2+"for i in self."+member.name+":")
365 code.append(self.tab*3+"packed += struct.pack(\""+\
366 prefix+self.__c2py.get_pattern(member.object)+\
367 "\",i)")
368 elif (isinstance(member, cheader.carray) and \
369 isinstance(member.object, cheader.cstruct)):
370 #Array of struct
371 if (member.size != 0):
372 for x in range(0, member.size):
373 code.append(self.tab*2+"packed += self."+member.name+"["+\
374 str(x).strip()+"].pack()")
375 else:
376 code.append(self.tab*2+"for i in self."+member.name+":")
377 code.append(self.tab*3+"packed += i.pack(assertstruct)")
378 #Clear remaining fields
379 (primPattern, primMemberNames) = \
380 self.__codepackprimitive(code, primPattern,
381 primMemberNames, prefix)
382 code.append(self.tab*2+"return packed")
383 return code
384
385 def __codepackprimitive(self, code, primPattern, primMemberNames, prefix):
386 """Return code for packing primitives
387 """
388 if (primPattern != ""):
389 #Clear prior primitives
390 code.append(self.tab*2+"packed += struct.pack(\""+\
391 prefix+primPattern+"\", "+\
392 str(primMemberNames).replace("'","")[1:-1]+")")
393 return ("",[])
394
395 def codelen(self, struct_in):
396 """Return code to return length
397 """
398 pattern = "!" + self.__c2py.get_pattern(struct_in)
399 code = []
400 code.append(self.tab+"def __len__(self):")
401 code.append(self.tab*2+"\"\"\"Return length of message")
402 code.append(self.tab*2+"\"\"\"")
403 code.append(self.tab*2+"l = "+str(self.__c2py.get_size(pattern)))
404 for member in struct_in.members:
405 if (isinstance(member, cheader.carray) and member.size == 0):
406 if (isinstance(member.object, cheader.cstruct)):
407 code.append(self.tab*2+"for i in self."+member.name+":")
408 code.append(self.tab*3+"l += i.length()")
409 else:
410 pattern="!"+self.__c2py.get_pattern(member.object)
411 size=self.__c2py.get_size(pattern)
412 code.append(self.tab*2+"l += len(self."+member.name+")*"+str(size))
413 code.append(self.tab*2+"return l")
414 return code
415
416 def codeeq(self, struct_in):
417 """Return code to return equality comparisons
418 """
419 code = []
420 code.append(self.tab+"def __eq__(self, other):")
421 code.append(self.tab*2+"\"\"\"Return True if self and other have same values")
422 code.append(self.tab*2+"\"\"\"")
423 code.append(self.tab*2+"if type(self) != type(other): return False")
424 for member in struct_in.members:
425 code.append(self.tab*2 + "if self." + member.name + " != other." +
426 member.name + ": return False")
427 code.append(self.tab*2+"return True")
428 code.append("")
429 code.append(self.tab+"def __ne__(self, other): return not self.__eq__(other)")
430 return code
431
432 def codeshow(self, struct_in):
433 """Return code to print basic members of structure
434 """
435 code = []
436 code.append(self.tab+"def show(self, prefix=''):")
437 code.append(self.tab*2+"\"\"\"" + "Print basic members of structure")
438 code.append(self.tab*2+"\"\"\"")
439 for member in struct_in.members:
440 if re.search('pad', member.name):
441 continue
442 elif (isinstance(member, cheader.cstruct)):
443 code.append(self.tab*2 + "print prefix + '" +
444 member.name + ": ' ")
445 code.append(self.tab*2 + "self." + member.name +
446 ".show(prefix + ' ')")
447 elif (isinstance(member, cheader.carray) and
448 not isinstance(member.object, cheader.cprimitive)):
449 code.append(self.tab*2 + "print prefix + '" + member.name +
450 ": ' ")
451 code.append(self.tab*2 + "for obj in self." + member.name + ":")
452 code.append(self.tab*3 + "obj.show(prefix + ' ')")
453 else:
454 code.append(self.tab*2 + "print prefix + '" + member.name +
455 ": ' + str(self." + member.name + ")")
456 return code
457
458 def codeunpack(self, struct_in, prefix="!"):
459 """Return code that unpack struct
460 """
461 pattern = self.__c2py.get_pattern(struct_in)
462 structlen = self.__c2py.get_size(prefix + pattern)
463 code = []
464 code.append(self.tab+"def unpack(self, binaryString):")
465 code.append(self.tab*2+"\"\"\"Unpack message")
466 code.append(self.tab*2+"Do not unpack empty array used as placeholder")
467 code.append(self.tab*2+"since they can contain heterogeneous type")
468 code.append(self.tab*2+"\"\"\"")
469 code.append(self.tab*2+"if (len(binaryString) < "+str(structlen)+"):")
470 code.append(self.tab*3+"return binaryString")
471 offset = 0
472 primPattern = ""
473 primMemberNames = []
474 for member in struct_in.members:
475 if (isinstance(member, cheader.cprimitive)):
476 #Primitives
477 primPattern += self.__c2py.structmap[member.typename]
478 primMemberNames.append("self."+member.name)
479 else:
480 (primPattern, primMemberNames, offset) = \
481 self.__codeunpackprimitive(code, offset, primPattern,
482 primMemberNames, prefix)
483 if (isinstance(member, cheader.cstruct)):
484 #Struct
485 code.append(self.tab*2+"self."+member.name+\
486 ".unpack(binaryString["+str(offset)+":])")
487 pattern = self.__c2py.get_pattern(member)
488 offset += self.__c2py.get_size(prefix+pattern)
489 elif (isinstance(member, cheader.carray) and member.typename == "char"):
490 #String
491 code.append(self.tab*2+"self."+member.name+\
492 " = binaryString["+str(offset)+":"+\
493 str(offset+member.size)+"].replace(\"\\0\",\"\")")
494 offset += member.size
495 elif (isinstance(member, cheader.carray) and \
496 isinstance(member.object, cheader.cprimitive)):
497 #Array of Primitives
498 expandedarr = ""
499 if (member.size != 0):
500 arrpattern = self.__c2py.structmap[member.object.typename]*member.size
501 for x in range(0, member.size):
502 expandedarr += "self."+member.name+"["+\
503 str(x).strip()+"], "
504 code.append(self.tab*2+"("+expandedarr[:-2]+") = struct.unpack_from(\""+\
505 prefix+arrpattern+\
506 "\", binaryString, "+str(offset)+")")
507 offset += struct.calcsize(prefix + arrpattern)
508 elif (isinstance(member, cheader.carray) and \
509 isinstance(member.object, cheader.cstruct)):
510 #Array of struct
511 astructlen = self.__c2py.get_size("!"+self.__c2py.get_pattern(member.object))
512 for x in range(0, member.size):
513 code.append(self.tab*2+"self."+member.name+"["+str(x)+"]"+\
514 ".unpack(binaryString["+str(offset)+":])")
515 offset += astructlen
516 #Clear remaining fields
517 (primPattern, primMemberNames, offset) = \
518 self.__codeunpackprimitive(code, offset, primPattern,
519 primMemberNames, prefix)
520 code.append(self.tab*2+"return binaryString["+str(structlen)+":]");
521 return code
522
523 def __codeunpackprimitive(self, code, offset, primPattern,
524 primMemberNames, prefix):
525 """Return code for unpacking primitives
526 """
527 if (primPattern != ""):
528 #Clear prior primitives
529 code.append(self.tab*2+"("+str(primMemberNames).replace("'","")[1:-1]+\
530 ") = struct.unpack_from(\""+\
531 prefix+primPattern+"\", binaryString, "+str(offset)+")")
532 return ("",[], offset+struct.calcsize(prefix+primPattern))
533