"""
This is a to_csd file that can serve as a starting point for a Python
console script. To run this script uncomment the following lines in the
``[options.entry_points]`` section in ``setup.cfg``::
console_scripts =
to_csd = csdigit.to_csd:run
Then run ``pip install .`` (or ``pip install -e .`` for editable mode)
which will install the command ``to_csd`` inside your current environment.
Besides console scripts, the header (i.e. until ``_logger``...) of this file can
also be used as template for Python modules.
References:
- https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html
- https://pip.pypa.io/en/stable/reference/pip_install
"""
import argparse
import logging
import sys
from typing import List
from csdigit import __version__
from csdigit.csd import to_csd, to_csdnnz, to_decimal
__author__ = "Wai-Shing Luk"
__copyright__ = "Wai-Shing Luk"
__license__ = "MIT"
# Initialize logger for the module
_logger = logging.getLogger(__name__)
# ---- CLI ----
# The functions defined in this section are wrappers around the main Python
# API allowing them to be called directly from the terminal as a CLI
# executable/script.
[docs]
def parse_args(args: List[str]) -> argparse.Namespace:
"""Parse command line parameters
Args:
args (List[str]): command line parameters as list of strings
(for example ``["--help"]``).
Returns:
:obj:`argparse.Namespace`: command line parameters namespace
"""
# Create argument parser with program description
parser = argparse.ArgumentParser(description="Converts a decimal to a CSD format")
# Add version information argument
parser.add_argument(
"--version",
action="version",
version="csdigit {ver}".format(ver=__version__),
)
# Add conversion options with type checking and default values
parser.add_argument(
"-c",
"--to_csd",
dest="decimal",
help="a decimal number",
type=float,
metavar="FLOAT",
default=float("Inf"), # Use infinity as sentinel value
)
parser.add_argument(
"-f",
"--to_csdnnz",
dest="decimal2",
help="a decimal number",
type=float,
metavar="FLOAT",
default=float("Inf"),
)
parser.add_argument(
"-d",
"--to_decimal",
dest="csdstr",
help="a CSD string",
type=str,
metavar="STR",
default="", # Empty string as sentinel value
)
# Add precision control parameters
parser.add_argument(
"-p",
"--places",
dest="places",
help="How many places",
type=int,
metavar="INT",
default=4,
)
parser.add_argument(
"-z",
"--nnz",
dest="nnz",
help="How many non-zeros",
type=int,
metavar="INT",
default=4,
)
# Add logging level control arguments
parser.add_argument(
"-v",
"--verbose",
dest="loglevel",
help="set loglevel to INFO",
action="store_const",
const=logging.INFO,
)
parser.add_argument(
"-vv",
"--very-verbose",
dest="loglevel",
help="set loglevel to DEBUG",
action="store_const",
const=logging.DEBUG,
)
return parser.parse_args(args)
[docs]
def setup_logging(loglevel: int) -> None:
"""Setup basic logging
Args:
loglevel (int): minimum loglevel for emitting messages
"""
# Configure logging format and output stream
logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
logging.basicConfig(
level=loglevel,
stream=sys.stdout,
format=logformat,
datefmt="%Y-%m-%d %H:%M:%S",
)
[docs]
def main(args: List[str]) -> None:
"""Wrapper allowing :func:`main` to be called with string arguments in a CLI fashion
Instead of returning the value from :func:`main`, it prints the result to the
``stdout`` in a nicely formatted message.
Args:
args (List[str]): command line parameters as list of strings
(for example ``["--verbose", "42"]``).
"""
parsed_args = parse_args(args)
setup_logging(parsed_args.loglevel)
_logger.debug("Starting crazy calculations...")
# Check which conversion option was specified and execute corresponding function
if parsed_args.decimal != float("Inf"):
ans = to_csd(parsed_args.decimal, parsed_args.places)
print(f"{ans}")
if parsed_args.decimal2 != float("Inf"):
ans = to_csdnnz(parsed_args.decimal2, parsed_args.nnz)
print(f"{ans}")
if parsed_args.csdstr != "":
print(f"{to_decimal(parsed_args.csdstr)}")
_logger.info("Script ends here")
[docs]
def run() -> None:
"""Calls :func:`main` passing the CLI arguments extracted from :obj:`sys.argv`
This function can be used as entry point to create console scripts with setuptools.
"""
# Pass all arguments except the script name
main(sys.argv[1:])
if __name__ == "__main__":
# Guard clause to prevent script execution when imported as module
# https://docs.python.org/3/library/__main__.html
# After installing your project with pip, users can also run your Python
# modules as scripts via the ``-m`` flag, as defined in PEP 338::
#
# python -m csdigit.cli --to_csd 42
#
run()