Skip to content

Commit 0c40533

Browse files
gh-126390: Support for preserving order of options and nonoption arguments in gnu_getopt()
1 parent e7c639f commit 0c40533

File tree

5 files changed

+47
-4
lines changed

5 files changed

+47
-4
lines changed

Doc/library/getopt.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ exception:
8585
variable :envvar:`!POSIXLY_CORRECT` is set, then option processing stops as
8686
soon as a non-option argument is encountered.
8787

88+
If the first character of the option string is ``'-'``, non-option arguments
89+
that are followed by options are added to the list of option-and-value pairs
90+
as a pair that has ``None`` as its first element and the list of non-option
91+
arguments as its second element.
92+
The second element of the :func:`!gnu_getopt` result is a list of
93+
program arguments after the last option.
94+
95+
.. versionchanged:: 3.14
96+
Support for returning intermixed options and non-option arguments in order.
97+
8898

8999
.. exception:: GetoptError
90100

@@ -144,6 +154,20 @@ Optional arguments should be specified explicitly:
144154
>>> args
145155
['a1', 'a2']
146156

157+
The order of options and non-option arguments can be preserved:
158+
159+
.. doctest::
160+
161+
>>> s = 'a1 -x a2 a3 a4 --long a5 a6'
162+
>>> args = s.split()
163+
>>> args
164+
['a1', '-x', 'a2', 'a3', 'a4', '--long', 'a5', 'a6']
165+
>>> optlist, args = getopt.gnu_getopt(args, '-x:', ['long='])
166+
>>> optlist
167+
[(None, ['a1']), ('-x', 'a2'), (None, ['a3', 'a4']), ('--long', 'a5')]
168+
>>> args
169+
['a6']
170+
147171
In a script, typical usage is something like this:
148172

149173
.. testcode::

Doc/whatsnew/3.14.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ getopt
316316
* Add support for options with optional arguments.
317317
(Contributed by Serhiy Storchaka in :gh:`126374`.)
318318

319+
* Add support for returning intermixed options and non-option arguments in order.
320+
(Contributed by Serhiy Storchaka in :gh:`126390`.)
321+
319322
http
320323
----
321324

Lib/getopt.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424
# TODO for gnu_getopt():
2525
#
2626
# - GNU getopt_long_only mechanism
27-
# - allow the caller to specify ordering
28-
# - RETURN_IN_ORDER option
29-
# - GNU extension with '-' as first character of option string
3027
# - an option string with a W followed by semicolon should
3128
# treat "-W foo" as "--foo"
3229

@@ -116,8 +113,13 @@ def gnu_getopt(args, shortopts, longopts = []):
116113
else:
117114
longopts = list(longopts)
118115

116+
return_in_order = False
117+
if shortopts.startswith('-'):
118+
shortopts = shortopts[1:]
119+
all_options_first = False
120+
return_in_order = True
119121
# Allow options after non-option arguments?
120-
if shortopts.startswith('+'):
122+
elif shortopts.startswith('+'):
121123
shortopts = shortopts[1:]
122124
all_options_first = True
123125
elif os.environ.get("POSIXLY_CORRECT"):
@@ -131,8 +133,14 @@ def gnu_getopt(args, shortopts, longopts = []):
131133
break
132134

133135
if args[0][:2] == '--':
136+
if return_in_order and prog_args:
137+
opts.append((None, prog_args))
138+
prog_args = []
134139
opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
135140
elif args[0][:1] == '-' and args[0] != '-':
141+
if return_in_order and prog_args:
142+
opts.append((None, prog_args))
143+
prog_args = []
136144
opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
137145
else:
138146
if all_options_first:

Lib/test/test_getopt.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ def test_gnu_getopt(self):
172172
self.assertEqual(args, ['-'])
173173
self.assertEqual(opts, [('-a', ''), ('-b', '-')])
174174

175+
# Return positional arguments intermixed with options.
176+
opts, args = getopt.gnu_getopt(cmdline, '-ab:', ['alpha', 'beta='])
177+
self.assertEqual(args, ['arg2'])
178+
self.assertEqual(opts, [('-a', ''), (None, ['arg1']), ('-b', '1'), ('--alpha', ''),
179+
('--beta', '2'), ('--beta', '3')])
180+
175181
# Posix style via +
176182
opts, args = getopt.gnu_getopt(cmdline, '+ab:', ['alpha', 'beta='])
177183
self.assertEqual(opts, [('-a', '')])
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support for returning intermixed options and non-option arguments in
2+
order in :func:`getopt.gnu_getopt`.

0 commit comments

Comments
 (0)