I'm just trying to learn python: I tend to write my own customized wrappers arround "more complicated" python functionalities so that i can use them more easily, more consistent, easier to change in one place, etc.
I.e. if wrote my own class my_os
with i.e. has the functions rm(path)
or mv(from_path, to_path)
so that on application- or script-level I don't need to know or care (or remember ;)) about the specialities of os
, shutil
, pathlib
or whatever.
I did the same for argparse
. Even though my own functions are quite sparse in this case, I still prefer a named my_add_required_arg
over worrying about the action parameter of the native function in every script I write.
So much to the history of this class:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Wrapper functionality for Pythons built-in argparse package."""
import argparse
class myArgparser(argparse.ArgumentParser):
"""Offers a wrapper for Pythons built-in argparse package.
It offers simple named functions to
* add a brief description
* set a version
* set optional named arguments
* set non optional named arguments
* set optional flags
and then to return the parsed arguments.
"""
def __init__(self, description=None):
"""Create the main argparser object.
Args:
description: The optional description.
"""
super(myArgparser, self).__init__(description=description)
# required arguments is just printed out on "-h"
self.myRequired = self.add_argument_group("required arguments")
def my_set_version(self, version):
"""Set the version information, printed out by --version.
Args:
version: version string
"""
self.add_argument("--version", action="version", version=version)
def my_add_flag(self, short, long, help):
"""Add a boolean flag.
If it is set, the flag argument is set to True, else to False.
Args:
short: the short name, i.e. '-d'
long: the long name, i.e. '--debug'
help: description printed out on '-h'
"""
self.add_argument(short, long, help=help, action="store_true")
def my_add_arg(self, short, long, help):
"""Add an optional named argument of string type.
If it isn't set on the commandline, it defaults to None.
Args:
short: the short name, i.e. '-ld'
long: the long name, i.e. '--logdir'
help: description printed out on '-h'
"""
self.add_argument(short, long, help=help, action="store")
def my_add_required_arg(self, short, long, help):
"""Add an required named argument of string Type.
Args:
short: the short name, i.e. '-ld'
long: the long name, i.e. '--logdir'
help: description printed out on '-h'
"""
self.myRequired.add_argument(
short, long, help=help, action="store", required=True
)
def my_get_args(self):
"""Return the arguments depending on the configured and set arguments.
Return:
Namespace-Object containing values for all added arguments
"""
return self.parse_args()
But using this class, I realized that I was using the same functions with oftenly the same parameters at the beginning of each of my scripts which needed arguments, so i added another function:
@classmethod
def get_args(
cls,
description=None,
version=None,
optional_args=[],
required_args=[],
flags=[],
):
"""Return the parsed arguments.
Each passed argument or flag tuple must contain the short name, the long name and the description
of the argument.
Args:
description: the optional description
version: the optional version string
optional_args: as list of tuples
required_args: as list of tuples
flags: as list of tuples
"""
argparser = cls(description)
if version is not None:
argparser.my_set_version(version)
for arg in optional_args:
argparser.my_add_arg(*arg)
for arg in required_args:
argparser.my_add_required_arg(*arg)
for flag in flags:
argparser.my_add_flag(*flag)
return argparser.my_get_args()
So now I can start a script with a clean argument definition, which I like:
# set global vars & args
version = 'mySpeedtest-20190918'
description = "Test the actual internet connection speed and log the result"
optional_args = [('-ld','--logdir', 'set destination directory for the logfile')]
flags = [('-d', '--debug', 'set loglevel to debug')]
args = my_util.my_argparse.myArgparser.get_args(description, version, optional_args, flags=flags)
Furthermore I don't have bloated init-function and still could use the single functions if I allready strongly had or wanted to.
Now the question: Does it make sense/is it good practice/is it "pythonic" to use a @classmethod
as a pseudo-constructer like this?
(Kind of "Because you can do it, doesn't mean necessarily you should do it!"-Question ;))
-h
for the user mustn't necessarily be same information as the docstring for the developer. \$\endgroup\$