sahl-argparse-helper 2.3.0
Helper functions and classes for argparse
Requires Python: >=3.6,<4.0

# SAHL Argparse Helper

An internal SAHL Python package providing a helper class and utility ounctions for rapid [argparse](https://docs.python.org/3/library/argparse.html) use.

# Usage

A single module is supplied, also called *sahl_argparse_helper*, which contains the available [**CommandLineParser** class](#commandlineparser-class) and [helper functions](#helper-functions).

```python
  from sahl_argparse_helper.sahl_argparse_helper import sahl_argparse_helper
```

# **CommandLineParser** Class

The main available class is **CommandLineParser**. The class is used by instantiating an option which creates a base parser and calling methods to add desired arguments. A description needs to be supplied when an object is instantiated. Methods are available to assist with:

* [program version](#version-arguments)
* [output control](#output-control-arguments)
* [database connections](#db-arguments)
* [datetime and timedelta objects](#datetime-arguments)
* [existing files](#existing-file-arguments)
* [host and port combinations](#host-and-port-combination-arguments)
* [arbitrary arguments](#arbitrary-arguments)

## Program Version

The method **add_version** will include a version, defaulting to *"--version"*.

### Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of version argument")
clp.add_version("1.0.0")

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
$ python demo.py -h
usage: demo.py [-h] [--version]

Demonstration of version argument

optional arguments:
  -h, --help  show this help message and exit
  --version   show program's version number and exit
```

#### Output
```bash
$ python demo.py --version
1.0.0
```

## Output Control Arguments

The methods **add_quiet** and **add_verbose** will suppress or enable text output, setting the **include_output** boolean attribute on the object. If both are included, the value of "*--quiet*" is used.

### Quiet Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of quiet argument")
clp.add_quiet()

clp.parse()
print(f"include_output: {clp.include_output}")
```

#### Help
```bash
usage: demo.py [-h] [-q]

Demonstration of quiet argument

optional arguments:
  -h, --help   show this help message and exit
  -q, --quiet  Whether to suppress output
```

#### Output
```bash
$ python demo.py
include_output: True

$ python demo.py -q
include_output: False
```

### Verbose Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of verbose argument")
clp.add_verbose()

clp.parse()
print(f"include_output: {clp.include_output}")
```

#### Help
```bash
usage: demo.py [-h] [-v]

Demonstration of verbose argument

optional arguments:
  -h, --help   show this help message and exit
  -v, --verbose  Whether to include output
```

#### Output
```bash
$ python demo.py
include_output: False

$ python demo.py -v
include_output: True
```

## DB Arguments

The method **add_db** will include arguments to facilitate database connections.

Available options are:
* host
* username
* password
* port
* schema

If a non-None non-False value is supplied for an option, it is included as an optional argument with the value used as the default.

If a None value is supplied for an option, it is included as a required argument.

If a False value is supplied the option is not included at all.

A *prefix* value can be supplied which is used to prefix the name of each argument.

### Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of DB arguments")
clp.add_db(prefix="db", host=None, username="username", password="password", schema=False, port=False)

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
$ python demo.py -h
usage: demo.py [-h] [--db_username DB_USERNAME]
                     [--db_password DB_PASSWORD]
                     db_host

Demonstration of DB arguments

positional arguments:
  db_host               The database host.

optional arguments:
   -h, --help            show this help message and exit
  --db_username DB_USERNAME
                        The database username. Defaults to "username"
  --db_password DB_PASSWORD
                        The database password. Defaults to the internally
                        stored value
```

#### Output
```bash
$ python demo.py host
                       db_host -> host                 | Type: str
                   db_username -> username             | Type: str
                   db_password -> password             | Type: str
```

## DateTime Arguments

### add_valid_datetime

The method **add_valid_datetime** includes an argument to accept and validate a Python datetime object, in form *YYYY*-*MM*-*DD*T*hh*:*mm*.

### add_valid_datetime_or_timestamp

The method **add_valid_datetime_or_timestamp** includes an argument to accept and validate a Python datetime object, either in form *YYYY*-*MM*-*DD*T*hh*:*mm* for an absolute value or in form *HH*:*MM* for a value relative to the current date and time.

### create_valid_timedelta_type_parser

The method **add_valid_timedelta** includes an argument to accept and validate a Python timedelta object, in form *hh*:*mm*. 0 is acceptable for *h* or *m*.

### Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of DateTime arguments")
clp.add_valid_datetime(["valid_dt_no_none"], "Example of a valid datetime argument, with None not permitted", allow_none=False)
clp.add_valid_datetime(["--valid_dt_none"], "Example of a valid datetime argument, with None permitted", allow_none=True)
clp.add_valid_datetime_or_timestamp(
    ["valid_dt_or_ts_no_none"], "Example of a valid datetime or timestamp argument, with None not permitted", allow_none=False
)
clp.add_valid_datetime_or_timestamp(
    ["--valid_dt_or_ts_none"], "Example of a valid datetime or timestamp argument, with None permitted", allow_none=True
)
clp.add_valid_timedelta(["valid_td"], "Example of a valid timedelta argument")

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
$ python demo.py -h
usage: demo.py [-h] [--valid_dt_none VALID_DT_NONE]
               [--valid_dt_or_ts_none VALID_DT_OR_TS_NONE]
               valid_dt_no_none valid_dt_or_ts_no_none valid_td

Demonstration of DateTime arguments

positional arguments:
  valid_dt_no_none      Example of a valid datetime argument, with None not
                        permitted. An absolute date/time in format YYYY-MM-
                        DDThh:mm.
  valid_dt_or_ts_no_none
                        Example of a valid datetime or timestamp argument,
                        with None not permitted. Either an absolute date/time
                        in format YYYY-MM-DDThh:mm or a time relative to the
                        current date and time, in format hh:mm.
  valid_td              Example of a valid timedelta argument. A timedelta in
                        format hh:mm. 0 is acceptable for hh or mm.

optional arguments:
  -h, --help            show this help message and exit
  --valid_dt_none VALID_DT_NONE
                        Example of a valid datetime argument, with None
                        permitted. An absolute date/time in format YYYY-MM-
                        DDThh:mm.
  --valid_dt_or_ts_none VALID_DT_OR_TS_NONE
                        Example of a valid datetime or timestamp argument,
                        with None permitted. Either an absolute date/time in
                        format YYYY-MM-DDThh:mm or a time relative to the
                        current date and time, in format hh:mm.
```

#### Output
```bash
$ python demo.py 2020-07-01T12:30 22:45 00:15 --valid_dt_or_ts_none 100:00
              valid_dt_no_none -> 2020-07-01 12:30:00            | Type: datetime
                 valid_dt_none -> None                           | Type: NoneType
        valid_dt_or_ts_no_none -> 2020-08-13 14:55:28.240088     | Type: datetime
           valid_dt_or_ts_none -> 2020-08-10 09:40:28.240174     | Type: datetime
                      valid_td -> 0:15:00                        | Type: timedelta
```

## Existing File Arguments

The function **add_existing_file** will add an argument to validate the supplied argument is an existing file. The parser returns the absolute path of the supplied file name.

### Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of Existing File argument")
clp.add_existing_file(["existing_file"], "An existing file")

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
$ python demo.py -h

usage: demo.py [-h] existing_file

Demonstration of Existing File argument

positional arguments:
  existing_file  An existing file. An absolute or relative path to an existing
                 file.

optional arguments:
  -h, --help     show this help message and exit
```

#### Output
```bash
$ python demo.py existing_file 
                 existing_file -> /home/kimv/work/repos/python-packages/sahl-argparse-helper/sahl_argparse_helper/existing_file | Type: str
```

## Host and Port Combination Arguments

The function **add_host_and_port** will add an argument to validate the supplied argument is a HOST:PORT_INTEGER value. The parser returns a tuple of (host, port).

### Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of Host and Port Arguments")
clp.add_host_and_port(["host_port"], "A host and port")

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
usage: demo.py [-h] host_port

Demonstration of Host and Port Arguments

positional arguments:
  host_port   A host and port. A host and port combo. in format HOST:PORT_INTEGER.

optional arguments:
  -h, --help  show this help message and exit
```

#### Output
```bash
$ python demo.py abd:1234
                     host_port -> ('abd', 1234)                  | Type: tuple
```

## Arbitrary Arguments

The function **add_argument** will add an arbitrary argument. Add parameters are passed directly through to the underlying argparse parser's *add_argument* method.

### Example

#### Code
```python
# demo.py
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser

clp = CommandLineParser("Demonstration of Arbitrary Arguments")

clp.add_argument("-a", "--argument", type=int, help="The argument")
clp.add_argument("choice", choices="abcde", help="Choose!")

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
$ python demo.py -h
usage: demo.py [-h] [-a ARGUMENT] {a,b,c,d,e}

Demonstration of Arbitrary Arguments

positional arguments:
  {a,b,c,d,e}           Choose!

optional arguments:
  -h, --help            show this help message and exit
  -a ARGUMENT, --argument ARGUMENT
                        The argument
```

#### Output
```bash
$ python demo.py -a 5 d
                      argument -> 5                              | Type: int
                        choice -> d                              | Type: str
```

# Helper Functions

## *create_parser_from_pyproject* function
The *create_parser_from_pyproject* function is supplied to create a **CommandLineParser** object from a **PyProjectToml** object. The program description and name are set and a version argument is added.

The *project_details.py* file created using the SAHL yeoman generator's Python project options can supply a **PyProjectToml** object from the project's *pyproject.toml* file.

### Example

#### Code
```python
# demo.py
import project_details
from sahl_argparse_helper.sahl_argparse_helper import CommandLineParser, create_parser_from_pyproject

p = project_details.PyProjectToml()
clp = create_parser_from_pyproject(p)

clp.parse()

for (k, v) in vars(clp.args).items():
    print(f"{str(k):>30} -> {str(v):30} | Type: {type(v).__name__}")
```

#### Help
```bash
$ python demo.py -h
usage: sahl_argparse_helper [-h] [--version]

Helper functions and classes for argparse

optional arguments:
  -h, --help  show this help message and exit
  --version   show program's version number and exit
```