1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2006-09-24 14:01:41 -0400 (Sun, 24 Sep 2006) $'.split()[1]
5 __author__ = 'Kurt Schwehr'
6
7 __doc__='''
8
9 Tools to generate python code to serialize/deserialize messages
10 between python and ais binary. Trying to be as inline as possible, so
11 no XML on the fly like in ais-py.
12
13 serialize: python to ais binary
14 deserialize: ais binary to python
15
16 The generated code uses translators.py, binary.py, and aisstring.py
17 which should be packaged with the resulting files.
18
19 @requires: U{lxml<http://codespeak.net/lxml/>}
20 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3
21 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>}
22
23 @author: U{'''+__author__+'''<http://xenon.stanford.edu/~schwehr/>}
24 @version: ''' + __version__ +'''
25 @copyright: 2006
26 @var __date__: Date of last svn commit
27 @undocumented: __version__ __author__ __doc__ myparser
28 @since: 2006-Sep-24
29 @status: under development
30 @organization: U{CCOM<http://ccom.unh.edu/>}
31 @license: Restricted while in development to NOAA and USCG.
32
33 @todo: add a link to generated doc string to bring up the html for the pretty version
34
35 @bug: NOT complete
36 '''
37
38 import sys, os
39 from decimal import Decimal
40 from lxml import etree
41
43 '''
44 Try to suggest a type name if one did not work.
45
46 @param printout: if true, write a suggestion to stdout.
47
48 >>> suggestType('myFieldName','unsigned int')
49 Recommend switching "unsigned int" to "uint" for field "myFieldName"
50 'uint'
51
52 >>> suggestType('JohnWarfon','yoyodyne')
53 Sorry! No recommendation available for bad type "yoyodyne" for field "JohnWarfon"
54 '''
55 newType = None
56 if curType.lower()=='unsigned int':
57 newType = 'uint'
58 elif curType.lower()=='unsigned decimal':
59 newType = 'udecimal'
60
61 if printout:
62 if None != newType:
63 print 'Recommend switching "'+curType+'" to "'+newType+'" for field "'+name+'"'
64 else:
65 print 'Sorry! No recommendation available for bad type "'+curType+'" for field "'+name+'"'
66 return newType
67
68
70 '''
71 @return: true if the tag a sub tag with name subtag
72 '''
73 if 0<len(et.xpath(subtag)): return True
74 return False
75
76
77
79 '''
80 Write the doc string header for the message file
81
82 param o: Open output file to write code to.
83 param msgET: element tree for the ais message definition.
84 Must be pre-expanded with the expandais.py command.
85 '''
86 import datetime
87 d = datetime.datetime.utcnow()
88 dateStr = str(d.year)+'-'+("%02d" %d.month)+'-'+("%02d"%d.day)
89
90
91
92
93
94 o.write('''#!/usr/bin/env python
95
96 __version__ = '$Revision: 4791 $'.split()[1]
97 __date__ = '$Da'''+'''te: '''+dateStr+''' $'.split()[1]
98 __author__ = 'xmlbinmsg'
99
100 __doc__=\'\'\'
101
102 Autogenerated python functions to serialize/deserialize binary messages.
103
104 Generated by: '''+__file__+'''
105
106 Need to then wrap these functions with the outer AIS packet and then
107 convert the whole binary blob to a NMEA string. Those functions are
108 not currently provided in this file.
109
110 serialize: python to ais binary
111 deserialize: ais binary to python
112
113 The generated code uses translators.py, binary.py, and aisstring.py
114 which should be packaged with the resulting files.
115
116 ''')
117
118 o.write('''
119 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3
120 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>}
121
122 @author: \'\'\'+__author__+\'\'\'
123 @version: \'\'\' + __version__ +\'\'\'
124 @var __date__: Date of last svn commit
125 @undocumented: __version__ __author__ __doc__ myparser
126 @status: under development
127 @license: Generated code has no license
128 \'\'\'
129
130 import sys
131 from decimal import Decimal
132 from BitVector import BitVector
133
134 import binary, aisstring
135
136 TrueBV = BitVector(bitstring="1")
137 "Why always rebuild the True bit? This should speed things up a bunch"
138 FalseBV = BitVector(bitstring="0")
139 "Why always rebuild the False bit? This should speed things up a bunch"
140
141
142 ''')
143 return
144
146 '''
147 @param infile: xml ais binary message definition file
148 @param outfile: where to dump the python code
149 '''
150
151 aisMsgsET = etree.parse(infile).getroot()
152
153 o = file(outfile,'w')
154 os.chmod(outfile,0755)
155
156 writeBeginning(o)
157
158 for msgET in aisMsgsET:
159 if msgET.tag != 'message': continue
160 print msgET.tag, msgET.attrib['name']
161
162 if len(msgET.xpath('include-struct')) > 0:
163 sys.exit("ERROR: cannot handle xml that still has include-struct tags.\n Please use expandais.py.")
164
165 buildEncode(o,msgET)
166 buildDecode(o,msgET)
167 buildPrint(o,msgET)
168
169 o.write('\n\n######################################################################\n')
170 o.write('# UNIT TESTING\n')
171 o.write('######################################################################\n')
172 o.write('import unittest\n')
173
174 for msgET in aisMsgsET:
175 if msgET.tag != 'message': continue
176 print 'Building unit tests for message ...', msgET.attrib['name']
177
178 buildUnitTest(o,msgET)
179
180 buildMain(o)
181 return
182
183
184
185
186
188 '''Get the maximum string length of any field name'''
189 maxStrLen=0
190 for field in msgET.xpath('field'):
191 fieldLen = len(field.attrib['name'])
192 if fieldLen>maxStrLen: maxStrLen = fieldLen
193 return maxStrLen
194
196 '''Pad a string out to the length requested with spaces out to the right'''
197 return aStr + ' '*(strlen-len(aStr))
198
200 '''
201 Write a simple in order print for the resulting dictionary.
202
203 param o: open file where resulting code will be written
204 param msgET: Element Tree starting at a message node
205 '''
206 assert(msgET.tag=='message')
207 name = msgET.attrib['name']
208
209 print 'Generating print for',name
210 funcName = name+'Print'
211 o.write('def '+funcName+'(params, out=sys.stdout):\n')
212
213
214
215 o.write("\t'''Print a "+name+" message to stdout.\n\n")
216 o.write('\tFields in params:\n')
217 for field in msgET.xpath('field'):
218 desc = field[0].text.replace('\n',' ')
219 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc)
220 if len(field.xpath("required")) == 1:
221 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")')
222
223 o.write('\n')
224 o.write('\t@param params: Dictionary of field names/values. \n')
225 o.write('\t@param out: File like object to write to\n')
226
227 o.write("\t@rtype: stdout\n")
228 o.write("\t@return: text to out\n")
229 o.write("\t'''\n\n")
230
231
232
233
234 o.write('\tout.write("'+name+':\\n")\n')
235
236 if verbose: print 'number of fields = ', len(msgET.xpath('field'))
237
238
239 maxFieldLen = 1 + getMaxFieldNameLen(msgET)
240
241
242 for field in msgET.xpath('field'):
243 name = field.attrib['name']
244 type = field.attrib['type']
245 numbits = int(field.attrib['numberofbits'])
246 required = None;
247 if hasSubtag(field,'required'):
248 required = field.xpath('required')[0].text
249
250 unavailable=None;
251 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
252 arraylen=1
253 if 'arraylength' in field.attrib:
254 arraylen=int(field.attrib['arraylength'])
255 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
256 else:
257 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
258
259 if 1==arraylen:
260 o.write('\tout.write("\t'+padStrRight(name+':',maxFieldLen)+' "+str(+params[\''+name+'\'])+"\\n")\n')
261 else:
262 print 'FIX: handle arrays in the buildPrint func'
263
264 o.write('\n\treturn # Nothing to return\n\n')
265
266
267
268
269 -def encodeBool(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
270 '''
271 Build the encoder for boolean variables
272 @type o: file like obj
273 @param o: where write the code
274 @type name: str
275 @param name: field name
276 @type type: str
277 @param type: bool, etc.
278 @type numbits: int = 1
279 @param numbits: How many bits per unit datum (must be 1 for bools)
280 @type required: bool or None
281 @param required: If not None, then the value must be set to this.
282 @type arraylen: int >= 1
283 @param arraylen: many bools will there be? FIX: handle variable
284 @type unavailable: bool or None
285 @param unavailable: the default value to use if none given (if not None)
286 @return: None
287 '''
288
289 if verbose: print 'bool encode',name,': unvail=',unavailable
290
291 assert type.lower()=='bool'
292 assert numbits==1
293 if arraylen != 1: assert False
294 if verbose: o.write('\t### FIELD: '+name+' (type=bool)\n')
295 if None != required:
296 assert type(required)==bool
297 if required: o.write('\t\tbvList.append(TrueBV)\n')
298 else: o.write('\t\tbvList.append(FalseBV)\n')
299 if verbose: o.write('\n')
300 return
301
302 if None==unavailable:
303 o.write('\tif params["'+name+'"]: bvList.append(TrueBV)\n')
304 o.write('\telse: bvList.append(FalseBV)\n')
305 else:
306 assert type(unavailable)==bool
307 o.write("\tif '"+name+"' in params:\n")
308 o.write('\t\tif params["'+name+'"]: bvList.append(TrueBV)\n')
309 o.write('\t\telse: bvList.append(FalseBV)\n')
310 o.write('\telse:\n')
311 if unavailable: o.write('\t\tbvList.append(TrueBV)\n')
312 else: o.write('\t\tbvList.append(FalseBV)\n')
313 if verbose: o.write('\n')
314
315 -def encodeUInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
316 '''
317 Build the encoder for unsigned integer variables
318
319 @type o: file like obj
320 @param o: where write the code
321 @type name: str
322 @param name: field name
323 @type type: str
324 @param type: uint, bool, etc.
325 @type numbits: int >= 1
326 @param numbits: How many bits per unit datum (must be 1..32)
327 @type required: bool or None
328 @param required: If not None, then the value must be set to this.
329 @type arraylen: int >= 1
330 @param arraylen: many unsigned ints will there be? FIX: handle variable
331 @type unavailable: bool or None
332 @param unavailable: the default value to use if none given (if not None)
333 @return: None
334 '''
335 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
336
337 assert type=='uint'
338 assert numbits>=1 and numbits<=32
339 if arraylen != 1: assert False
340 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
341
342 if None != required:
343 if verbose: print ' required:',required
344 required=int(required)
345 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(required)+'),'+str(numbits)+'))\n')
346 if verbose: o.write('\n')
347 return
348
349 if None==unavailable:
350 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']),'+str(numbits)+'))\n')
351 else:
352
353 int(unavailable)
354 o.write("\tif '"+name+"' in params:\n")
355 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']'+'),'+str(numbits)+'))\n')
356 o.write('\telse:\n')
357 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(unavailable)+'),'+str(numbits)+'))\n')
358
359 if verbose: o.write('\n')
360
361
362 -def encodeFloat(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
363 '''
364 Build the encoder for IEEE float variables
365
366 @type o: file like obj
367 @param o: where write the code
368 @type name: str
369 @param name: field name
370 @type type: str
371 @param type: uint, bool, etc.
372 @type numbits: int >= 1
373 @param numbits: How many bits per unit datum (must be 1..32)
374 @type required: bool or None
375 @param required: If not None, then the value must be set to this.
376 @type arraylen: int >= 1
377 @param arraylen: many unsigned ints will there be? FIX: handle variable
378 @type unavailable: bool or None
379 @param unavailable: the default value to use if none given (if not None)
380 @return: None
381 '''
382 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
383
384 assert numbits==32
385 if arraylen != 1: assert False
386 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
387
388 if None != required:
389 if verbose: print ' required:',required
390 required=int(required)
391 o.write('\tbvList.append(binary.float2bitvec('+str(required)+'))\n')
392 if verbose: o.write('\n')
393 return
394
395 if None==unavailable:
396 o.write('\tbvList.append(binary.float2bitvec(params[\''+name+'\']))\n')
397 else:
398
399 int(unavailable)
400 o.write("\tif '"+name+"' in params:\n")
401 o.write('\t\tbvList.append(binary.float2bitvec(params[\''+name+'\']'+'))\n')
402 o.write('\telse:\n')
403 o.write('\t\tbvList.append(binary.float2bitvec('+str(unavailable)+'))\n')
404
405 if verbose: o.write('\n')
406
407
408 -def encodeAisstr6(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
409 '''
410 Build the encoder for aisstr6 variables. Generally are arrays.
411 @bug: do we need to optionally check for a valid string?
412
413 @type o: file like obj
414 @param o: where write the code
415 @type name: str
416 @param name: field name
417 @type type: str
418 @param type: uint, bool, etc.
419 @type numbits: int >= 1
420 @param numbits: How many bits per unit datum (must be 1..32)
421 @type required: bool or None
422 @param required: If not None, then the value must be set to this.
423 @type arraylen: int >= 1
424 @param arraylen: many unsigned ints will there be? FIX: handle variable
425 @type unavailable: bool or None
426 @param unavailable: the default value to use if none given (if not None)
427 @return: None
428 '''
429 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
430
431 assert numbits==6
432 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
433
434 if None != required:
435 if verbose: print ' required:',required
436 required=int(required)
437 o.write('\tbvList.append(aisstring.encode(\''+str(required)+'\','+str(numbits*arraylen)+'))\n')
438 if verbose: o.write('\n')
439 return
440
441 if None==unavailable:
442 o.write('\tbvList.append(aisstring.encode(params[\''+name+'\'],'+str(numbits*arraylen)+'))\n')
443 else:
444 o.write("\tif '"+name+"' in params:\n")
445 o.write('\t\tbvList.append(aisstring.encode(params[\''+name+'\'],'+str(numbits*arraylen)+'))\n')
446 o.write('\telse:\n')
447 o.write('\t\tbvList.append(aisstring.encode(\''+str(unavailable)+'\','+str(numbits*arraylen)+'))\n')
448
449 if verbose: o.write('\n')
450
451
452 -def encodeInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
453 '''
454 Build the encoder for signed integer variables
455
456 @type o: file like obj
457 @param o: where write the code
458 @type name: str
459 @param name: field name
460 @type type: str
461 @param type: uint, bool, etc.
462 @type numbits: int >= 1
463 @param numbits: How many bits per unit datum (must be 1..32)
464 @type required: bool or None
465 @param required: If not None, then the value must be set to this.
466 @type arraylen: int >= 1
467 @param arraylen: many signed ints will there be? FIX: handle variable
468 @type unavailable: number or None
469 @param unavailable: the default value to use if none given (if not None)
470 @return: None
471 '''
472 if verbose: print ' encodeInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
473
474 assert numbits>=1 and numbits<=32
475 if arraylen != 1: assert False
476 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
477
478 if None != required:
479 if verbose: print ' required:',required
480 required=int(required)
481 o.write('\tbvList.append(binary.bvFromSignedInt('+str(required)+','+str(numbits)+'))\n')
482 if verbose: o.write('\n')
483 return
484
485
486 if None==unavailable:
487 o.write('\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\'],'+str(numbits)+'))\n')
488 else:
489
490 int(unavailable)
491 o.write("\tif '"+name+"' in params:\n")
492 o.write('\t\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\']'+','+str(numbits)+'))\n')
493 o.write('\telse:\n')
494 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(unavailable)+','+str(numbits)+'))\n')
495
496 if verbose: o.write('\n')
497
498
499
500
501 -def encodeDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
502 '''
503 Build the encoder for signed decimal variables
504
505 @type o: file like obj
506 @param o: where write the code
507 @type name: str
508 @param name: field name
509 @type type: str
510 @param type: decimal
511 @type numbits: int >= 1
512 @param numbits: How many bits per unit datum (must be 1..32)
513 @type required: bool or None
514 @param required: If not None, then the value must be set to this.
515 @type arraylen: int >= 1
516 @param arraylen: many decimals will there be? FIX: handle variable
517 @type unavailable: Decimal or None
518 @param unavailable: the default value to use if none given (if not None)
519 @return: None
520 '''
521 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
522
523 assert numbits>=1 and numbits<=32
524 if arraylen != 1: assert False
525 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
526
527
528 if None == scale:
529 print 'WARNING: if you are not scaling, then you probably want to use an int instead!'
530 print 'Beware canadians bearing travel videos'
531 scale='1'
532
533 if None != required:
534 if verbose: print ' required:',required
535 required=int(required)
536 o.write('\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(required)*Decimal(scale)))+','+str(numbits)+'))\n')
537 if verbose: o.write('\n')
538 return
539
540
541 if None==unavailable:
542 o.write('\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n')
543 else:
544 o.write("\tif '"+name+"' in params:\n")
545 o.write('\t\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n')
546 o.write('\telse:\n')
547 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(unavailable)*Decimal(scale)))+','+str(numbits)+'))\n')
548
549 if verbose: o.write('\n')
550
551
552
553 -def encodeUDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
554 '''
555 Build the encoder for signed decimal variables
556
557 @type o: file like obj
558 @param o: where write the code
559 @type name: str
560 @param name: field name
561 @type type: str
562 @param type: decimal
563 @type numbits: int >= 1
564 @param numbits: How many bits per unit datum (must be 1..32)
565 @type required: bool or None
566 @param required: If not None, then the value must be set to this.
567 @type arraylen: int >= 1
568 @param arraylen: many decimals will there be? FIX: handle variable
569 @type unavailable: Decimal or None
570 @param unavailable: the default value to use if none given (if not None)
571 @return: None
572 '''
573 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
574 assert type=='udecimal'
575 assert numbits>=1 and numbits<=32
576 if arraylen != 1: assert False
577 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
578
579
580 if None == scale:
581 print 'WARNING: if you are not scaling, then you probably want to use an int instead!'
582 print 'Beware canadians bearing travel videos'
583 scale='1'
584
585 if None != required:
586 if verbose: print ' required:',required
587 required=int(required)
588 assert(0<=required)
589 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(int(Decimal(required)*Decimal(scale)))+'),'+str(numbits)+'))\n')
590 if verbose: o.write('\n')
591 return
592
593
594 if None==unavailable:
595 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n')
596 else:
597 o.write("\tif '"+name+"' in params:\n")
598 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n')
599 o.write('\telse:\n')
600 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int('+str(int(Decimal(unavailable)*Decimal(scale)))+')),'+str(numbits)+'))\n')
601
602 if verbose: o.write('\n')
603
604
605
606
607
608
609
610 -def decodeBool(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
611 bv='bv',dataDict='r',verbose=False):
612 '''
613 Build the decoder for boolean variables
614
615 @type o: file like obj
616 @param o: where write the code
617 @type name: str
618 @param name: field name
619 @type type: str
620 @param type: uint, bool, etc.
621 @type startindex: int
622 @param startindex: bit that begins the bool(s)
623 @type numbits: int = 1
624 @param numbits: How many bits per unit datum (must be 1 for bools)
625 @type required: bool or None
626 @param required: If not None, then the value must be set to this.
627 @type arraylen: int >= 1
628 @param arraylen: many bools will there be? FIX: handle variable
629 @type unavailable: bool or None
630 @param unavailable: the default value to use if none given (if not None)
631 @type bv: str
632 @param bv: BitVector containing the incoming data
633 @type dataDict: str
634 @param dataDict: dictionary in which to place the results
635 @rtype: int
636 @return: index one past the end of where this read
637 '''
638 assert(type=='bool')
639 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
640
641 assert numbits==1
642 assert arraylen == 1
643 if verbose: o.write('\t### FIELD foo: '+name+' (type='+type+')\n')
644
645 if None != required:
646 assert type(required)==bool
647 if required: o.write('\t\t'+dataDict+'[\''+name+'\']=True\n')
648 else: o.write('\t\t'+dataDict+'[\''+name+'\']=False\n')
649 if verbose: o.write('\n')
650 return int(startindex)+int(numbits)
651
652 o.write('\t'+dataDict+'[\''+name+'\']=bool(int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+']))\n')
653 if verbose: o.write('\n')
654
655 return int(startindex)+int(numbits)
656
657
658 -def decodeUInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
659 bv='bv',dataDict='r',verbose=False):
660 '''
661 Build the decoder for unsigned integer variables
662
663 @type o: file like obj
664 @param o: where write the code
665 @type name: str
666 @param name: field name
667 @type type: str
668 @param type: uint, etc.
669 @type startindex: int
670 @param startindex: bit that begins the uint(s)
671 @type numbits: int >= 1
672 @param numbits: How many bits per unit datum
673 @type required: int or None
674 @param required: If not None, then the value must be set to this.
675 @type arraylen: int >= 1
676 @param arraylen: many ints will there be? FIX: handle variable
677 @type unavailable: int or None
678 @param unavailable: the default value to use if none given (if not None)
679 @type bv: str
680 @param bv: BitVector containing the incoming data
681 @type dataDict: str
682 @param dataDict: dictionary in which to place the results
683 @rtype: int
684 @return: index one past the end of where this read
685 '''
686 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
687 if None==arraylen: arraylen=1
688 assert arraylen == 1
689 assert numbits>=1
690 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
691
692 if None != required:
693 int(required)
694 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n')
695 if verbose: o.write('\n')
696 return startindex+numbits
697
698 o.write('\t'+dataDict+'[\''+name+'\']=int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+'])\n')
699 if verbose: o.write('\n')
700
701 return startindex+numbits
702
703
704 -def decodeInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
705 bv='bv',dataDict='r',verbose=False):
706 '''
707 Build the decoder for unsigned integer variables
708
709 @type o: file like obj
710 @param o: where write the code
711 @type name: str
712 @param name: field name
713 @type type: str
714 @param type: int
715 @type startindex: int
716 @param startindex: bit that begins the int(s)
717 @type numbits: int >= 1
718 @param numbits: How many bits per unit datum
719 @type required: int or None
720 @param required: If not None, then the value must be set to this.
721 @type arraylen: int >= 1
722 @param arraylen: many ints will there be? FIX: handle variable
723 @type unavailable: int or None
724 @param unavailable: the default value to use if none given (if not None)
725 @type bv: str
726 @param bv: BitVector containing the incoming data
727 @type dataDict: str
728 @param dataDict: dictionary in which to place the results
729 @rtype: int
730 @return: index one past the end of where this read
731 '''
732 assert type=='int'
733 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
734 if None==arraylen: arraylen=1
735 end = startindex+int(numbits)*int(arraylen)
736 assert arraylen == 1
737 assert numbits>=1
738 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
739
740 if None != required:
741 int(required)
742 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n')
743 if verbose: o.write('\n')
744 return end
745
746 o.write('\t'+dataDict+'[\''+name+'\']=binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+'])\n')
747 if verbose: o.write('\n')
748
749 return end
750
751 -def decodeFloat(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
752 bv='bv',dataDict='r',verbose=False):
753 '''
754 Build the decoder for IEEE float variables
755
756 @type o: file like obj
757 @param o: where write the code
758 @type name: str
759 @param name: field name
760 @type type: str
761 @param type: int
762 @type startindex: int
763 @param startindex: bit that begins the int(s)
764 @type numbits: int >= 1
765 @param numbits: How many bits per unit datum
766 @type required: float or None
767 @param required: If not None, then the value must be set to this.
768 @type arraylen: int >= 1
769 @param arraylen: many ints will there be? FIX: handle variable
770 @type unavailable: float or None
771 @param unavailable: the default value to use if none given (if not None)
772 @type bv: str
773 @param bv: BitVector containing the incoming data
774 @type dataDict: str
775 @param dataDict: dictionary in which to place the results
776 @rtype: int
777 @return: index one past the end of where this read
778 '''
779 assert type=='float'
780 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
781 if None==arraylen: arraylen=1
782 end = startindex+int(numbits)*int(arraylen)
783 assert arraylen == 1
784 assert numbits>=1
785 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
786
787 if None != required:
788 float(required)
789 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n')
790 if verbose: o.write('\n')
791 return end
792
793 o.write('\t'+dataDict+'[\''+name+'\']=binary.bitvec2float('+bv+'['+str(startindex)+':'+str(end)+'])\n')
794 if verbose: o.write('\n')
795
796 return end
797
798
799 -def decodeAisstr6(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
800 bv='bv',dataDict='r',verbose=False):
801 '''
802 Build the decoder for aisstr6 variables. Generally arrays.
803 @bug: FIX: validate strings??
804 @type o: file like obj
805 @param o: where write the code
806 @type name: str
807 @param name: field name
808 @type type: str
809 @param type: 'aisstr6'
810 @type startindex: int
811 @param startindex: bit that begins the int(s)
812 @type numbits: int >= 1
813 @param numbits: How many bits per unit datum
814 @type required: restricted str or None
815 @param required: If not None, then the value must be set to this.
816 @type arraylen: int >= 1
817 @param arraylen: many ints will there be? FIX: handle variable
818 @type unavailable: restricted str or None
819 @param unavailable: the default value to use if none given (if not None)
820 @type bv: str
821 @param bv: BitVector containing the incoming data
822 @type dataDict: str
823 @param dataDict: dictionary in which to place the results
824 @rtype: int
825 @return: index one past the end of where this read
826 '''
827 assert type=='aisstr6'
828 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
829 if None==arraylen: arraylen=1
830 end = startindex+int(numbits)*int(arraylen)
831 assert arraylen >= 1
832 assert numbits>=1
833 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
834
835 if None != required:
836 float(required)
837 o.write('\t'+dataDict+'[\''+name+'\']='+required+'\n')
838 if verbose: o.write('\n')
839 return end
840
841 o.write('\t'+dataDict+'[\''+name+'\']=aisstring.decode('+bv+'['+str(startindex)+':'+str(end)+'])\n')
842 if verbose: o.write('\n')
843
844 return end
845
846
847 -def decodeDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
848 bv='bv',dataDict='r',verbose=False,scale=None):
849 '''
850 Build the decoder for signed decimal variables
851
852 @type o: file like obj
853 @param o: where write the code
854 @type name: str
855 @param name: field name
856 @type type: str
857 @param type: 'decimal'
858 @type startindex: int
859 @param startindex: bit that begins the int(s)
860 @type numbits: int >= 1
861 @param numbits: How many bits per unit datum
862 @type required: Decimal or None
863 @param required: If not None, then the value must be set to this.
864 @type arraylen: int >= 1
865 @param arraylen: many ints will there be? FIX: handle variable
866 @type unavailable: Decimal or None
867 @param unavailable: the default value to use if none given (if not None)
868 @type bv: str
869 @param bv: BitVector containing the incoming data
870 @type dataDict: str
871 @param dataDict: dictionary in which to place the results
872 @rtype: int
873 @return: index one past the end of where this read
874 '''
875 assert type=='decimal'
876 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
877 if None==arraylen: arraylen=1
878 end = startindex+int(numbits)*int(arraylen)
879 assert arraylen == 1
880 assert numbits>=1 and numbits <= 32
881 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
882
883 if None == scale: scale='1'
884
885 if None != required:
886 Decimal(required)
887 o.write('\t'+dataDict+'[\''+name+'\']='+str(Decimal(required))+'/Decimal(\''+scale+'\')\n')
888 if verbose: o.write('\n')
889 return end
890
891 o.write('\t'+dataDict+'[\''+name+'\']=Decimal(binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')\n')
892 if verbose: o.write('\n')
893
894 return end
895
896
897
898
899 -def decodeUDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
900 bv='bv',dataDict='r',verbose=False,scale=None):
901 '''
902 Build the decoder for unsigned decimal variables
903
904 @type o: file like obj
905 @param o: where write the code
906 @type name: str
907 @param name: field name
908 @type type: str
909 @param type: 'udecimal'
910 @type startindex: int
911 @param startindex: bit that begins the int(s)
912 @type numbits: int >= 1
913 @param numbits: How many bits per unit datum
914 @type required: Decimal or None
915 @param required: If not None, then the value must be set to this.
916 @type arraylen: int >= 1
917 @param arraylen: many ints will there be? FIX: handle variable
918 @type unavailable: Decimal or None
919 @param unavailable: the default value to use if none given (if not None)
920 @type bv: str
921 @param bv: BitVector containing the incoming data
922 @type dataDict: str
923 @param dataDict: dictionary in which to place the results
924 @rtype: int
925 @return: index one past the end of where this read
926 '''
927 assert type=='udecimal'
928 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
929 if None==arraylen: arraylen=1
930 end = startindex+int(numbits)*int(arraylen)
931 assert arraylen == 1
932 assert numbits>=1 and numbits <= 32
933 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
934
935 if None == scale: scale='1'
936
937 if None != required:
938 assert (Decimal(required)>=0.)
939 o.write('\t'+dataDict+'[\''+name+'\']='+str(Decimal(required))+'/Decimal(\''+scale+'\')\n')
940 if verbose: o.write('\n')
941 return end
942
943 o.write('\t'+dataDict+'[\''+name+'\']=Decimal(int('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')\n')
944 if verbose: o.write('\n')
945
946 return end
947
948
949
950
951
952
953
954
955
957 '''Scrape the testvalues to make a basic param
958
959 @bug: FIX: make this create a dictionary that sits in the overall namespace and spit out deep copies?
960 '''
961 name = msgET.attrib['name']
962
963 o.write('def '+name+'TestParams():\n')
964 o.write("\t'''Return a params file base on the testvalue tags.\n\t@rtype: dict\n\t@return: params based on testvalue tags\n\t'''\n")
965 o.write('\tparams = {}\n')
966 for field in msgET.xpath('field'):
967 name = field.attrib['name']
968 type = field.attrib['type']
969 if verbose: print 'buildTestParamFunc ...',name,type
970 val = None
971 if hasSubtag(field,'testvalue') and hasSubtag(field,'required'):
972 print 'ERROR: can not have both test value and required tags in the same field'
973 assert(False)
974 if hasSubtag(field,'testvalue'):
975 val = field.xpath('testvalue')[0].text
976 else:
977 if not hasSubtag(field,'required'):
978 sys.exit("ERROR: missing required or testvalue for field: "+name)
979 val = field.xpath('required')[0].text
980 if verbose: print 'buildTestParamFunc for field '+name+' ...',type,val
981 o.write('\tparams[\''+name+'\'] = ')
982 if type=='bool':
983 if val=='1' or val.lower=='true': val = 'True'
984 else: val = 'False'
985 o.write(val)
986 elif type in ('uint','int','float'):
987 o.write(val)
988 elif type in ('decimal','udecimal'):
989 o.write('Decimal(\''+val+'\')')
990 elif type in ('aisstr6'):
991 o.write('\''+val+'\'')
992 else:
993 print 'ERROR: type not handled ...',type,' (found in the ',name,' field). Time to buy more coffee'
994 suggestType(name,type)
995 assert(False)
996
997 o.write('\n')
998
999
1000 o.write('\n\treturn params\n\n')
1001
1003 '''
1004 Write the unittests for a message
1005
1006 param o: open file where resulting code will be written
1007 param msgET: Element Tree starting at a message node
1008 '''
1009 assert(msgET.tag=='message')
1010 name = msgET.attrib['name']
1011
1012 buildTestParamFunc(o,msgET)
1013
1014 o.write('class Test'+name+'(unittest.TestCase):\n')
1015 o.write("\t'''Uses the testvalue tag text from each type to build a test case for the "+name+" message'''\n")
1016 o.write('\tdef testEncodeDecode(self):\n')
1017
1018 o.write('\t\n')
1019 o.write('\t\tparams = '+name+'TestParams()\n')
1020
1021
1022 o.write('\t\tbits = '+name+'Encode(params)\n')
1023
1024 o.write('\t\tr = '+name+'Decode(bits)\n')
1025
1026 o.write('\n')
1027 o.write('\t\t# Check that each parameter came through ok.\n')
1028
1029
1030 for field in msgET.xpath('field'):
1031 name = field.attrib['name']
1032 type = field.attrib['type']
1033 if type in ('bool','uint','int','aisstr6'):
1034 o.write('\t\tself.failUnlessEqual(r[\''+name+'\'],params[\''+name+'\'])\n')
1035 else:
1036
1037
1038 places = '7'
1039 if hasSubtag(field,'decimalplaces'): places = field.xpath('decimalplaces')[0].text
1040 o.write('\t\tself.failUnlessAlmostEqual(r[\''+name+'\'],params[\''+name+'\'],'+places+')\n')
1041
1042
1043
1045 '''
1046 Write the encoder/decoder for a message
1047
1048 http://jaynes.colorado.edu/PythonIdioms.html
1049
1050 param o: open file where resulting code will be written
1051 param msgET: Element Tree starting at a message node
1052 '''
1053 assert(msgET.tag=='message')
1054 name = msgET.attrib['name']
1055
1056 print 'Generating encoder for',name
1057 funcName = name+'Encode'
1058 o.write('def '+funcName+'(params, validate=False):\n')
1059
1060
1061
1062 o.write("\t'''Create a "+name+" binary message payload to pack into an AIS Msg "+name+".\n\n")
1063 o.write('\tFields in params:\n')
1064 for field in msgET.xpath('field'):
1065 desc = field[0].text.replace('\n',' ')
1066 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc)
1067 if len(field.xpath("required")) == 1:
1068 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")')
1069
1070 o.write('\n')
1071 o.write('\t@param params: Dictionary of field names/values. Throws a ValueError exception if required is missing\n')
1072 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n')
1073
1074 o.write("\t@rtype: BitVector\n")
1075 o.write("\t@return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8\n")
1076 o.write("\t'''\n\n")
1077
1078
1079
1080
1081 o.write('\tbvList = []\n')
1082
1083 if verbose: print 'number of fields = ', len(msgET.xpath('field'))
1084
1085 dynamicArrays = False
1086
1087 for field in msgET.xpath('field'):
1088 name = field.attrib['name']
1089 type = field.attrib['type']
1090 numbits = int(field.attrib['numberofbits'])
1091 required = None;
1092 if hasSubtag(field,'required'):
1093 required = field.xpath('required')[0].text
1094
1095 unavailable=None;
1096 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
1097 arraylen=1
1098 if 'arraylength' in field.attrib:
1099 arraylen=int(field.attrib['arraylength'])
1100 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
1101 else:
1102 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
1103
1104 if type=='bool' : encodeBool (o,name,type,numbits,required,arraylen,unavailable)
1105 elif type=='uint' : encodeUInt (o,name,type,numbits,required,arraylen,unavailable)
1106 elif type=='int' : encodeInt (o,name,type,numbits,required,arraylen,unavailable)
1107 elif type=='float' : encodeFloat (o,name,type,numbits,required,arraylen,unavailable)
1108 elif type=='aisstr6': encodeAisstr6(o,name,type,numbits,required,arraylen,unavailable)
1109 elif type=='decimal':
1110 scale = None
1111 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1112 encodeDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale)
1113 elif type=='udecimal':
1114 scale = None
1115 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1116 encodeUDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale)
1117 else:
1118 print 'WARNING: In buildEncode - Unhandled field type for',name,'...',type
1119 suggestType (name,type)
1120 assert False
1121
1122 o.write('\n\treturn binary.joinBV(bvList)\n\n')
1123
1124
1125
1126
1127
1129 '''
1130 Write the decoder for a message
1131
1132 param o: open file where resulting code will be written
1133 type msgET: elementtree
1134 param msgET: Element Tree starting at a message node
1135 return: None
1136
1137 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload
1138 '''
1139
1140 assert(msgET.tag=='message')
1141 name = msgET.attrib['name']
1142
1143 print 'Generating decoder for',name
1144 funcName = name+'Decode'
1145 o.write('def '+funcName+'(bv, validate=False):\n')
1146
1147
1148
1149 o.write("\t'''Unpack a "+name+" message \n\n")
1150 o.write('\tFields in params:\n')
1151 for field in msgET.xpath('field'):
1152 desc = field[0].text.replace('\n',' ')
1153 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc)
1154 if len(field.xpath("required")) == 1:
1155 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")')
1156
1157 o.write('\n')
1158 o.write('\t@type bv: BitVector\n')
1159 o.write('\t@param bv: Bits defining a message\n')
1160 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n')
1161
1162 o.write("\t@rtype: dict\n")
1163 o.write("\t@return: params\n")
1164 o.write("\t'''\n\n")
1165
1166
1167 o.write('\t#Would be nice to check the bit count here..\n')
1168 o.write('\t#if validate:\n')
1169 o.write('\t#\tassert (len(bv)==FIX: SOME NUMBER)\n')
1170
1171
1172
1173
1174
1175 o.write('\tr = {}\n')
1176
1177
1178 if verbose: print 'number of fields = ', len(msgET.xpath('field'))
1179
1180 dynamicArrays = False
1181
1182 startindex = 0
1183
1184 for field in msgET.xpath('field'):
1185 name = field.attrib['name']
1186 type = field.attrib['type']
1187 numbits = int(field.attrib['numberofbits'])
1188 required = None;
1189 if hasSubtag(field,'required'):
1190 required = field.xpath('required')[0].text
1191
1192 unavailable=None;
1193 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
1194 arraylen=1
1195 if 'arraylength' in field.attrib:
1196 arraylen=int(field.attrib['arraylength'])
1197 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
1198 else:
1199 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
1200
1201 assert None!=startindex
1202 if verbose: print 'startindex',startindex
1203 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable)
1204 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable)
1205 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable)
1206 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable)
1207 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable)
1208 elif type=='decimal':
1209 scale = None
1210 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1211
1212
1213 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale)
1214 elif type=='udecimal':
1215 scale = None
1216 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1217
1218
1219 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale)
1220
1221 else:
1222 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type
1223 suggestType (name,type)
1224 assert False
1225
1226
1227 if None==startindex: print 'FIX: here. drat. treat me right'
1228 assert None!=startindex
1229
1230
1231 o.write('\treturn r\n\n')
1232
1233
1234
1235
1237 o.write('''
1238 ############################################################
1239 if __name__=='__main__':
1240
1241 from optparse import OptionParser
1242 myparser = OptionParser(usage="%prog [options]",
1243 version="%prog "+__version__)
1244
1245 #sys.exit('EARLY EXIT - FIX: remove')
1246 myparser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
1247 help='run the documentation tests')
1248 myparser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
1249 help='run the unit tests')
1250 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
1251 help='Make the test output verbose')
1252
1253 (options,args) = myparser.parse_args()
1254 success=True
1255
1256 if options.doctest:
1257 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
1258 sys.argv= [sys.argv[0]]
1259 if options.verbose: sys.argv.append('-v')
1260 import doctest
1261 numfail,numtests=doctest.testmod()
1262 if numfail==0: print 'ok'
1263 else:
1264 print 'FAILED'
1265 success=False
1266
1267 if not success:
1268 sys.exit('Something Failed')
1269
1270 del success # Hide success from epydoc
1271
1272 if options.unittest:
1273 sys.argv = [sys.argv[0]]
1274 if options.verbose: sys.argv.append('-v')
1275 unittest.main()
1276 ''')
1277
1278
1279 if __name__=='__main__':
1280 from optparse import OptionParser
1281 myparser = OptionParser(usage="%prog [options]",
1282 version="%prog "+__version__)
1283
1284 myparser.add_option('-o','--output',dest='outputFileName',default=None,
1285 help='Name of the python file to write')
1286
1287
1288 myparser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None,
1289 help='XML definition file for the msg to use')
1290
1291
1292
1293
1294
1295 myparser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
1296 help='run the documentation tests')
1297
1298 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
1299 help='run the tests run in verbose mode')
1300
1301 (options,args) = myparser.parse_args()
1302
1303 success=True
1304
1305 if options.doctest:
1306 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
1307 argvOrig = sys.argv
1308 sys.argv= [sys.argv[0]]
1309 if options.verbose: sys.argv.append('-v')
1310 import doctest
1311 numfail,numtests=doctest.testmod()
1312 if numfail==0: print 'ok'
1313 else:
1314 print 'FAILED'
1315 success=False
1316 sys.argv = argvOrig
1317 del argvOrig
1318 sys.exit()
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328 if None==options.xmlFileName:
1329 sys.exit('ERROR: must specify an xml definition file.')
1330 if None==options.outputFileName:
1331 sys.exit('ERROR: must specify an python file to write to.')
1332 generatePython(options.xmlFileName,options.outputFileName)
1333
1334 print '\nrecommend running pychecker like this:'
1335 print ' pychecker -q',options.outputFileName
1336