Repository navigation
Expand file tree
/
Copy pathcmdopts.py
More file actions
127 lines (104 loc) · 4.24 KB
/
Copy pathcmdopts.py
File metadata and controls
127 lines (104 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# Copyright © 2011-2024 Splunk, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"): you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Command line utilities shared by command line tools & unit tests."""
from os import path
from optparse import OptionParser
import sys
from dotenv import dotenv_values
__all__ = ["error", "Parser", "cmdline"]
# Print the given message to stderr, and optionally exit
def error(message, exitcode=None):
print(f"Error: {message}", file=sys.stderr)
if exitcode is not None:
sys.exit(exitcode)
class record(dict):
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
self[name] = value
class Parser(OptionParser):
def __init__(self, rules=None, **kwargs):
OptionParser.__init__(self, **kwargs)
self.dests = set({})
self.result = record({"args": [], "kwargs": record()})
if rules is not None:
self.init(rules)
def init(self, rules):
"""Initialize the parser with the given command rules."""
# Initialize the option parser
for dest in rules.keys():
rule = rules[dest]
# Assign defaults ourselves here, instead of in the option parser
# itself in order to allow for multiple calls to parse (dont want
# subsequent calls to override previous values with default vals).
if "default" in rule:
self.result["kwargs"][dest] = rule["default"]
flags = rule["flags"]
kwargs = {"action": rule.get("action", "store")}
# NOTE: Don't provision the parser with defaults here, per above.
for key in ["callback", "help", "metavar", "type"]:
if key in rule:
kwargs[key] = rule[key]
self.add_option(*flags, dest=dest, **kwargs)
# Remember the dest vars that we see, so that we can merge results
self.dests.add(dest)
# Load command options from '.env' file
def load(self, filepath):
argv = []
try:
filedata = dotenv_values(filepath)
except:
error("Unable to open '%s'" % filepath, 2)
# update result kwargs value with .env file data
for key, value in filedata.items():
value = value.strip()
if len(value) == 0 or value is None:
continue # Skip blank value
elif key in self.dests:
self.result["kwargs"][key] = value
else:
raise NameError("No such option --" + key)
return self
def loadif(self, filepath):
"""Load the given filepath if it exists, otherwise ignore."""
if path.isfile(filepath):
self.load(filepath)
return self
def loadenv(self, filename):
dir_path = path.dirname(path.realpath(__file__))
filepath = path.join(dir_path, "..", filename)
self.loadif(filepath)
return self
def parse(self, argv):
"""Parse the given argument vector."""
kwargs, args = self.parse_args(argv)
self.result["args"] += args
# Annoying that parse_args doesn't just return a dict
for dest in self.dests:
value = getattr(kwargs, dest)
if value is not None:
self.result["kwargs"][dest] = value
return self
def format_epilog(self, formatter):
return self.epilog or ""
def cmdline(argv, rules=None, config=None, **kwargs):
"""Simplified cmdopts interface that does not default any parsing rules
and that does not allow compounding calls to the parser."""
parser = Parser(rules, **kwargs)
if config is not None:
parser.loadenv(config)
return parser.parse(argv).result