how to optimize the below code with a helper function

Martin A. Brown martin at linux-ip.net
Mon Apr 4 10:07:20 EDT 2016


Greetings (again) Ganesh,

I notice that you ask about how to optimize the code, but I think 
what you mean is you want simpler code that is less repetitive and 
less copy/paste error-prone.  Is that correct?

Below, I make a few suggestions about how to simplify, although, 
there probably is further simplification that could be done.

>I am on python 2.7.10 and Linux.

Noted.  I tested my answer to your question on a Python 2.7, as 
well.

>I have a python function where the similar kind of pattern 
>repeating 100 of times

When you have a repetitive sequence of operations (or test cases, it 
looks like), you can often figure out a way to store the parameters 
as data.  When the parameters become data, then, the code becomes 
simpler.

>Sample code snippet:
>
>test01_log = os.path.join(LOG_DIR, "test01.log")
>        cls.get_baddr['test01'] = failure.run_tool(
>            test01_log, object="inode", offset="18", size="4",
>            optype="set")
>
>test02_log = os.path.join(LOG_DIR, "test02.log")
>        cls.get_baddr['test02'] = failure.run_tool(
>            test02_log, lin=lin_02, object="lin", offset="100", size="5",
>            optype="set")
> ..............................................------------------------
>
>test100_log = os.path.join(LOG_DIR, "test100.log")
>        cls.get_baddr['test100'] = failure.run_tool(
>            test02_log, baddr=lin_02, object="baddr", offset="100", size="5",
>            optype="set")

I observe that here in the failure.run_tool() call, the logfile is 
test02_log.  I would have expected it to be test100_log.  I'm 
guessing that this is exactly the sort of copy/paste error that you 
wish to avoid.

>(1)  Any tips how I can optimize this i.e test case, should have a helper
>function that all test cases call.

One function that jumps out very easily (to my eye) is a function to 
create the logfile name from the test case name.  You will see how I 
do that in the sample, so that you can call the run_tool function as 
you are currently calling it.

(You might consider putting the logfile name generation into the 
run_tool function, though, in which case, run_tool gets even simpler 
and you can get rid of my function, addLogFilename.  Hopefully, that 
makes sense to you....)

>(2) Also note that failure.run_tool function can have variable 
>number of argments how to handle this in the helper function?

A variable number of arguments:  this seems like the perfect case 
for using keyword arguments!

  https://docs.python.org/2/tutorial/controlflow.html#keyword-arguments

I have one additional observation about your sample code, Ganesh.  
When I read this:

  cls.get_baddr['test01'] = failure.run_tool(
           test01_log, object="inode", offset="18", size="4",
           optype="set")

I am guessing that you are calling an external program in your test.  
I also notice that you have string contents in your variables, for 
example, offset="18".  (That's part of why I guess you are calling 
an external program as part of your test.)

If you are calling an external command in 'run_tool', why not put 
the exact command-line you want to execute into the test data.  
This would simplify your code and makes the test more transparent, 
as well.

  d = dict()
  d['test01'] = dict(cmd=['some_command', '--offset', '18', '--size', '4'],
                     optype="set", object='inode')

Then, in run_tool, something like this:

  subprocess.Popen(cmd, shell=False, stderr=logfile)

But, these suggestions are all, basically, different riffs on the 
same basic idea:

  Where you have many testing scenarios, try to figure out a way to 
  store all of the test cases in data and then have the test runner 
  operate on the data.

Good luck,

-Martin


#! /usr/bin/python

from __future__ import absolute_import, division, print_function

import os
import sys
import logging


logging.basicConfig(stream=sys.stderr, level=logging.INFO)
logger = logging.getLogger(__name__)

LOG_DIR = '/var/log/frobnitz'


def createTestCases(LOG_DIR):
    '''create a test case data dictionary with parameters'''
    d = dict()
    d['test01'] = dict(object="inode", offset="18", size="4", optype="set")
    lin_02 = "something"
    d['test02'] = dict(object="lin", lin=lin_02, offset="18", size="5",
                       optype="set")
    d['test100'] = dict(object="baddr", baddr=lin_02, offset="100", size="5",
                        optype="set")
    return addLogFilename(d, LOG_DIR)


def run_tool(logfile, **kw):
    logger.info('%s would execute with %r', logfile, kw)


def addLogFilename(d, logdir):
    '''put the logfile name into the test case data dictionary'''
    for casename, args in d.items():
        args['logfile'] = os.path.join(logdir, casename + '.log')
    return d


def main():
    testcases = createTestCases(LOG_DIR)
    get_baddr = dict()
    for casename, kw in testcases.items():
        # -- yank the logfile name out of the dictionary, before calling func
        logfile = kw.pop('logfile')
        get_baddr[casename] = run_tool(logfile, **kw)


if __name__ == '__main__':
    main()

# -- end of file


-- 
Martin A. Brown
http://linux-ip.net/



More information about the Python-list mailing list