ACL uses enums, some more tests, other stuff.
[python_utils.git] / argparse_utils.py
1 #!/usr/bin/python3
2
3 import argparse
4 import datetime
5 import logging
6 import os
7
8 # This module is commonly used by others in here and should avoid
9 # taking any unnecessary dependencies back on them.
10
11 logger = logging.getLogger(__name__)
12
13
14 class ActionNoYes(argparse.Action):
15     def __init__(
16             self,
17             option_strings,
18             dest,
19             default=None,
20             required=False,
21             help=None
22     ):
23         if default is None:
24             msg = 'You must provide a default with Yes/No action'
25             logger.critical(msg)
26             raise ValueError(msg)
27         if len(option_strings) != 1:
28             msg = 'Only single argument is allowed with YesNo action'
29             logger.critical(msg)
30             raise ValueError(msg)
31         opt = option_strings[0]
32         if not opt.startswith('--'):
33             msg = 'Yes/No arguments must be prefixed with --'
34             logger.critical(msg)
35             raise ValueError(msg)
36
37         opt = opt[2:]
38         opts = ['--' + opt, '--no_' + opt]
39         super().__init__(
40             opts,
41             dest,
42             nargs=0,
43             const=None,
44             default=default,
45             required=required,
46             help=help
47         )
48
49     def __call__(self, parser, namespace, values, option_strings=None):
50         if (
51                 option_strings.startswith('--no-') or
52                 option_strings.startswith('--no_')
53         ):
54             setattr(namespace, self.dest, False)
55         else:
56             setattr(namespace, self.dest, True)
57
58
59 def valid_bool(v):
60     if isinstance(v, bool):
61         return v
62     from string_utils import to_bool
63     return to_bool(v)
64
65
66 def valid_ip(ip: str) -> str:
67     from string_utils import extract_ip_v4
68     s = extract_ip_v4(ip.strip())
69     if s is not None:
70         return s
71     msg = f"{ip} is an invalid IP address"
72     logger.warning(msg)
73     raise argparse.ArgumentTypeError(msg)
74
75
76 def valid_mac(mac: str) -> str:
77     from string_utils import extract_mac_address
78     s = extract_mac_address(mac)
79     if s is not None:
80         return s
81     msg = f"{mac} is an invalid MAC address"
82     logger.warning(msg)
83     raise argparse.ArgumentTypeError(msg)
84
85
86 def valid_percentage(num: str) -> float:
87     num = num.strip('%')
88     n = float(num)
89     if 0.0 <= n <= 100.0:
90         return n
91     msg = f"{num} is an invalid percentage; expected 0 <= n <= 100.0"
92     logger.warning(msg)
93     raise argparse.ArgumentTypeError(msg)
94
95
96 def valid_filename(filename: str) -> str:
97     s = filename.strip()
98     if os.path.exists(s):
99         return s
100     msg = f"{filename} was not found and is therefore invalid."
101     logger.warning(msg)
102     raise argparse.ArgumentTypeError(msg)
103
104
105 def valid_date(txt: str) -> datetime.date:
106     from string_utils import to_date
107     date = to_date(txt)
108     if date is not None:
109         return date
110     msg = f'Cannot parse argument as a date: {txt}'
111     logger.warning(msg)
112     raise argparse.ArgumentTypeError(msg)
113
114
115 def valid_datetime(txt: str) -> datetime.datetime:
116     from string_utils import to_datetime
117     dt = to_datetime(txt)
118     if dt is not None:
119         return dt
120     msg = f'Cannot parse argument as datetime: {txt}'
121     logger.warning(msg)
122     raise argparse.ArgumentTypeError(msg)