preparse 0.1.0.dev2__tar.gz → 0.1.0.dev4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {preparse-0.1.0.dev2/src/preparse.egg-info → preparse-0.1.0.dev4}/PKG-INFO +1 -1
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/pyproject.toml +1 -1
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/_parsing/Parsing.py +58 -29
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/core/PreParser.py +9 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/core/enums.py +7 -0
- preparse-0.1.0.dev4/src/preparse/tests/test_max.py +93 -0
- preparse-0.1.0.dev4/src/preparse/tests/test_max_1.py +51 -0
- preparse-0.1.0.dev4/src/preparse/tests/test_min.py +80 -0
- preparse-0.1.0.dev4/src/preparse/tests/test_min_1.py +103 -0
- preparse-0.1.0.dev4/src/preparse/tests/test_min_2.py +83 -0
- preparse-0.1.0.dev4/src/preparse/tests/test_min_3.py +73 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4/src/preparse.egg-info}/PKG-INFO +1 -1
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse.egg-info/SOURCES.txt +6 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/LICENSE.txt +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/MANIFEST.in +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/README.rst +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/setup.cfg +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/__init__.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/_parsing/__init__.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/core/Click.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/core/__init__.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/core/warnings.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/__init__.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_expit.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_extra.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_extra_no_permutation.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_extra_posix.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_features.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_long_only.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_long_only_no_permutation.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_long_only_posix.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_parse.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_parse_no_permutation.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_parse_posix.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_9001.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_9002.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_9005.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_9006.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_9007.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_even_more.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_warn_more.py +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse.egg-info/dependency_links.txt +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse.egg-info/requires.txt +0 -0
- {preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse.egg-info/top_level.txt +0 -0
|
@@ -29,8 +29,22 @@ class Parsing:
|
|
|
29
29
|
self.ans.extend(self.spec)
|
|
30
30
|
self.spec.clear()
|
|
31
31
|
|
|
32
|
+
def get_nargs_for_letter(self: Self, letter: str) -> Nargs:
|
|
33
|
+
try:
|
|
34
|
+
return self.optdict["-" + letter]
|
|
35
|
+
except KeyError:
|
|
36
|
+
self.warn(
|
|
37
|
+
PreparseInvalidOptionWarning,
|
|
38
|
+
prog=self.parser.prog,
|
|
39
|
+
option=letter,
|
|
40
|
+
)
|
|
41
|
+
return Nargs.NO_ARGUMENT
|
|
42
|
+
|
|
32
43
|
@functools.cached_property
|
|
33
44
|
def islongonly(self: Self) -> bool:
|
|
45
|
+
# if a long option with a single hyphon exists
|
|
46
|
+
# then all options are treated as long options
|
|
47
|
+
# example: -foo
|
|
34
48
|
for k in self.optdict.keys():
|
|
35
49
|
if len(k) < 3:
|
|
36
50
|
continue
|
|
@@ -38,7 +52,6 @@ class Parsing:
|
|
|
38
52
|
continue
|
|
39
53
|
if not k.startswith("-"):
|
|
40
54
|
continue
|
|
41
|
-
# example: -foo
|
|
42
55
|
return True
|
|
43
56
|
return False
|
|
44
57
|
|
|
@@ -51,12 +64,9 @@ class Parsing:
|
|
|
51
64
|
option=self.ans[-1],
|
|
52
65
|
)
|
|
53
66
|
|
|
54
|
-
@
|
|
67
|
+
@property
|
|
55
68
|
def optdict(self: Self) -> Dict[str, Nargs]:
|
|
56
|
-
|
|
57
|
-
for k, v in self.parser.optdict.items():
|
|
58
|
-
ans[str(k)] = Nargs(v)
|
|
59
|
-
return ans
|
|
69
|
+
return self.parser.optdict
|
|
60
70
|
|
|
61
71
|
def possibilities(self: Self, opt: str) -> list[str]:
|
|
62
72
|
if opt in self.optdict.keys():
|
|
@@ -71,26 +81,32 @@ class Parsing:
|
|
|
71
81
|
|
|
72
82
|
def tick(self: Self, optn: str) -> str:
|
|
73
83
|
if optn == "break":
|
|
84
|
+
# if no more options are allowed
|
|
74
85
|
self.spec.extend(self.args)
|
|
75
86
|
self.args.clear()
|
|
76
87
|
return "break"
|
|
77
88
|
arg: str = self.args.pop(0)
|
|
78
89
|
if optn == "open":
|
|
90
|
+
# if a value for an option is already expected
|
|
79
91
|
self.ans.append(arg)
|
|
80
92
|
return "closed"
|
|
81
93
|
if arg == "--":
|
|
94
|
+
# if arg is the special argument
|
|
82
95
|
self.ans.append("--")
|
|
83
96
|
return "break"
|
|
97
|
+
hasgroup: bool = optn == "group"
|
|
84
98
|
if arg.startswith("-") and arg != "-":
|
|
85
|
-
|
|
99
|
+
# if arg is an option
|
|
100
|
+
return self.tick_opt(arg, hasgroup=hasgroup)
|
|
86
101
|
else:
|
|
87
|
-
|
|
102
|
+
# if arg is positional
|
|
103
|
+
return self.tick_pos(arg, hasgroup=hasgroup)
|
|
88
104
|
|
|
89
|
-
def tick_opt(self: Self, arg: str) -> str:
|
|
105
|
+
def tick_opt(self: Self, arg: str, /, *, hasgroup: bool) -> str:
|
|
90
106
|
if arg.startswith("--") or self.islongonly:
|
|
91
107
|
return self.tick_opt_long(arg)
|
|
92
108
|
else:
|
|
93
|
-
return self.tick_opt_short(arg)
|
|
109
|
+
return self.tick_opt_short(arg, hasgroup=hasgroup)
|
|
94
110
|
|
|
95
111
|
def tick_opt_long(self: Self, arg: str) -> str:
|
|
96
112
|
try:
|
|
@@ -135,26 +151,39 @@ class Parsing:
|
|
|
135
151
|
else:
|
|
136
152
|
return "closed"
|
|
137
153
|
|
|
138
|
-
def tick_opt_short(self: Self, arg: str) -> str:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
self.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
def tick_opt_short(self: Self, arg: str, /, *, hasgroup: bool) -> str:
|
|
155
|
+
i: int
|
|
156
|
+
a: str
|
|
157
|
+
nargs: Nargs
|
|
158
|
+
if self.parser.group != Group.MINIMIZE:
|
|
159
|
+
self.ans.append("-")
|
|
160
|
+
if (self.parser.group == Group.MAXIMIZE) and hasgroup:
|
|
161
|
+
self.ans.pop()
|
|
162
|
+
for i, a in enumerate(arg):
|
|
163
|
+
if i == 0:
|
|
164
|
+
continue
|
|
165
|
+
if self.parser.group != Group.MINIMIZE:
|
|
166
|
+
self.ans[-1] += a
|
|
167
|
+
elif a == "-":
|
|
168
|
+
self.ans[-1] += a
|
|
169
|
+
else:
|
|
170
|
+
self.ans.append("-" + a)
|
|
171
|
+
nargs = self.get_nargs_for_letter(a)
|
|
172
|
+
if nargs == Nargs.NO_ARGUMENT:
|
|
173
|
+
continue
|
|
174
|
+
value: str = arg[i + 1 :]
|
|
175
|
+
return self.tick_opt_short_nongroup(nargs=nargs, value=value)
|
|
176
|
+
return "group"
|
|
177
|
+
|
|
178
|
+
def tick_opt_short_nongroup(self: Self, *, nargs: Nargs, value: str) -> str:
|
|
179
|
+
if value:
|
|
180
|
+
self.ans[-1] += value
|
|
155
181
|
return "closed"
|
|
182
|
+
if nargs == Nargs.OPTIONAL_ARGUMENT:
|
|
183
|
+
return "closed"
|
|
184
|
+
return "open"
|
|
156
185
|
|
|
157
|
-
def tick_pos(self: Self, arg: str) -> str:
|
|
186
|
+
def tick_pos(self: Self, arg: str, *, hasgroup: bool) -> str:
|
|
158
187
|
self.spec.append(arg)
|
|
159
188
|
if self.parser.order == Order.POSIX:
|
|
160
189
|
return "break"
|
|
@@ -162,7 +191,7 @@ class Parsing:
|
|
|
162
191
|
self.dumpspec()
|
|
163
192
|
return "closed"
|
|
164
193
|
else:
|
|
165
|
-
return "closed"
|
|
194
|
+
return "group" if hasgroup else "closed"
|
|
166
195
|
|
|
167
196
|
def warn(self: Self, wrncls: type, /, **kwargs: Any) -> None:
|
|
168
197
|
wrn: PreparseWarning = wrncls(**kwargs)
|
|
@@ -20,6 +20,7 @@ __all__ = ["PreParser"]
|
|
|
20
20
|
class PreParser:
|
|
21
21
|
__slots__ = (
|
|
22
22
|
"_abbr",
|
|
23
|
+
"_group",
|
|
23
24
|
"_optdict",
|
|
24
25
|
"_order",
|
|
25
26
|
"_prog",
|
|
@@ -31,6 +32,7 @@ class PreParser:
|
|
|
31
32
|
optdict: Any = None,
|
|
32
33
|
prog: Any = None,
|
|
33
34
|
abbr: Any = Abbr.COMPLETE,
|
|
35
|
+
group: Any = Group.MAINTAIN,
|
|
34
36
|
order: Any = Order.PERMUTE,
|
|
35
37
|
warn: Callable = str,
|
|
36
38
|
) -> None:
|
|
@@ -39,6 +41,7 @@ class PreParser:
|
|
|
39
41
|
self.optdict = optdict
|
|
40
42
|
self.prog = prog
|
|
41
43
|
self.abbr = abbr
|
|
44
|
+
self.group = group
|
|
42
45
|
self.order = order
|
|
43
46
|
self.warn = warn
|
|
44
47
|
|
|
@@ -59,6 +62,11 @@ class PreParser:
|
|
|
59
62
|
"This method returns a copy of the current instance."
|
|
60
63
|
return type(self)(**self.todict())
|
|
61
64
|
|
|
65
|
+
@makeprop()
|
|
66
|
+
def group(self: Self, value: Any) -> dict:
|
|
67
|
+
"This property decides how to approach the grouping of short options."
|
|
68
|
+
return Group(value)
|
|
69
|
+
|
|
62
70
|
@makeprop()
|
|
63
71
|
def optdict(self: Self, value: Any) -> dict:
|
|
64
72
|
"This property gives a dictionary of options."
|
|
@@ -66,6 +74,7 @@ class PreParser:
|
|
|
66
74
|
self._optdict.clear()
|
|
67
75
|
return self._optdict
|
|
68
76
|
value = dict(value)
|
|
77
|
+
value = {str(k): Nargs(v) for k, v in value.items()}
|
|
69
78
|
self._optdict.clear()
|
|
70
79
|
self._optdict.update(value)
|
|
71
80
|
return self._optdict
|
|
@@ -9,6 +9,7 @@ from typing import *
|
|
|
9
9
|
|
|
10
10
|
__all__ = [
|
|
11
11
|
"Abbr",
|
|
12
|
+
"Group",
|
|
12
13
|
"Order",
|
|
13
14
|
"Nargs",
|
|
14
15
|
]
|
|
@@ -26,6 +27,12 @@ class Abbr(BaseEnum):
|
|
|
26
27
|
COMPLETE = 2
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
class Group(BaseEnum):
|
|
31
|
+
MINIMIZE = 0
|
|
32
|
+
MAXIMIZE = 1
|
|
33
|
+
MAINTAIN = 2
|
|
34
|
+
|
|
35
|
+
|
|
29
36
|
class Order(BaseEnum):
|
|
30
37
|
GIVEN = 0
|
|
31
38
|
POSIX = 1
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from preparse.core import Group, Order, PreParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestGroupMaximize(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def parse(self, *, optdict, query, order=Order.GIVEN):
|
|
9
|
+
p = PreParser(order=order, group=Group.MAXIMIZE, optdict=optdict)
|
|
10
|
+
ans = p.parse_args(query)
|
|
11
|
+
self.assertEqual(list(ans), list(p.parse_args(ans)))
|
|
12
|
+
return ans
|
|
13
|
+
|
|
14
|
+
def test_basic_maximize_grouping(self):
|
|
15
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0}
|
|
16
|
+
query = ["-a", "-b", "-c"]
|
|
17
|
+
solution = ["-abc"]
|
|
18
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
19
|
+
self.assertEqual(solution, answer)
|
|
20
|
+
|
|
21
|
+
def test_mixed_with_non_groupable_due_to_argument(self):
|
|
22
|
+
optdict = {"-a": 0, "-b": 1, "-c": 0}
|
|
23
|
+
query = ["-a", "-b", "val", "-c"]
|
|
24
|
+
solution = ["-ab", "val", "-c"] # no further grouping due to -b needing arg
|
|
25
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
26
|
+
self.assertEqual(solution, answer)
|
|
27
|
+
|
|
28
|
+
def test_grouping_across_permuted_positionals(self):
|
|
29
|
+
optdict = {"-x": 0, "-y": 0, "-z": 0}
|
|
30
|
+
query = ["arg1", "-x", "-y", "arg2", "-z"]
|
|
31
|
+
solution = ["-xyz", "arg1", "arg2"]
|
|
32
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
33
|
+
self.assertEqual(solution, answer)
|
|
34
|
+
|
|
35
|
+
def test_grouping_stops_due_to_optional_arg(self):
|
|
36
|
+
optdict = {"-a": 0, "-b": 2, "-c": 0}
|
|
37
|
+
query = ["-a", "-b", "-c"]
|
|
38
|
+
solution = ["-ab", "-c"] # b has optional arg, cannot group
|
|
39
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
40
|
+
self.assertEqual(solution, answer)
|
|
41
|
+
|
|
42
|
+
def test_grouping_preserved_if_possible(self):
|
|
43
|
+
optdict = {"-f": 0, "-g": 0, "-h": 0}
|
|
44
|
+
query = ["-f", "-g"]
|
|
45
|
+
solution = ["-fg"] # compacted
|
|
46
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
47
|
+
self.assertEqual(solution, answer)
|
|
48
|
+
|
|
49
|
+
def test_grouping_multiple_clusters(self):
|
|
50
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0, "-x": 0, "-y": 0}
|
|
51
|
+
query = ["-a", "-b", "-c", "arg", "-x", "-y"]
|
|
52
|
+
solution = ["-abcxy", "arg"]
|
|
53
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
54
|
+
self.assertEqual(solution, answer)
|
|
55
|
+
|
|
56
|
+
def test_grouping_multiple_clusters_given(self):
|
|
57
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0, "-x": 0, "-y": 0}
|
|
58
|
+
query = ["-a", "-b", "-c", "arg", "-x", "-y"]
|
|
59
|
+
solution = ["-abc", "arg", "-xy"]
|
|
60
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.GIVEN)
|
|
61
|
+
self.assertEqual(solution, answer)
|
|
62
|
+
|
|
63
|
+
def test_grouping_multiple_clusters_posix(self):
|
|
64
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0, "-x": 0, "-y": 0}
|
|
65
|
+
query = ["-a", "-b", "-c", "arg", "-x", "-y"]
|
|
66
|
+
solution = ["-abc", "arg", "-x", "-y"]
|
|
67
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
68
|
+
self.assertEqual(solution, answer)
|
|
69
|
+
|
|
70
|
+
def test_cannot_group_across_required_arg(self):
|
|
71
|
+
optdict = {"-m": 0, "-n": 1, "-o": 0}
|
|
72
|
+
query = ["-m", "-n", "data", "-o"]
|
|
73
|
+
solution = ["-mn", "data", "-o"] # -n prevents grouping
|
|
74
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
75
|
+
self.assertEqual(solution, answer)
|
|
76
|
+
|
|
77
|
+
def test_grouping_with_double_dash(self):
|
|
78
|
+
optdict = {"-a": 0, "-b": 0}
|
|
79
|
+
query = ["-a", "--", "-b"]
|
|
80
|
+
solution = ["-a", "--", "-b"] # grouping not done past "--"
|
|
81
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
82
|
+
self.assertEqual(solution, answer)
|
|
83
|
+
|
|
84
|
+
def test_preserve_original_if_only_one(self):
|
|
85
|
+
optdict = {"-q": 0}
|
|
86
|
+
query = ["-q"]
|
|
87
|
+
solution = ["-q"]
|
|
88
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
89
|
+
self.assertEqual(solution, answer)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
unittest.main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from preparse.core import Group, Order, PreParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestGroupMaximizeEdgeCases(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def parse(self, *, optdict, query, order=Order.GIVEN):
|
|
9
|
+
p = PreParser(order=order, group=Group.MAXIMIZE, optdict=optdict)
|
|
10
|
+
ans = p.parse_args(query)
|
|
11
|
+
self.assertEqual(list(ans), list(p.parse_args(ans)))
|
|
12
|
+
return ans
|
|
13
|
+
|
|
14
|
+
def test_combine_with_gap_due_to_argument(self):
|
|
15
|
+
optdict = {"-a": 0, "-b": 1, "-c": 0, "-d": 0}
|
|
16
|
+
query = ["-a", "-b", "value", "-cd"]
|
|
17
|
+
solution = ["-ab", "value", "-cd"]
|
|
18
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
19
|
+
self.assertEqual(solution, answer)
|
|
20
|
+
|
|
21
|
+
def test_do_not_combine_across_double_dash(self):
|
|
22
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0}
|
|
23
|
+
query = ["-a", "--", "-b", "-c"]
|
|
24
|
+
solution = ["-a", "--", "-b", "-c"]
|
|
25
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
26
|
+
self.assertEqual(solution, answer)
|
|
27
|
+
|
|
28
|
+
def test_combine_after_permute(self):
|
|
29
|
+
optdict = {"-x": 0, "-y": 0, "-z": 0}
|
|
30
|
+
query = ["file.txt", "-x", "-y", "-z"]
|
|
31
|
+
solution = ["-xyz", "file.txt"]
|
|
32
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
33
|
+
self.assertEqual(solution, answer)
|
|
34
|
+
|
|
35
|
+
def test_preserve_mixed_grouping_and_single(self):
|
|
36
|
+
optdict = {"-m": 0, "-n": 0, "-o": 0}
|
|
37
|
+
query = ["-m", "-no"]
|
|
38
|
+
solution = ["-mno"]
|
|
39
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
40
|
+
self.assertEqual(solution, answer)
|
|
41
|
+
|
|
42
|
+
def test_merge_respects_optional_args(self):
|
|
43
|
+
optdict = {"-a": 0, "-b": 2, "-c": 0}
|
|
44
|
+
query = ["-a", "-bopt", "-c"]
|
|
45
|
+
solution = ["-abopt", "-c"] # -b consumes opt
|
|
46
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
47
|
+
self.assertEqual(solution, answer)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
unittest.main()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from preparse.core import Group, Order, PreParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestMinimizeGroupParsing(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def parse(self, *, optdict, query):
|
|
9
|
+
p = PreParser(order=Order.GIVEN, group=Group.MINIMIZE, optdict=optdict)
|
|
10
|
+
ans = p.parse_args(query)
|
|
11
|
+
self.assertEqual(list(ans), list(p.parse_args(ans))) # round-trip check
|
|
12
|
+
return ans
|
|
13
|
+
|
|
14
|
+
def test_simple_grouped_flags(self):
|
|
15
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0}
|
|
16
|
+
query = ["-abc"]
|
|
17
|
+
solution = ["-a", "-b", "-c"]
|
|
18
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
19
|
+
self.assertEqual(solution, answer)
|
|
20
|
+
|
|
21
|
+
def test_grouped_with_argument_at_end(self):
|
|
22
|
+
optdict = {"-a": 0, "-b": 0, "-c": 1}
|
|
23
|
+
query = ["-abcvalue"]
|
|
24
|
+
solution = ["-a", "-b", "-cvalue"]
|
|
25
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
26
|
+
self.assertEqual(solution, answer)
|
|
27
|
+
|
|
28
|
+
def test_grouped_with_argument_middle(self):
|
|
29
|
+
optdict = {"-a": 0, "-b": 1, "-c": 0}
|
|
30
|
+
query = ["-abvaluec"]
|
|
31
|
+
solution = ["-a", "-bvaluec"] # because -b consumes "valuec"
|
|
32
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
33
|
+
self.assertEqual(solution, answer)
|
|
34
|
+
|
|
35
|
+
def test_multiple_grouped_chunks(self):
|
|
36
|
+
optdict = {"-x": 0, "-y": 0, "-z": 1}
|
|
37
|
+
query = ["-xy", "-zabc"]
|
|
38
|
+
solution = ["-x", "-y", "-zabc"]
|
|
39
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
40
|
+
self.assertEqual(solution, answer)
|
|
41
|
+
|
|
42
|
+
def test_grouped_with_nonexistent_flag(self):
|
|
43
|
+
optdict = {"-a": 0, "-b": 0}
|
|
44
|
+
query = ["-abc"]
|
|
45
|
+
# assume unknown option "-c" is preserved as-is
|
|
46
|
+
solution = ["-a", "-b", "-c"]
|
|
47
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
48
|
+
self.assertEqual(solution, answer)
|
|
49
|
+
|
|
50
|
+
def test_grouped_with_equals_sign(self):
|
|
51
|
+
optdict = {"-a": 0, "-b": 0, "-c": 1}
|
|
52
|
+
query = ["-abc=foo"]
|
|
53
|
+
solution = ["-a", "-b", "-c=foo"] # assuming `=foo` binds to last opt
|
|
54
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
55
|
+
self.assertEqual(solution, answer)
|
|
56
|
+
|
|
57
|
+
def test_mixed_with_normal_args(self):
|
|
58
|
+
optdict = {"-v": 0, "-f": 1, "-o": 2}
|
|
59
|
+
query = ["-v", "-fooutput.txt", "log.txt", "--", "file1"]
|
|
60
|
+
solution = ["-v", "-fooutput.txt", "log.txt", "--", "file1"]
|
|
61
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
62
|
+
self.assertEqual(solution, answer)
|
|
63
|
+
|
|
64
|
+
def test_double_dash_preserved(self):
|
|
65
|
+
optdict = {"-a": 0, "-b": 0}
|
|
66
|
+
query = ["-ab", "--", "-c"]
|
|
67
|
+
solution = ["-a", "-b", "--", "-c"]
|
|
68
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
69
|
+
self.assertEqual(solution, answer)
|
|
70
|
+
|
|
71
|
+
def test_repeated_grouped_options(self):
|
|
72
|
+
optdict = {"-x": 0, "-y": 0}
|
|
73
|
+
query = ["-xy", "-yx"]
|
|
74
|
+
solution = ["-x", "-y", "-y", "-x"]
|
|
75
|
+
answer = self.parse(optdict=optdict, query=query)
|
|
76
|
+
self.assertEqual(solution, answer)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
unittest.main()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from preparse.core import Group, Order, PreParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestMinimizeGroupParsing(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def parse(self, *, optdict, query):
|
|
9
|
+
p = PreParser(order=Order.GIVEN, group=Group.MINIMIZE, optdict=optdict)
|
|
10
|
+
ans = p.parse_args(query)
|
|
11
|
+
self.assertEqual(list(ans), list(p.parse_args(ans))) # round-trip check
|
|
12
|
+
return ans
|
|
13
|
+
|
|
14
|
+
def test_basic_grouping(self):
|
|
15
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0}
|
|
16
|
+
self.assertEqual(
|
|
17
|
+
self.parse(optdict=optdict, query=["-abc"]), ["-a", "-b", "-c"]
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def test_last_option_takes_argument(self):
|
|
21
|
+
optdict = {"-a": 0, "-b": 0, "-c": 1}
|
|
22
|
+
self.assertEqual(
|
|
23
|
+
self.parse(optdict=optdict, query=["-abcval"]), ["-a", "-b", "-cval"]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def test_argument_in_middle(self):
|
|
27
|
+
optdict = {"-a": 0, "-b": 1, "-c": 0}
|
|
28
|
+
self.assertEqual(
|
|
29
|
+
self.parse(optdict=optdict, query=["-abvalc"]), ["-a", "-bvalc"]
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def test_unknown_short_option_warning_behavior(self):
|
|
33
|
+
optdict = {"-a": 0, "-b": 0}
|
|
34
|
+
self.assertEqual(
|
|
35
|
+
self.parse(optdict=optdict, query=["-abx"]), ["-a", "-b", "-x"]
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def test_equals_sign_treated_as_value(self):
|
|
39
|
+
optdict = {"-a": 0, "-b": 0, "-c": 1}
|
|
40
|
+
self.assertEqual(
|
|
41
|
+
self.parse(optdict=optdict, query=["-abc=foo"]), ["-a", "-b", "-c=foo"]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def test_mixed_grouped_and_separate_options(self):
|
|
45
|
+
optdict = {"-a": 0, "-b": 1, "-v": 0}
|
|
46
|
+
self.assertEqual(
|
|
47
|
+
self.parse(optdict=optdict, query=["-av", "-bvalue"]),
|
|
48
|
+
["-a", "-v", "-bvalue"],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def test_long_option_untouched(self):
|
|
52
|
+
optdict = {"-x": 0, "--file": 1}
|
|
53
|
+
self.assertEqual(
|
|
54
|
+
self.parse(optdict=optdict, query=["-x", "--file=data.txt"]),
|
|
55
|
+
["-x", "--file=data.txt"],
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def test_option_with_dash_value(self):
|
|
59
|
+
optdict = {"-c": 1}
|
|
60
|
+
self.assertEqual(self.parse(optdict=optdict, query=["-c-value"]), ["-c-value"])
|
|
61
|
+
|
|
62
|
+
def test_double_dash_terminator(self):
|
|
63
|
+
optdict = {"-a": 0, "-b": 0}
|
|
64
|
+
self.assertEqual(
|
|
65
|
+
self.parse(optdict=optdict, query=["-ab", "--", "-c"]),
|
|
66
|
+
["-a", "-b", "--", "-c"],
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def test_grouped_with_space_after(self):
|
|
70
|
+
optdict = {"-x": 1, "-y": 0}
|
|
71
|
+
self.assertEqual(
|
|
72
|
+
self.parse(optdict=optdict, query=["-xy", "val"]), ["-xy", "val"]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def test_grouped_option_last_requires_arg_followed_by_space(self):
|
|
76
|
+
optdict = {"-a": 0, "-b": 0, "-c": 1}
|
|
77
|
+
self.assertEqual(
|
|
78
|
+
self.parse(optdict=optdict, query=["-abc", "VAL"]),
|
|
79
|
+
["-a", "-b", "-c", "VAL"],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def test_unrecognized_grouped_mix(self):
|
|
83
|
+
optdict = {"-a": 0}
|
|
84
|
+
self.assertEqual(
|
|
85
|
+
self.parse(optdict=optdict, query=["-abc"]), ["-a", "-b", "-c"]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def test_repeated_group_flags(self):
|
|
89
|
+
optdict = {"-v": 0, "-d": 0}
|
|
90
|
+
self.assertEqual(
|
|
91
|
+
self.parse(optdict=optdict, query=["-vv", "-dd"]), ["-v", "-v", "-d", "-d"]
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def test_combined_behavior_argument_and_unknown(self):
|
|
95
|
+
optdict = {"-a": 0, "-b": 1}
|
|
96
|
+
self.assertEqual(
|
|
97
|
+
self.parse(optdict=optdict, query=["-abvalue", "-c"]),
|
|
98
|
+
["-a", "-bvalue", "-c"],
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
unittest.main()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from preparse.core import Group, Order, PreParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestPermuteAndPosixParsing(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def parse(self, *, optdict, query, order):
|
|
9
|
+
p = PreParser(order=order, group=Group.MINIMIZE, optdict=optdict)
|
|
10
|
+
ans = p.parse_args(query)
|
|
11
|
+
self.assertEqual(list(ans), list(p.parse_args(ans)))
|
|
12
|
+
return ans
|
|
13
|
+
|
|
14
|
+
# --- PERMUTE ---
|
|
15
|
+
|
|
16
|
+
def test_permute_all_positionals_moved(self):
|
|
17
|
+
optdict = {"-a": 0, "-b": 1}
|
|
18
|
+
query = ["input.txt", "-a", "-bvalue", "config.json"]
|
|
19
|
+
solution = ["-a", "-bvalue", "input.txt", "config.json"]
|
|
20
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
21
|
+
self.assertEqual(solution, answer)
|
|
22
|
+
|
|
23
|
+
def test_permute_with_mixed_options(self):
|
|
24
|
+
optdict = {"-x": 0, "-y": 2, "-z": 0}
|
|
25
|
+
query = ["-x", "file1", "-yextra", "file2", "-z"]
|
|
26
|
+
solution = ["-x", "-yextra", "-z", "file1", "file2"]
|
|
27
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
28
|
+
self.assertEqual(solution, answer)
|
|
29
|
+
|
|
30
|
+
def test_permute_grouped_flags_and_positionals(self):
|
|
31
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0}
|
|
32
|
+
query = ["data.csv", "-abc"]
|
|
33
|
+
solution = ["-a", "-b", "-c", "data.csv"]
|
|
34
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
35
|
+
self.assertEqual(solution, answer)
|
|
36
|
+
|
|
37
|
+
def test_permute_with_double_dash(self):
|
|
38
|
+
optdict = {"-v": 0, "-o": 1}
|
|
39
|
+
query = ["-v", "file1", "--", "-oout.txt"]
|
|
40
|
+
solution = ["-v", "--", "file1", "-oout.txt"]
|
|
41
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
42
|
+
self.assertEqual(solution, answer)
|
|
43
|
+
|
|
44
|
+
# --- POSIX ---
|
|
45
|
+
|
|
46
|
+
def test_posix_stops_at_first_positional(self):
|
|
47
|
+
optdict = {"-x": 0, "-y": 0}
|
|
48
|
+
query = ["-x", "main.py", "-y"]
|
|
49
|
+
solution = ["-x", "main.py", "-y"]
|
|
50
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
51
|
+
self.assertEqual(solution, answer)
|
|
52
|
+
|
|
53
|
+
def test_posix_multiple_positionals(self):
|
|
54
|
+
optdict = {"-a": 0, "-b": 1}
|
|
55
|
+
query = ["-a", "input.txt", "-bval", "config.yaml"]
|
|
56
|
+
solution = ["-a", "input.txt", "-bval", "config.yaml"]
|
|
57
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
58
|
+
self.assertEqual(solution, answer)
|
|
59
|
+
|
|
60
|
+
def test_posix_double_dash_preserves_options_after(self):
|
|
61
|
+
optdict = {"-v": 0, "-d": 1}
|
|
62
|
+
query = ["file.txt", "--", "-v", "-dlog.txt"]
|
|
63
|
+
solution = ["file.txt", "--", "-v", "-dlog.txt"]
|
|
64
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
65
|
+
self.assertEqual(solution, answer)
|
|
66
|
+
|
|
67
|
+
def test_posix_with_grouped_options_then_positionals(self):
|
|
68
|
+
optdict = {"-a": 0, "-b": 0, "-c": 0}
|
|
69
|
+
query = ["-abc", "file.txt", "-d"]
|
|
70
|
+
solution = ["-a", "-b", "-c", "file.txt", "-d"]
|
|
71
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
72
|
+
self.assertEqual(solution, answer)
|
|
73
|
+
|
|
74
|
+
def test_posix_optional_arg_ignored_after_positional(self):
|
|
75
|
+
optdict = {"-o": 2, "-v": 0}
|
|
76
|
+
query = ["-o", "input.txt", "file.txt", "-v"]
|
|
77
|
+
solution = ["-o", "input.txt", "file.txt", "-v"]
|
|
78
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
79
|
+
self.assertEqual(solution, answer)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
unittest.main()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from preparse.core import Group, Order, PreParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestMixedArgScenarios(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def parse(self, *, optdict, query, order):
|
|
9
|
+
p = PreParser(order=order, group=Group.MINIMIZE, optdict=optdict)
|
|
10
|
+
ans = p.parse_args(query)
|
|
11
|
+
self.assertEqual(list(ans), list(p.parse_args(ans)))
|
|
12
|
+
return ans
|
|
13
|
+
|
|
14
|
+
def test_mix_positional_long_short_optional_args_permute(self):
|
|
15
|
+
optdict = {"-a": 0, "-b": 1, "-c": 2, "--log": 1, "--debug": 0, "--config": 2}
|
|
16
|
+
query = ["input.txt", "--log=logfile", "-acextra", "--debug", "file.csv"]
|
|
17
|
+
solution = [
|
|
18
|
+
"--log=logfile",
|
|
19
|
+
"-a",
|
|
20
|
+
"-cextra",
|
|
21
|
+
"--debug",
|
|
22
|
+
"input.txt",
|
|
23
|
+
"file.csv",
|
|
24
|
+
]
|
|
25
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
26
|
+
self.assertEqual(solution, answer)
|
|
27
|
+
|
|
28
|
+
def test_mix_positional_long_short_optional_args_posix(self):
|
|
29
|
+
optdict = {"-a": 0, "-b": 1, "-c": 2, "--log": 1, "--debug": 0, "--config": 2}
|
|
30
|
+
query = [
|
|
31
|
+
"--debug",
|
|
32
|
+
"-acextra",
|
|
33
|
+
"--log=logfile",
|
|
34
|
+
"input.txt",
|
|
35
|
+
"--config",
|
|
36
|
+
"settings",
|
|
37
|
+
]
|
|
38
|
+
solution = [
|
|
39
|
+
"--debug",
|
|
40
|
+
"-a",
|
|
41
|
+
"-cextra",
|
|
42
|
+
"--log=logfile",
|
|
43
|
+
"input.txt",
|
|
44
|
+
"--config",
|
|
45
|
+
"settings",
|
|
46
|
+
]
|
|
47
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.POSIX)
|
|
48
|
+
self.assertEqual(solution, answer)
|
|
49
|
+
|
|
50
|
+
def test_optional_and_required_arg_mixed(self):
|
|
51
|
+
optdict = {"-x": 2, "-y": 1, "--mode": 2, "--file": 1}
|
|
52
|
+
query = ["-xyval", "--mode=fast", "--file", "out.txt", "input.dat"]
|
|
53
|
+
solution = ["-xyval", "--mode=fast", "--file", "out.txt", "input.dat"]
|
|
54
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.GIVEN)
|
|
55
|
+
self.assertEqual(solution, answer)
|
|
56
|
+
|
|
57
|
+
def test_long_equals_and_space_combination(self):
|
|
58
|
+
optdict = {"--alpha": 1, "--beta": 1, "-a": 0, "-b": 1}
|
|
59
|
+
query = ["--alpha=one", "--beta", "two", "-a", "-bthree", "final"]
|
|
60
|
+
solution = ["--alpha=one", "--beta", "two", "-a", "-bthree", "final"]
|
|
61
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
62
|
+
self.assertEqual(solution, answer)
|
|
63
|
+
|
|
64
|
+
def test_mix_grouped_and_non_grouped(self):
|
|
65
|
+
optdict = {"-a": 0, "-b": 0, "-c": 1, "--set": 2}
|
|
66
|
+
query = ["-abcval", "--set", "x", "y", "arg1"]
|
|
67
|
+
solution = ["-a", "-b", "-cval", "--set", "x", "y", "arg1"]
|
|
68
|
+
answer = self.parse(optdict=optdict, query=query, order=Order.PERMUTE)
|
|
69
|
+
self.assertEqual(solution, answer)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
unittest.main()
|
|
@@ -25,6 +25,12 @@ src/preparse/tests/test_features.py
|
|
|
25
25
|
src/preparse/tests/test_long_only.py
|
|
26
26
|
src/preparse/tests/test_long_only_no_permutation.py
|
|
27
27
|
src/preparse/tests/test_long_only_posix.py
|
|
28
|
+
src/preparse/tests/test_max.py
|
|
29
|
+
src/preparse/tests/test_max_1.py
|
|
30
|
+
src/preparse/tests/test_min.py
|
|
31
|
+
src/preparse/tests/test_min_1.py
|
|
32
|
+
src/preparse/tests/test_min_2.py
|
|
33
|
+
src/preparse/tests/test_min_3.py
|
|
28
34
|
src/preparse/tests/test_parse.py
|
|
29
35
|
src/preparse/tests/test_parse_no_permutation.py
|
|
30
36
|
src/preparse/tests/test_parse_posix.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{preparse-0.1.0.dev2 → preparse-0.1.0.dev4}/src/preparse/tests/test_long_only_no_permutation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|