-
Notifications
You must be signed in to change notification settings - Fork 0
/
plac_ini.py
152 lines (122 loc) · 4.73 KB
/
plac_ini.py
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import sys
try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest
import argparse
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import SafeConfigParser as ConfigParser
import plac
if sys.version >= '3':
from inspect import getfullargspec
else:
from plac import getfullargspec
# defaults are defined by the function
# defaults are overridden with values from config file
# defaults and config file are overridden with command line parameters
CONFIG_PARSER_CFG = getfullargspec(ConfigParser.__init__).args[1:]
# the default arguments accepted by a ConfigParser object
def config_conf(obj):
"Extracts the configuration of the underlying ConfigParser from obj"
# If we ever want to add some default options this is where to do that
cfg = {}
for name in dir(obj):
if name in CONFIG_PARSER_CFG: # argument of ConfigParser
cfg[name] = getattr(obj, name)
return cfg
def config_parser_from(obj, config, default_section=None, **confparams):
conf = config_conf(obj)
conf.update(confparams)
parser = ConfigParser(**conf)
parser.plac_ini_obj = obj
parser.plac_ini_config = config
parser.plac_ini_default_section = default_section
return parser
def _read_config(cp, config, default_section=None):
if sys.version >= '3':
try:
with open(config) as fp:
cp.readfp(fp)
except FileNotFoundError:
# consider raising an exception here.
# but, tools may operate fine without a config file.
return {}
else:
from StringIO import StringIO
try:
# this is needed in Python 2 to work with some kinds of ini files
data = StringIO('\n'.join(line.strip() for line in open(config)))
cp.readfp(data)
except IOError:
# consider raising an exception here.
# but, tools may operate fine without a config file.
return {}
cfg = {}
for section in cp.sections():
if default_section is not None and default_section == section:
prefix = ''
else:
prefix = '%s_' % section
for k, v in cp.items(section):
cfg['%s%s' % (prefix, k)] = v
return cfg
def add_gnu_argument(self, *args, **kwargs):
"Prevent the addition of any single hyphen, multiple letter args"
gnu_args = []
for arg in args:
# Fix if we have at least 3 chars where the first is a hyphen
# and the second is not a hyphen (e.g. -op becomes --op)
if len(arg) > 3 and arg[0] == '-' and arg[1] != '-':
gnu_args.append('-' + arg)
else:
gnu_args.append(arg)
argparse.ArgumentParser.add_argument(self, *gnu_args, **kwargs)
def _print_exit(message, file=None):
if message:
if file is None:
file = sys.stderr
file.write(message)
sys.exit(2)
def call(obj, arglist=sys.argv[1:], eager=True, config=None,
default_section=None, gnu=True):
if gnu:
plac.ArgumentParser.add_argument = add_gnu_argument
if config is None:
return plac.call(obj, arglist=arglist, eager=eager)
argparser = plac.parser_from(obj)
argnames = argparser.argspec.args
defaults = argparser.argspec.defaults
cp = config_parser_from(obj, config, default_section)
cfg = dict(zip_longest(argnames, defaults))
ini_values = _read_config(cp, config, default_section)
for k in obj.__annotations__.keys():
a = plac.Annotation.from_(obj.__annotations__[k])
if a.type and k in ini_values:
if a.type is type(True):
try:
ini_values[k] = cp._convert_to_boolean(ini_values[k])
except ValueError:
argparser.print_usage(sys.stderr)
_print_exit(
"{}: error: {}={} failed conversion to <type 'bool'> in:\n{}\n".format(
argparser.prog, k, ini_values[k], config))
else:
try:
ini_values[k] = a.type(ini_values[k])
except ValueError:
argparser.print_usage(sys.stderr)
_print_exit(
'{}: error: {}={} failed conversion to {} in:\n{}\n'.format(
argparser.prog, k, ini_values[k], a.type, config))
cfg.update(ini_values)
if sys.version >= '3':
items = cfg.items()
else:
items = cfg.iteritems()
argparser.set_defaults(**dict((k, v) for k, v in items))
cmd, result = argparser.consume(arglist)
if plac.iterable(result) and eager: # listify the result
return list(result)
return result