cli_test.py
The main file that runs the program
This file is executable and can be called from the terminal like:
foo@bar:~$ cd [path to directory of this file] foo@bar:~[path to directory of this file]$ ./cli_test.py [cmd line]
or if installed in a global location:
foo@bar:~$ cli_test [cmd line]
Typical usage is show in the main() method.
CliTest
The main class, responsible for the operation of the program
Public methods
main: The main method of the program
This class does the most of the work of a typical CLI program. It parses command line options, loads/saves config files, and performs the operations required for the program.
Source code in src/cli_test.py
class CliTest:
"""
The main class, responsible for the operation of the program
Public methods:
main: The main method of the program
This class does the most of the work of a typical CLI program. It parses
command line options, loads/saves config files, and performs the operations
required for the program.
"""
# --------------------------------------------------------------------------
# Class constants
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# find path to project
P_DIR_PRJ = Path(__file__).parents[1].resolve()
# --------------------------------------------------------------------------
# short description
S_PP_SHORT_DESC = "Short description"
# version string
S_PP_VERSION = "Version 0.0.0"
# config option strings
S_ARG_CFG_OPTION = "-c"
S_ARG_CFG_DEST = "CFG_DEST"
# I18N: config file option help
S_ARG_CFG_HELP = _("load configuration from file")
# I18N: config file dest
S_ARG_CFG_METAVAR = _("FILE")
# debug option strings
S_ARG_DBG_OPTION = "-d"
S_ARG_DBG_ACTION = "store_true"
S_ARG_DBG_DEST = "DBG_DEST"
# I18N: debug mode help
S_ARG_DBG_HELP = _("enable debugging mode")
# config option strings
S_ARG_HLP_OPTION = "-h"
S_ARG_HLP_ACTION = "store_true"
S_ARG_HLP_DEST = "HLP_DEST"
# I18N: help option help
S_ARG_HLP_HELP = _("show this help message and exit")
# config option strings
S_ARG_UNINST_OPTION = "--uninstall"
S_ARG_UNINST_ACTION = "store_true"
S_ARG_UNINST_DEST = "UNINST_DEST"
# I18N: uninstall option help
S_ARG_UNINST_HELP = _("uninstall this program")
# about string
S_ABOUT = (
"\n"
"CLI_Test\n"
f"{S_PP_SHORT_DESC}\n"
f"{S_PP_VERSION}\n"
"https://github.com/cyclopticnerve/CLI_Test\n"
)
# I18N if using argparse, add help at end of about
S_ABOUT_HELP = _("Use -h for help") + "\n"
# path to default config file
P_CFG_DEF = Path("conf/cli_test.json")
# uninst not found
# I18N: uninstall not found
S_ERR_NO_UNINST = _("Uninstall files not found")
# --------------------------------------------------------------------------
# Instance methods
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# Initialize the new object
# --------------------------------------------------------------------------
def __init__(self):
"""
Initialize the new object
Initializes a new instance of the class, setting the default values
of its properties, and any other code that needs to run to create a
new object.
"""
# set defaults
# cmd line args
self._dict_args = {}
self._debug = False
self._path_cfg_arg = None
# the final cfg path and dict
self._path_cfg = None
self._dict_cfg = {}
# --------------------------------------------------------------------------
# Public methods
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# The main method of the program
# --------------------------------------------------------------------------
def main(self):
"""
The main method of the program
This method is the main entry point for the program, initializing the
program, and performing its steps.
"""
# ----------------------------------------------------------------------
# setup
# call boilerplate code
self._setup()
# ----------------------------------------------------------------------
# main stuff
# do the thing with the thing
print(self._func())
# ----------------------------------------------------------------------
# teardown
# call boilerplate code
self._teardown()
# --------------------------------------------------------------------------
# Private methods
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# Short description
# --------------------------------------------------------------------------
def _func(self):
"""
Short description
Args:
var_name: Short description
Returns:
Description
Raises:
exception_type(vars): Description
Long description (including HTML).
"""
# check for debug flag
if self._debug:
# I18N: context for this string
return _("this is func (DEBUG)")
# no debug, return normal result
# I18N: context for this string
return _("this is func")
# --------------------------------------------------------------------------
# Boilerplate to use at the start of main
# --------------------------------------------------------------------------
def _setup(self):
"""
Boilerplate to use at the start of main
Perform some mundane stuff like running the arg parser and loading
config files.
"""
# print default about text
print(self.S_ABOUT)
# ----------------------------------------------------------------------
# use cmd line
# create a parser object in case we need it
parser = argparse.ArgumentParser(
formatter_class=CNFormatter, add_help=False
)
# add help text to about block
print(self.S_ABOUT_HELP)
# add cfg option
parser.add_argument(
self.S_ARG_CFG_OPTION,
dest=self.S_ARG_CFG_DEST,
help=self.S_ARG_CFG_HELP,
metavar=self.S_ARG_CFG_METAVAR,
)
# add debug option
parser.add_argument(
self.S_ARG_DBG_OPTION,
action=self.S_ARG_DBG_ACTION,
dest=self.S_ARG_DBG_DEST,
help=self.S_ARG_DBG_HELP,
)
# add help option
parser.add_argument(
self.S_ARG_HLP_OPTION,
action=self.S_ARG_HLP_ACTION,
dest=self.S_ARG_HLP_DEST,
help=self.S_ARG_HLP_HELP,
)
# add help option
parser.add_argument(
self.S_ARG_UNINST_OPTION,
action=self.S_ARG_UNINST_ACTION,
dest=self.S_ARG_UNINST_DEST,
help=self.S_ARG_UNINST_HELP,
)
# run the parser
args = parser.parse_args()
self._dict_args = vars(args)
# if -h passed, this will print and exit
if self._dict_args.get(self.S_ARG_HLP_DEST, False):
parser.print_help()
print()
sys.exit(-1)
# set props from args
self._debug = self._dict_args.get(self.S_ARG_DBG_DEST, self._debug)
self._path_cfg_arg = self._dict_args.get(
self.S_ARG_CFG_DEST, self._path_cfg_arg
)
# punt to sub func
if self._dict_args.get(self.S_ARG_UNINST_DEST, False):
self._do_uninstall()
# ----------------------------------------------------------------------
# use cfg
# fix paths and get cfg to load
self._get_path_cfg()
# load config file (or not, if no param and not using -c)
self._load_config()
# --------------------------------------------------------------------------
# Boilerplate to use at the end of main
# --------------------------------------------------------------------------
def _teardown(self):
"""
Boilerplate to use at the end of main
Perform some mundane stuff like saving config files.
"""
# ----------------------------------------------------------------------
# use cfg
# call to save config
self._save_config()
# --------------------------------------------------------------------------
# Get path to config file from cmd line option or default
# --------------------------------------------------------------------------
def _get_path_cfg(self):
"""
Get path to config file from cmd line option or default
This method figures the config path from either:
1. the command line -c option (if present)
or
2. the self.P_CFG_DEF value (if present)
If you use the -c option, and the file exists, it will used as the
_path_cfg property, and processing stops.
If you do not use the -c option, or it is not present on the command
line, the path_def value will be used.
If you use neither, nothing happens to the _path_cfg property.
"""
# accept path or str
path_def = self.P_CFG_DEF
if path_def:
path_def = Path(path_def)
if not path_def.is_absolute():
# make abs rel to self
path_def = self.P_DIR_PRJ / path_def
# accept path or str
if self._path_cfg_arg:
self._path_cfg_arg = Path(self._path_cfg_arg)
if not self._path_cfg_arg.is_absolute():
# make abs rel to self
self._path_cfg_arg = self.P_DIR_PRJ / self._path_cfg_arg
# ----------------------------------------------------------------------
# assume def
if path_def:
self._path_cfg = path_def
# arg supersedes def
if self._path_cfg_arg:
self._path_cfg = self._path_cfg_arg
# --------------------------------------------------------------------------
# Load config data from a file
# --------------------------------------------------------------------------
def _load_config(self):
"""
Load config data from a file
This method loads data from a config file. It is written to load a dict
from a json file, but it can be used for other formats as well. It uses
the values of self._dict_cfg and self._path_cfg to load the config
data.
"""
# if one or the other, load it
if self._path_cfg and self._path_cfg.exists():
self._dict_cfg = F.load_dicts([self._path_cfg], self._dict_cfg)
# throw in a debug test
if self._debug:
print("load cfg from:", self._path_cfg)
F.pp(self._dict_cfg, label="load cfg")
# --------------------------------------------------------------------------
# Save config data to a file
# --------------------------------------------------------------------------
def _save_config(self):
"""
Save config data to a file
This method saves the config data to the same file it was loaded from.
It is written to save a dict to a json file, but it can be used for
other formats as well. It uses the values of self._dict_cfg and
self._path_cfg to save the config data.
"""
# save dict to path
if self._path_cfg:
self._path_cfg.parent.mkdir(parents=True, exist_ok=True)
F.save_dict(self._dict_cfg, [self._path_cfg])
# throw in a debug test
if self._debug:
print("save cfg to:", self._path_cfg)
F.pp(self._dict_cfg, label="save cfg")
# --------------------------------------------------------------------------
# Handle the --uninstall cmd line op
# --------------------------------------------------------------------------
def _do_uninstall(self):
"""
Handle the -- uninstall cmd line op
"""
# find uninstall file
path_uninst = (
Path.home() / ".local/share/cli_test/uninstall.py"
)
# if path exists
if path_uninst.exists():
# run uninstall and exit
cmd = str(path_uninst)
subprocess.run(cmd, shell=True, check=True)
sys.exit(-1)
else:
print(self.S_ERR_NO_UNINST)
sys.exit(-1)
__init__()
Initialize the new object
Initializes a new instance of the class, setting the default values of its properties, and any other code that needs to run to create a new object.
Source code in src/cli_test.py
def __init__(self):
"""
Initialize the new object
Initializes a new instance of the class, setting the default values
of its properties, and any other code that needs to run to create a
new object.
"""
# set defaults
# cmd line args
self._dict_args = {}
self._debug = False
self._path_cfg_arg = None
# the final cfg path and dict
self._path_cfg = None
self._dict_cfg = {}
main()
The main method of the program
This method is the main entry point for the program, initializing the program, and performing its steps.
Source code in src/cli_test.py
def main(self):
"""
The main method of the program
This method is the main entry point for the program, initializing the
program, and performing its steps.
"""
# ----------------------------------------------------------------------
# setup
# call boilerplate code
self._setup()
# ----------------------------------------------------------------------
# main stuff
# do the thing with the thing
print(self._func())
# ----------------------------------------------------------------------
# teardown
# call boilerplate code
self._teardown()