Path: csiph.com!usenet.pasdenom.info!news.redatomik.org!newsfeed.xs4all.nl!newsfeed1a.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'else:': 0.03; 'skip:[ 20': 0.04; 'argument': 0.05; 'elif': 0.05; 'args': 0.07; 'error:': 0.07; 'parser': 0.07; 'python3': 0.07; '"__main__":': 0.09; '__name__': 0.09; 'exit': 0.09; 'lookup': 0.09; 'parameter': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'try:': 0.09; 'unhandled': 0.09; 'variables.': 0.09; 'way:': 0.09; 'python': 0.11; 'def': 0.12; 'arguments:': 0.16; 'dict': 0.16; 'enough.': 0.16; 'func': 0.16; 'naming': 0.16; 'none.': 0.16; 'optional': 0.16; 'positional': 0.16; 'received:80.91.229.3': 0.16; 'received:dip0.t-ipconnect.de': 0.16; 'received:plane.gmane.org': 0.16; 'received:t-ipconnect.de': 0.16; 'reedy': 0.16; 'sys.exit(1)': 0.16; 'wrote:': 0.18; 'all,': 0.19; 'bit': 0.19; 'import': 0.22; 'print': 0.22; 'header:User-Agent:1': 0.23; 'skip:% 10': 0.24; 'skip:{ 20': 0.24; 'define': 0.26; 'equivalent': 0.26; 'values': 0.27; 'header:X-Complaints-To:1': 0.27; 'testing': 0.29; 'code': 0.31; "skip:' 10": 0.31; 'usually': 0.31; 'constant': 0.31; 'factor': 0.31; 'flags': 0.31; 'keyerror:': 0.31; 'this.': 0.32; 'framework': 0.33; 'moment': 0.34; 'except': 0.35; 'something': 0.35; 'test': 0.35; 'there': 0.35; 'should': 0.36; 'project': 0.37; 'to:addr:python-list': 0.38; 'pm,': 0.38; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'received:org': 0.40; 'complete': 0.62; 'show': 0.63; 'name': 0.63; 'more': 0.64; 'needs,': 0.65; 'specialized': 0.65; 'direct': 0.67; 'believe': 0.68; 'invalid': 0.68; 'subject:this': 0.83; 'argparse:': 0.84; 'duplication': 0.84; 'subject:good': 0.84; 'approach.': 0.91; 'fibonacci': 0.91; 'involved.': 0.91 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Peter Otten <__peter__@web.de> Subject: Re: Is this a good way to implement testing Date: Sun, 03 May 2015 11:22:19 +0200 Organization: None References: <878ud6mx4y.fsf@Equus.decebal.nl> Mime-Version: 1.0 Content-Type: text/plain; charset="ISO-8859-1" Content-Transfer-Encoding: 7Bit X-Gmane-NNTP-Posting-Host: p57bd8571.dip0.t-ipconnect.de User-Agent: KNode/4.13.3 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.20+ Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 124 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1430644949 news.xs4all.nl 2830 [2001:888:2000:d::a6]:45160 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:89842 Terry Reedy wrote: > On 5/2/2015 6:29 PM, Cecil Westerhof wrote: > >> At the moment I define the test functionality in the following way: > > Any automated testing is better than none. For idlelib, I use unittest. > For an individual project with specialized needs, I use a custom test > framework tuned to those needs. > >> else: >> action = options[0][0] >> if action == '--all': >> do_all = True >> elif action == '--factorial': >> do_factorial = True >> elif action == '--fibonacci': >> do_fibonacci = True >> elif action == '--happy': >> do_happy = True >> elif action == '--lucky': >> do_lucky = True >> else: >> print >> sys.stderr, progname + ': Unhandled parameter ' >> + action sys.exit(1) > > There is usually a way to factor out the duplication of repetitive code > like this. > Often, a dict is somehow involved. That cannot be stressed enough. In Python if you test against multiple constant values you should always consider a dict-based approach. Also, you should not shy away from using functions as variables. Instead of do_lucky = True ... if do_lucky: do_lucky_func() you can often write something more direct action = do_lucky_func ... action_func() > I believe the following > is equivalent to the above. > > else: > action = options[0][0] > try: > globals()['do_'+action[2:]] = True > except KeyError: > print >> sys.stderr, progname + ': Unhandled parameter ' + action > sys.exit(1) To go a bit farther down that path of using naming conventions, dict instead of extensive if ... elif ... and functions instead of flags here's a complete implementation with argparse: $ python3 choose_action.py -h usage: choose_action.py [-h] [{all,fibonacci,factorial}] positional arguments: {all,fibonacci,factorial} Known actions: all, fibonacci, factorial optional arguments: -h, --help show this help message and exit $ python3 choose_action.py all fibonacci factorial $ python3 choose_action.py factorial fibonacci $ python3 choose_action.py fibonacci fibonacci $ python3 choose_action.py non-existent usage: choose_action.py [-h] [{all,factorial,fibonacci}] choose_action.py: error: argument action: invalid choice: 'non-existent' (choose from 'all', 'factorial', 'fibonacci') $ cat choose_action.py import argparse def do_factorial(): print("factorial") # ... def do_fibonacci(): print("fibonacci") # ... def do_all(): for name, action in lookup.items(): if name != "all": action() lookup = { name[3:]: func for name, func in globals().items() if name.startswith("do_") } if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "action", nargs="?", default="all", choices=lookup, help="Known actions: %(choices)s") args = parser.parse_args() lookup[args.action]() $