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?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Sep 27, 2016 at 9:51
\$\endgroup\$

1 Answer 1

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"
answered Sep 28, 2016 at 22:06
\$\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\$ Commented Oct 3, 2016 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\$ Commented Oct 3, 2016 at 9:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.