Skip to content

Commit 5308909

Browse files
Merge pull request #40 from martinkilbinger/args
read param script
2 parents 397604a + c277a68 commit 5308909

9 files changed

Lines changed: 355 additions & 8 deletions

File tree

cs_util/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@
2323

2424
from warnings import warn
2525

26-
__version__ = "0.0.7"
26+
__version__ = "0.0.8"

cs_util/args.py

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
"""ARGS.
2+
3+
:Description: Handling of command line arguments.
4+
5+
:Author: Martin Kilbinger <martin.kilbinger@cea.fr>
6+
7+
8+
"""
9+
10+
import sys
11+
import os
12+
13+
from optparse import OptionParser
14+
15+
16+
def parse_options(p_def, short_options, types, help_strings, args=None):
17+
"""Parse command line options.
18+
19+
Parameters
20+
----------
21+
p_def : dict
22+
default parameter values
23+
short_options : dict
24+
command line options short (one character) versions
25+
types : dict
26+
command line options types
27+
help_strings : dict
28+
command line options help strings
29+
args : list, optional
30+
list of arguments, default is None, in which case sys.args[1:]
31+
is used
32+
33+
Returns
34+
-------
35+
options: tuple
36+
Command line options
37+
38+
"""
39+
if args is None:
40+
args = sys.argv[1:]
41+
42+
usage = "%prog [OPTIONS]"
43+
parser = OptionParser(usage=usage)
44+
45+
# Loop over default parameter values
46+
for key in p_def:
47+
# Process if help string exists
48+
if key in help_strings:
49+
# Set short option
50+
short = short_options[key] if key in short_options else ""
51+
52+
# Set type
53+
typ = types[key] if key in types else "string"
54+
55+
# Special case: bool type
56+
if typ == "bool":
57+
parser.add_option(
58+
f"{short}",
59+
f"--{key}",
60+
dest=key,
61+
default=False,
62+
action="store_true",
63+
help=help_strings[key].format(p_def[key]),
64+
)
65+
else:
66+
parser.add_option(
67+
short,
68+
f"--{key}",
69+
dest=key,
70+
type=typ,
71+
default=p_def[key],
72+
help=help_strings[key].format(p_def[key]),
73+
)
74+
75+
# Add verbose option
76+
parser.add_option(
77+
"-v",
78+
"--verbose",
79+
dest="verbose",
80+
action="store_true",
81+
help="verbose output",
82+
)
83+
84+
options_values, my_args = parser.parse_args(args)
85+
86+
# Transform parameter values to dict
87+
options = {
88+
key: getattr(options_values, key) for key in vars(options_values)
89+
}
90+
91+
# Add other default values which do not have argument option
92+
for key in p_def:
93+
if key not in options:
94+
options[key] = p_def[key]
95+
96+
return options
97+
98+
99+
def my_string_split(string, num=-1, verbose=False, stop=False, sep=None):
100+
"""My String Split.
101+
102+
Split a *string* into a list of strings. Choose as separator
103+
the first in the list [space, underscore] that occurs in the string.
104+
(Thus, if both occur, use space.)
105+
106+
Parameters
107+
----------
108+
string : str
109+
Input string
110+
num : int
111+
Required length of output list of strings, -1 if no requirement.
112+
verbose : bool
113+
Verbose output
114+
stop : bool
115+
Stop programs with error if True, return None and continues otherwise
116+
sep : bool
117+
Separator, try ' ', '_', and '.' if None (default)
118+
119+
Raises
120+
------
121+
ValueError
122+
If number of elements in string and num are different, for stop=True
123+
If no separator found in string
124+
125+
Returns
126+
-------
127+
list
128+
List of string on success, and None if failed
129+
130+
"""
131+
if string is None:
132+
return None
133+
134+
if sep is None:
135+
has_space = string.find(" ")
136+
has_underscore = string.find("_")
137+
has_dot = string.find(".")
138+
139+
if has_space != -1:
140+
my_sep = " "
141+
elif has_underscore != -1:
142+
my_sep = "_"
143+
elif has_dot != -1:
144+
my_sep = "."
145+
else:
146+
# no separator found, does string consist of only one element?
147+
if num == -1 or num == 1:
148+
my_sep = None
149+
else:
150+
raise ValueError(
151+
"No separator (' ', '_', or '.') found in string"
152+
+ f" '{string}', cannot split"
153+
)
154+
else:
155+
if not string.find(sep):
156+
raise ValueError(
157+
f"No separator '{sep}' found in string '{string}' cannot split"
158+
)
159+
my_sep = sep
160+
161+
res = string.split(my_sep)
162+
163+
if num != -1 and num != len(res) and stop:
164+
raise ValueError(
165+
f"String '{len(res)}' has length {num}, required is {num}"
166+
)
167+
168+
return res
169+
170+
171+
def read_param_script(params_in_path, params_def, verbose=False):
172+
"""Read Params Scritpt.
173+
174+
Reads a python parameter script, or prompts user for input if file does not
175+
exist.
176+
Returns updated parameters.
177+
178+
Parameters
179+
----------
180+
params_in_path : str
181+
input file path
182+
params : dict
183+
default parameters
184+
verbose : bool, optional
185+
verbose output if ``True``; default is ``False``
186+
187+
Returns
188+
-------
189+
dict :
190+
updated parameters
191+
192+
"""
193+
params_in = {}
194+
params_out = {}
195+
for key in params_def:
196+
params_out[key] = params_def[key]
197+
198+
if os.path.exists(params_in_path):
199+
if verbose:
200+
print(f"Reading parameter script {params_in_path}")
201+
with open(params_in_path) as f:
202+
exec(f.read())
203+
# Set instance parameters, copy from above
204+
for key in params_in:
205+
params_out[key] = params_in[key]
206+
else:
207+
print(
208+
f"Configuration script {params_in_path} not found, asking for user input"
209+
)
210+
for key in params_def:
211+
msg = f"{key}? [{params_def[key]}] "
212+
val_user = input(msg)
213+
if val_user != "":
214+
params_out[key] = val_user
215+
216+
return params_out

cs_util/cfis.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
99
"""
1010

11-
1211
import re
1312
import numpy as np
1413
from astropy import units

cs_util/logging.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
88
"""
99

10-
1110
import os
1211
import sys
1312

cs_util/plots.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
99
"""
1010

11-
1211
import matplotlib.pylab as plt
1312
import numpy as np
1413

@@ -207,7 +206,7 @@ def plot_data_1d(
207206
markers=None,
208207
xlim=None,
209208
ylim=None,
210-
shift_x=True,
209+
shift_x=False,
211210
close_fig=True,
212211
):
213212
"""Plot Data 1D.

cs_util/tests/test_args.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""UNIT TESTS FOR ARGS SUBPACKAGE.
2+
3+
This module contains unit tests for the args subpackage.
4+
5+
"""
6+
7+
import os
8+
9+
import numpy as np
10+
from numpy import testing as npt
11+
import optparse
12+
import pytest
13+
14+
from unittest import TestCase
15+
from unittest.mock import patch
16+
17+
from cs_util import args
18+
19+
20+
class ArgsTestCase(TestCase):
21+
"""Test case for the ``args`` module."""
22+
23+
def setUp(self):
24+
"""Set test parameter values."""
25+
self._params_def = {
26+
"p_int": 1,
27+
"p_float": 0.612,
28+
"p_str": "some_string",
29+
"p_bool": -1,
30+
"p_Bool": -1,
31+
"p_str_def": "second_string",
32+
"p_none": "",
33+
}
34+
self._types = {
35+
"p_int": "int",
36+
"p_float": "float",
37+
"p_bool": "bool",
38+
"p_Bool": "bool",
39+
"p_str": "str",
40+
}
41+
self._short_options = {
42+
"p_int": "-i",
43+
"p_float": "-f",
44+
"p_bool": "-b",
45+
"p_Bool": "-B",
46+
"p_str": "-s",
47+
}
48+
self._help_strings = {
49+
"p_int": "integer option, default={}",
50+
"p_float": "float option, default={}",
51+
"p_bool": "bool option, set to True if given",
52+
"p_Bool": "bool option, set to True if given",
53+
"p_str": "string option, default={}",
54+
}
55+
self._string0 = None
56+
self._string1 = "abc"
57+
self._string3 = "a b c"
58+
59+
def tearDown(self):
60+
"""Unset test parameter values."""
61+
self._params_def = None
62+
self._types = None
63+
self._short_options = None
64+
self._help_strings = None
65+
self._string1 = None
66+
67+
def test_parse_options(self):
68+
"""Test `cs_util.args.parse_options` method."""
69+
self._options = args.parse_options(
70+
self._params_def,
71+
self._short_options,
72+
self._types,
73+
self._help_strings,
74+
args=["-i", "2", "-s", "test", "-b"],
75+
)
76+
77+
# Test updated options
78+
npt.assert_equal(self._options["p_int"], 2)
79+
npt.assert_equal(self._options["p_str"], "test")
80+
npt.assert_equal(self._options["p_bool"], True)
81+
82+
# Test unchanged (default) options
83+
npt.assert_equal(self._options["p_float"], 0.612)
84+
npt.assert_equal(self._options["p_Bool"], False)
85+
npt.assert_equal(self._options["p_str_def"], "second_string")
86+
87+
# Test default args array
88+
test_argv = [None, "-i", "2"]
89+
with patch("sys.argv", test_argv):
90+
self._options = args.parse_options(
91+
self._params_def,
92+
self._short_options,
93+
self._types,
94+
self._help_strings,
95+
args=None,
96+
)
97+
npt.assert_equal(self._options["p_int"], 2)
98+
99+
def test_my_string_split(self):
100+
"""Test `cs_util.args.my_string_split` method."""
101+
102+
# Test string=None
103+
results = args.my_string_split(self._string0)
104+
self.assertIsNone(results)
105+
106+
# Test string without separator
107+
results = args.my_string_split(self._string1)
108+
npt.assert_equal(len(results), 1)
109+
npt.assert_equal(results[0], self._string1)
110+
111+
# Test string with separators
112+
results = args.my_string_split(self._string3)
113+
npt.assert_equal(len(results), 3)
114+
for idx in (0, 1, 2):
115+
npt.assert_equal(results[idx], self._string3[idx * 2])
116+
117+
# Test different separator
118+
results = args.my_string_split(self._string3, sep="_")
119+
npt.assert_equal(len(results), 1)
120+
npt.assert_equal(results[0], self._string3)
121+
122+
# Test mismatching number of substrings
123+
# Exception for stop=True
124+
with self.assertRaises(ValueError):
125+
args.my_string_split(self._string3, num=2, stop=True)
126+
# Return value when stop is False
127+
results = args.my_string_split(self._string3, num=2, stop=False)
128+
npt.assert_equal(len(results), 3)
129+
for idx in (0, 1, 2):
130+
npt.assert_equal(results[idx], self._string3[idx * 2])

0 commit comments

Comments
 (0)