3
\$\begingroup\$

I am starting to write a CLI using argparse library. This is my first attempt to structure a large project, usually I write single script. The CLI auto is defined in a top directory, helper functions are in a subfolder named project and a main function (where I want to put the logic of my program) in sub-sub-folder named sync.

Here is the tree-structure:

├── top/
├── auto
├── __init__.py
  └── project/
  ├── helper.py
  ├── __init__.py
    └── sync/
    ├── __init__.py
    ├── sync_file.py

helper.py:

def play(device, verbose=False):
    if verbose:
        print device + "with verbose on"
    else:
        print device + "verbose off"

sync_file.py:

from .. import helper

def main(device, verbose=False):
    helper.play(device,verbose)

if __name__ == '__main__':
    #Test here the logic        
    main('omv')
    main('omv',verbose=True)

auto:

#!/usr/bin/env python

import argparse
import project.sync.sync_file as logic_sync


def sync(args):
    if args.verbose:
        logic_sync.main(args.device,verbose=True)
    else:
        logic_sync.main(args.device)

parser = argparse.ArgumentParser(description='Info on my project')
subparsers = parser.add_subparsers()

sync_parser = subparsers.add_parser('sync', help='synchronize')
sync_parser.add_argument('--device', default='nas', help='specify the device name')
sync_parser.add_argument('--verbose', action='store_true', help='increase the output verbose')
sync_parser.set_defaults(func=sync)  # set the default function to sync


if __name__ == '__main__':
    args = parser.parse_args()
    args.func(args)

After I've made the auto file executable chmode +x auto I can use the CLI:

./auto sync
# nasverbose off
./auto sync --verbose
# naswith verbose on

I would like to keep this structure but I see a lot of repetition in propagating the verbose parameter from one file to another. Is there a better way to do it?

\$\endgroup\$
1
\$\begingroup\$

I would create a shared.py module somewhere (I'll let you handle the import directory tree). It would just contain

args = None

Import it in all your modules and assign args from argparser directly to it. Now shared.args contains all the information (even device that you did not ask for :))

Of course this is not reentrant because of the shared global structure, but if you need reentrancy, you just have to pass 1 parameter: a fake args structure like argparse created (see the FakeArgs in syncfile.py)

And when you're ready to create one single python singleton object to avoid too much parameter passing, just yell :)

in auto code

#!/usr/bin/env python

import argparse
import shared
import project.sync.sync_file as logic_sync


def sync(args):
    shared.args = args
    logic_sync.main()

parser = argparse.ArgumentParser(description='Info on my project')
subparsers = parser.add_subparsers()

sync_parser = subparsers.add_parser('sync', help='synchronize')
sync_parser.add_argument('--device', default='nas', help='specify the device name')
sync_parser.add_argument('--verbose', action='store_true', help='increase the output verbose')
sync_parser.set_defaults(func=sync)  # set the default function to sync


if __name__ == '__main__':
    args = parser.parse_args()
    args.func(args)

sync_file.py code:

from .. import helper
import shared

def main():
    helper.play()

if __name__ == '__main__':
    #Test here the logic

    # we don't have argparse module creating the structure
    # for us ? what the heck? create a fake one

    class FakeArgs:
        def __init__(self):
           self.verbose = False
           self.device = 'omv'

    shared.args = FakeArgs()
    main()
    shared.args.verbose = True
    main()

helper.py code:

import shared
def play():
    if shared.args.verbose:
        print shared.args.device + " with verbose on"
    else:
        print shared.args.device + " verbose off"
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your help. Your solution works quite well for me. Beside I have few questions on your comments. What do you mean with 'reentrancy'? How can I create single python singleton to avoid repetition? Can you point me in the right direction (a link to some other code for example) if you cannot give me an example here? thank you \$\endgroup\$ – diegus Oct 3 '16 at 8:24
  • \$\begingroup\$ reentrancy: means that if you're running concurrent threads you cannot modify your arguments: modifying arguments changes for all threads. But you can read the shared arguments across several threads. That's a limitation you may not be subjected to. \$\endgroup\$ – Jean-François Fabre Oct 3 '16 at 9:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.