1 #=======================================================================
3 __version__
= '''0.0.01'''
4 __sub_version__
= '''20121219035042'''
5 __copyright__
= '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
11 from pprint
import pprint
, pformat
14 #-----------------------------------------------------------------------
19 TEST_FILE_NAME
= '<test>'
26 #-----------------------------------------------------------------------
28 # XXX the script does not see the imported modules...
29 # from pprint import pprint
31 # ## this will priduce a name error!
35 # there are cases when this works...
38 # TODO add error log support...
39 # TODO exception handling both in testing and in failures....
40 # TODO add more clever output handling...
46 # TODO add multiline things like if, for, ...
47 # TODO error reporting should generate something not only readable but
48 # reproducable, at this poit it will print the actual result
49 # rather than the expected, which will change the semantics of the
53 #-----------------------------------------------------------------------
54 #-----------------------------------------------------------------log---
59 depth
= kw
.pop('depth', 1)
60 rep
= kw
.pop('repr', REPR_FUNCTION
)
61 mute
= kw
.pop('mute', False)
63 filename
= kw
.pop('filename', TEST_FILE_NAME
)
64 mute_prefix
= kw
.pop('mute_prefix', MUTE_PREFIX
)
66 lcl
= sys
._getframe
(depth
).f_locals
67 glbl
= sys
._getframe
(depth
).f_globals
71 code
= '%(indent)s %(mute_prefix)s%(command)s%(result)s'
79 data
['indent'] = ' '*(INDENT
-1)
82 data
['mute_prefix'] = mute_prefix
83 data
['command'] = cmd
[0].strip()
85 ##!!! add exception handling...
86 res
= eval(compile(cmd
[0].strip(), filename
, 'eval'), glbl
, lcl
)
88 if len(cmd
[0].strip()) + INDENT
>= 8:
89 data
['result'] = '\n\t-> %s\n' % rep(res
)
91 data
['result'] = '\t-> %s\n' % rep(res
)
94 # we've got a statement...
96 # XXX need a more robust way to do this...
97 ##!!! add exception handling...
98 eval(compile(cmd
[0].strip(), filename
, 'exec'), glbl
, lcl
)
100 # we've got an exception...
103 # XXX figure out a better syntax to represent exceptions...
104 data
['result'] = '\n\t-X-> %s\n' % rep(err
)
107 data
['mute_prefix'] = mute_prefix
108 data
['command'] = ''.join([c
.strip() for c
in cmd
]) + cmd
[-1].strip()
109 ##!!! add exception handling...
110 res
= eval(compile(cmd
[-1].strip(), filename
, 'eval'), glbl
, lcl
)
112 if len(cmd
[-1].strip()) + INDENT
>= 8:
113 data
['result'] = '\n\t->%s\n' % rep(res
)
115 data
['result'] = '\t->%s\n' % rep(res
)
117 data
['result'] = '\n'
121 return code
% data
, res
, err
124 #----------------------------------------------------------------test---
125 def test(*cmd
, **kw
):
126 expected
, cmd
= cmd
[-1], cmd
[:-1]
127 expected_err
= kw
.pop('expected_err', None)
128 depth
= kw
.pop('depth', 1)
129 rep
= kw
.pop('repr', REPR_FUNCTION
)
130 code
, res
, err
= log(depth
=depth
+1, *cmd
)
132 ##!!! for some reason, if we have an exception, we do not reach this spot...
135 if expected_err
is None:
137 if expected_err
== err
:
144 text
+= '\t## Error: result did not match the expected: %s' % rep(expected
)
149 #--------------------------------------------------------pretty_print---
151 # XXX this is the same as log but with pretty printing, need to
152 # redesign this to be more like a mixin to the log...
153 def pretty_print(*cmd
, **kw
):
156 NOTE: this will print the value in a non-printable comment so as to be
157 self-applicamle -- currently multiline structures are not supported.
160 depth
= kw
.pop('depth', 1)
161 rep
= kw
.pop('repr', REPR_FUNCTION
)
162 filename
= kw
.pop('filename', TEST_FILE_NAME
)
163 lcl
= sys
._getframe
(depth
).f_locals
164 glbl
= sys
._getframe
(depth
).f_globals
166 text
= '%s %s' % (PPRINT_PREFIX
, code
)
167 ##!!! add exception handling...
168 res
= pformat(eval(compile(cmd
[0].strip(), filename
, 'eval'), glbl
, lcl
), width
=80-8-3)
169 text
+= '%s %s' % ('\n##\t->', '\n##\t '.join(res
.split('\n')))
173 #------------------------------------------------------------loglines---
174 def loglines(*lines
, **kw
):
177 depth
= kw
.pop('depth', 1)
178 mute_prefix
= kw
.pop('mute_prefix', MUTE_PREFIX
)
179 pprint_prefix
= kw
.pop('pprint_prefix', PPRINT_PREFIX
)
183 if line
.strip().startswith('##'):
186 if line
.strip().startswith('#'):
187 print (' '*INDENT
) + line
.strip()
190 if line
.strip().startswith('---'):
191 print '-'*(TERM_WIDTH
-1)
194 if line
.strip().startswith('==='):
195 print '='*(TERM_WIDTH
-1)
198 if line
.strip().startswith(pprint_prefix
):
199 pretty_print(line
.strip()[len(pprint_prefix
):],
201 pprint_prefix
=pprint_prefix
,
202 mute_prefix
=mute_prefix
,
206 if line
.strip().startswith(mute_prefix
):
207 print log(line
.strip()[len(mute_prefix
):],
210 pprint_prefix
=pprint_prefix
,
211 mute_prefix
=mute_prefix
,
215 if type(line
) in (str, unicode):
216 if line
.strip() == '':
219 print log(line
, depth
=depth
+1, **kw
)[0]
220 elif type(line
) is tuple:
221 test(depth
=depth
+1, *line
, **kw
)
223 raise TypeError, 'unsupported line type (can handle strings and tuples, got: %s)' % type(line
)
227 #-----------------------------------------------------------loglines2---
229 # XXX if '->' is somewhare inside a string in a test string then we are in trouble :)
230 # e.g. the next line will die with a 'support only one "->" per line' error
231 # 'f("->") -> "some string"'
232 ##!!! make this extend loglines rather than copy most of the functionality....
233 def loglines2(*lines
, **kw
):
236 depth
= kw
.pop('depth', 1)
237 mute_prefix
= kw
.pop('mute_prefix', MUTE_PREFIX
)
238 pprint_prefix
= kw
.pop('pprint_prefix', PPRINT_PREFIX
)
239 only_errors
= kw
.pop('only_errors', False)
246 if line
.strip().startswith('##'):
249 if line
.strip().startswith('#'):
251 yield (' '*INDENT
) + line
.strip()
254 if line
.strip().startswith('---'):
256 yield '-'*(TERM_WIDTH
-1)
259 if line
.strip().startswith('==='):
261 yield '='*(TERM_WIDTH
-1)
264 if line
.strip().startswith(pprint_prefix
):
266 yield pretty_print(line
.strip()[len(pprint_prefix
):],
268 pprint_prefix
=pprint_prefix
,
269 mute_prefix
=mute_prefix
,
273 if line
.strip().startswith(mute_prefix
):
275 res
= log(line
.strip()[len(mute_prefix
):],
278 pprint_prefix
=pprint_prefix
,
279 mute_prefix
=mute_prefix
,
285 line
= line
.split('->')
288 if line
.strip() == '':
293 res
= log(line
, depth
=depth
+1, **kw
)[0]
298 ##!!! add exception handling...
299 res
, text
= test(line
[0], eval(line
[1]), depth
=depth
+1, **kw
)
302 if not only_errors
or not res
:
305 raise TypeError, 'support only one "->" per line'
310 'fails': lines_failed
,
314 #--------------------------------------------------------------logstr---
315 def logstr(text
, **kw
):
318 depth
= kw
.pop('depth', 1)
319 stats
= kw
.pop('print_stats', True)
322 for s
in text
.split('\n'):
323 if s
.strip().startswith('->'):
327 for l
in loglines2(depth
=depth
+1, *strs
, **kw
):
328 if type(l
) not in (str, unicode):
329 if stats
and l
['lines'] > 0:
330 print (' '*INDENT
) + '## executed %(lines)s lines, of which %(fails)s failed.' % l
336 #-----------------------------------------------------------------------
337 if __name__
== '__main__':
338 from pprint
import pprint
340 # this module will define a special DSL based on python. this
341 # language is designed to facilitate module self-testing.
343 # this module can be considered as a usage example. below you see
344 # the lines that both demo and test the functionality of the
347 # comments starting with a double '#' are not shown...
348 ## here's is an example...
351 # NOTE: indent is ignored...
352 # ...but this does not concern comment formatting.
354 # next, a few empty lines...
358 # now an expression...
361 # an expression with a test value...
364 # we can put the expected result on a separate line...
368 # an expression that will fail it's value test...
370 ## this will break...
374 # now we can test the value...
377 # we can also test for fails...
381 ## -X-> ZeroDivisionError
385 >>> {1:range(10), 2:range(10), 3:range(10)}
388 # it is also possible to mute result output...
389 ! {1:range(10), 2:range(10), 3:range(10)}
392 # now for some basic markup...
393 # we can do basic lines...
397 # NOTE: one can have more than three dashes, but not less... two
398 # dashes will be passed to python and thus generate a syntax
402 # and we can print only errors (same code as up to this point
403 # re-run with errors_only option set)...
406 logstr(test_code
, print_stats
=False)
408 # enable only error printing...
409 logstr(test_code
+ '''\n\t# we can also print exec stats...''',
410 print_stats
=True, only_errors
=True)
415 # statements are supported too, but only if no expected result is
417 # NOTE: it is best to avoid things that print things, they will
418 # generate output that is not a valid test script.
422 # oh, and did I mention that logstr is self-applicable?
423 # ...well it is! that is if you avoud mixing the code with prints ;)
426 # that's all at this point.
431 #=======================================================================
432 # vim:set ts=4 sw=4 nowrap :