I am writing a python script that provides a more user friendly API to a command line tool. Some of the necessary command calls take a lot of parameters (up to around 10 sometimes), but that is not good practice in Python. They can't just be defaults; it has to be possible to set all the parameters for a given call.
My current structure is an API class that has functions such as expose_image(), and then an interface class to handle the construction of the subprocess command and the call. I don't see that adding more classes will help, as the API class still has to generate and pass the parameters in some way.
One solution I have come up with is to fill a dictionary or namedtuple with the parameters and pass it as **kwargs, which makes things look a little nicer, but less explicit.
Is there a better way of handling this?
Thanks!
It is commendable that you want to build a Pythonic API rather than just an API for this command.
I'm not sure why you disregard default parameters though? If the default is None
, you could treat that as a guide to not add things to the command line.
For example, suppose you want to call the tree command. You could have something like:
def my_tree(dirs_only=False, full_prefix=False, max_level=None, pattern=None):
cmd_line = ['tree']
if dirs_only:
cmd_line.append('-d')
if full_prefix:
cmd_line.append('-f')
if max_level is not None:
cmd_line.append('-L')
cmd_line.append(str(max_level))
if pattern is not None:
cmd_line.append('-P')
cmd_line.append(pattern)
subprocess.do_something_with(cmd_line)
Callers of my_tree
could then interact with it like in the shell:
my_tree()
my_tree(dirs_only=True)
my_tree(pattern='Foo*')
my_tree(pattern='Foo*', max_level=2, full_prefix=True)
In languages such as Java, C# or Dart, you often see "fluent" APIs, and perhaps those might help. It would result in code such as:
my_tree().call()
my_tree().dirs_only().call()
my_tree().with_pattern('Foo*').call()
my_tree() \
.with_pattern('Foo*') \
.with_max_level(2) \
.full_prefix() \
.call()
Though the invocation looks nicer, there is a lot of boilerplate you need to write in order to obtain said niceity, which definitely feels a little bit un-Pythonic.