/timm's /charming /python /tricks

demo.py

Download demo.py.
Read more on How to be Charming (in Python).


001: import sys
002: sys.dont_write_bytecode = True
003: from copyleft import *
004: 
005: """
006: This file address three problems:
007: 
008: - The demo problem: how to show off your cool code.
009: - The test suite problem: how to check if past
010:   code still runs.
011: - The command-line problem: how to call code from
012:   command-line.
013: 
014: ####################################################
015: Quick and dirty method. We do better below. 
016: """
017: 
018: def go(f):
019:   "Annotate functions to run at load time."
020:   print "\n# ---|", f.__name__,"|-----------------"
021:   if f.__doc__:
022:     print "#", f.__doc__
023:   f()
024:   return f
025: 
026: #@go #<-- uncomment this line and load this file
027: def someFunctionWithLongName():
028:   "example of using @go."
029:   print "All done with a few keystrokes"
030:   quit()
031: 
032: """#################################################
033: PROBLEM: How to show off you cool code to other
034: people, remembering all those tricks that make the
035: code do cool things.
036: 
037: SOLUTION: Do all those cool things in a function.
038: 
039: CONVENTIONS :  
040: 1. Annotate those functions with @demo 
041: 2. Demo functions end in "d".  
042: 3. If there are many 
043:    demos, make a seperate file, also ending in "d". 
044:    So demos for lib.py would be found in libd.py.
045: 3. For functions that random numbers, there is no
046:    call to "resetSeed" unless that the argument to
047:    that call can be set by a demo function.
048: """
049: 
050: def demo0(f=None): return demo(f,0)
051: def demo1(f=None): return demo(f,1)
052: def demo2(f=None): return demo(f,2)
053: def demo3(f=None): return demo(f,3)
054: def demo4(f=None): return demo(f,4)
055: def demo5(f=None): return demo(f,5)
056: def demo6(f=None): return demo(f,6)
057: def demo7(f=None): return demo(f,7)
058: 
059: def demo(f=None,lvl=0,d={}):
060:   """
061:   A decorator for a demo suite. E.g.
062: 
063:     @demo1
064:     def demoed() : 
065:       "Doco about this demo"
066:       print "I am a demo"
067:   
068:   will add demoed to a list of known demos. 
069:   Then, we can call all demos using "demo()".
070:   Note that demos can be defined at one of
071:   eight levels demo0...demo7 and higher
072:   level demos are run after lower-ones. 
073:   """
074:   if f: 
075:     if not lvl in d: d[lvl] = []
076:     d[lvl]  += [f]
077:     return f
078:   for lvl in sorted(d.keys()):
079:     for f in d[lvl]: 
080:       print '\n--|','('+str(lvl)+')',\
081:            f.func_name,'|','-'*40,'\n',f.__doc__,'\n'
082:       f()
083: 
084: """#################################################
085: PROBLEM: How to build a regression suite so that
086: tomorrow you can test that the code still does what
087: it did yesterday?
088: 
089: SOLUTION: Write lots of functions that return 
090: (want,got) pairs, count how many times want == got 
091: and want != got.
092: 
093: CONVENTIONS:
094: 1. Annotate those functions with @test
095: 2. Test functions end in "d". 
096: 3. If lots of tests, store them in a seperate demos 
097:    files (name ending in "d"). 
098: """
099: 
100: def test(f=None,tests=[]): 
101:   """ 
102:   A decorator for test suites. Each test function 
103:   returns a list of n pairs `[(got1,want1),(got2,want2),...]` 
104:   and this scores n more "passes" if  `got==want`. E.g.
105: 
106:     @test
107:     def tested(): 
108:       thing = 22
109:       return [(22,thing),  
110:               (False, type thing = float)]
111:     
112:   will add "tested" to a list of known tests. Then,
113:   we can call all tests and get a print out of 
114:   number of passes and fails using "test()
115:   """
116:   if f: tests.append(f); return f
117:   ok=no=0
118:   for t in tests: 
119:     print "#",t.func_name + ': ',t.__doc__
120:     for n,(want,got) in  enumerate(t()):
121:       if want == got:
122:         ok += 1; print "CORRECT:",t.func_name,'question', n+1
123:       else:
124:         no += 1; print "WRONG  :",t.func_name,'question',n+1
125:   if tests:
126:     print '\n# Final score = %s/%s = %s%% CORRECT' \
127:             % (ok,(ok+no),round(100*ok/(ok+no)))
128: 
129: """#################################################
130: PROBLEM: How to call all these code/test functions
131: from the command line (handling arguments, compiling
132: number strings, etc).
133: 
134: SOLUTION: See below.
135: 
136: CONVENTIONS: The function called in this way has 
137: default values to its arguments, which can be 
138: over-ridden from the command line.
139: """
140: 
141: def cmd(com='demo()'):
142:   """
143:   If the call to python file.py includes additional;
144:   arguments then parse them into a function call.
145:   If not, call some default command. For example,
146:   if this line is added to end of file:
147: 
148:    if __name__ == '__main__' : 
149:         eval(cmd('copyleft()'))
150: 
151:   Then if the call this file as follows:
152: 
153:     python demo,py 
154: 
155:   then some default copyright notice will be shown.
156:   But it we do it again using
157: 
158:     python demo.py copyleft hilary 2016 1000
159: 
160:   then we see a customized copyright notice where
161:   the defaults for the copyleft function have been
162:   replaced with name=hilary, year=2016, rate=1000.
163:   """
164:   if len(sys.argv) < 2:
165:     return com
166:   else: 
167:     def stringp(x): 
168:       return isinstance(x,basestring)
169:     def atom(x):
170:       try: return float(x)
171:       except ValueError: return x
172:     def wrapper(x):
173:         return "'" + x + "'" if stringp(x) else str(x)
174:     words = map(wrapper,map(atom,sys.argv[2:]))
175:     return sys.argv[1] + '(' + ','.join(words) + ')'
176: 
177: if __name__ == '__main__' : 
178:   eval(cmd('copyleft()'))
179: 

This file is part of Timm's charming Python tricks.
© 2014, Tim Menzies: tim.menzies@gmail.com, http://menzies.us.

Timm's charming Python tricks are free software: you can redistribute it and/or modify it under the terms of the GNU Lesser Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

Timm's charming Python tricks are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU Lesser Public License along with Foobar. If not, see http://www.gnu.org/licenses.