Skip to content

Commit 08b522a

Browse files
stinospfalcon
authored andcommitted
argparse: Implement parse_known_args
This is convenient when components need only to parse a subset of an application's arguments, and can be implemented with minor changes to _parse_args: basically just add unknown arguments to a list instead of raising an exception.
1 parent 4c6e7f7 commit 08b522a

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

argparse/argparse.py

+28-5
Original file line numberDiff line numberDiff line change
@@ -144,25 +144,37 @@ def render_arg(arg):
144144
print(" %-16s%s" % (', '.join(opt.names) + render_arg(opt), opt.help))
145145

146146
def parse_args(self, args=None):
147+
return self._parse_args_impl(args, False)
148+
149+
def parse_known_args(self, args=None):
150+
return self._parse_args_impl(args, True)
151+
152+
def _parse_args_impl(self, args, return_unknown):
147153
if args is None:
148154
args = sys.argv[1:]
149155
else:
150156
args = args[:]
151157
try:
152-
return self._parse_args(args)
158+
return self._parse_args(args, return_unknown)
153159
except _ArgError as e:
154160
self.usage(False)
155161
print("error:", e)
156162
sys.exit(2)
157163

158-
def _parse_args(self, args):
164+
def _parse_args(self, args, return_unknown):
159165
# add optional args with defaults
160166
arg_dest = []
161167
arg_vals = []
162168
for opt in self.opt:
163169
arg_dest.append(opt.dest)
164170
arg_vals.append(opt.default)
165171

172+
# deal with unknown arguments, if needed
173+
unknown = []
174+
def consume_unknown():
175+
while args and not args[0].startswith("-"):
176+
unknown.append(args.pop(0))
177+
166178
# parse all args
167179
parsed_pos = False
168180
while args or not parsed_pos:
@@ -179,15 +191,26 @@ def _parse_args(self, args):
179191
found = True
180192
break
181193
if not found:
182-
raise _ArgError("unknown option %s" % a)
194+
if return_unknown:
195+
unknown.append(a)
196+
consume_unknown()
197+
else:
198+
raise _ArgError("unknown option %s" % a)
183199
else:
184200
# positional arg
185201
if parsed_pos:
186-
raise _ArgError("extra args: %s" % " ".join(args))
202+
if return_unknown:
203+
unknown = unknown + args
204+
break
205+
else:
206+
raise _ArgError("extra args: %s" % " ".join(args))
187207
for pos in self.pos:
188208
arg_dest.append(pos.dest)
189209
arg_vals.append(pos.parse(pos.names[0], args))
190210
parsed_pos = True
211+
if return_unknown:
212+
consume_unknown()
191213

192214
# build and return named tuple with arg values
193-
return namedtuple("args", arg_dest)(*arg_vals)
215+
values = namedtuple("args", arg_dest)(*arg_vals)
216+
return (values, unknown) if return_unknown else values

argparse/test_argparse.py

+22
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,25 @@
4444
assert args.files1 == ["a", "b"] and args.files2 == []
4545
args = parser.parse_args(["a", "b", "c"])
4646
assert args.files1 == ["a", "b"] and args.files2 == ["c"]
47+
48+
parser = argparse.ArgumentParser()
49+
parser.add_argument("a", nargs=2)
50+
parser.add_argument("-b")
51+
args, rest = parser.parse_known_args(["a", "b", "-b", "2"])
52+
assert args.a == ["a", "b"] and args.b == "2"
53+
assert rest == []
54+
args, rest = parser.parse_known_args(["-b", "2", "a", "b", "c"])
55+
assert args.a == ["a", "b"] and args.b == "2"
56+
assert rest == ["c"]
57+
args, rest = parser.parse_known_args(["a", "b", "-b", "2", "c"])
58+
assert args.a == ["a", "b"] and args.b == "2"
59+
assert rest == ["c"]
60+
args, rest = parser.parse_known_args(["-b", "2", "a", "b", "-", "c"])
61+
assert args.a == ["a", "b"] and args.b == "2"
62+
assert rest == ["-", "c"]
63+
args, rest = parser.parse_known_args(["a", "b", "-b", "2", "-", "x", "y"])
64+
assert args.a == ["a", "b"] and args.b == "2"
65+
assert rest == ["-", "x", "y"]
66+
args, rest = parser.parse_known_args(["a", "b", "c", "-b", "2", "--x", "5", "1"])
67+
assert args.a == ["a", "b"] and args.b == "2"
68+
assert rest == ["c", "--x", "5", "1"]

0 commit comments

Comments
 (0)