https://github.com/python/cpython/commit/35010b8cf2e6f5f2791fb336951c518e4f087a43
commit: 35010b8cf2e6f5f2791fb336951c518e4f087a43
branch: main
author: Serhiy Storchaka <storch...@gmail.com>
committer: serhiy-storchaka <storch...@gmail.com>
date: 2024-11-13T22:50:46+02:00
summary:

gh-126390: Support for preserving order of options and nonoption arguments in 
gnu_getopt() (GH-126393)

files:
A Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst
M Doc/library/getopt.rst
M Doc/whatsnew/3.14.rst
M Lib/getopt.py
M Lib/test/test_getopt.py

diff --git a/Doc/library/getopt.rst b/Doc/library/getopt.rst
index def0ea357bceb2..891885d3afbf7a 100644
--- a/Doc/library/getopt.rst
+++ b/Doc/library/getopt.rst
@@ -85,6 +85,16 @@ exception:
    variable :envvar:`!POSIXLY_CORRECT` is set, then option processing stops as
    soon as a non-option argument is encountered.
 
+   If the first character of the option string is ``'-'``, non-option arguments
+   that are followed by options are added to the list of option-and-value pairs
+   as a pair that has ``None`` as its first element and the list of non-option
+   arguments as its second element.
+   The second element of the :func:`!gnu_getopt` result is a list of
+   program arguments after the last option.
+
+   .. versionchanged:: 3.14
+      Support for returning intermixed options and non-option arguments in 
order.
+
 
 .. exception:: GetoptError
 
@@ -144,6 +154,20 @@ Optional arguments should be specified explicitly:
    >>> args
    ['a1', 'a2']
 
+The order of options and non-option arguments can be preserved:
+
+.. doctest::
+
+   >>> s = 'a1 -x a2 a3 a4 --long a5 a6'
+   >>> args = s.split()
+   >>> args
+   ['a1', '-x', 'a2', 'a3', 'a4', '--long', 'a5', 'a6']
+   >>> optlist, args = getopt.gnu_getopt(args, '-x:', ['long='])
+   >>> optlist
+   [(None, ['a1']), ('-x', 'a2'), (None, ['a3', 'a4']), ('--long', 'a5')]
+   >>> args
+   ['a6']
+
 In a script, typical usage is something like this:
 
 .. testcode::
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 31754fb55fcf02..d38188f0054754 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -331,6 +331,9 @@ getopt
 * Add support for options with optional arguments.
   (Contributed by Serhiy Storchaka in :gh:`126374`.)
 
+* Add support for returning intermixed options and non-option arguments in 
order.
+  (Contributed by Serhiy Storchaka in :gh:`126390`.)
+
 http
 ----
 
diff --git a/Lib/getopt.py b/Lib/getopt.py
index c12c08b29c79c4..a9c452a601ee81 100644
--- a/Lib/getopt.py
+++ b/Lib/getopt.py
@@ -24,9 +24,6 @@
 # TODO for gnu_getopt():
 #
 # - GNU getopt_long_only mechanism
-# - allow the caller to specify ordering
-# - RETURN_IN_ORDER option
-# - GNU extension with '-' as first character of option string
 # - an option string with a W followed by semicolon should
 #   treat "-W foo" as "--foo"
 
@@ -63,7 +60,7 @@ def getopt(args, shortopts, longopts = []):
     long options which should be supported.  The leading '--'
     characters should not be included in the option name.  Options
     which require an argument should be followed by an equal sign
-    ('=').  Options which acept an optional argument should be
+    ('=').  Options which accept an optional argument should be
     followed by an equal sign and question mark ('=?').
 
     The return value consists of two elements: the first is a list of
@@ -116,8 +113,13 @@ def gnu_getopt(args, shortopts, longopts = []):
     else:
         longopts = list(longopts)
 
+    return_in_order = False
+    if shortopts.startswith('-'):
+        shortopts = shortopts[1:]
+        all_options_first = False
+        return_in_order = True
     # Allow options after non-option arguments?
-    if shortopts.startswith('+'):
+    elif shortopts.startswith('+'):
         shortopts = shortopts[1:]
         all_options_first = True
     elif os.environ.get("POSIXLY_CORRECT"):
@@ -131,8 +133,14 @@ def gnu_getopt(args, shortopts, longopts = []):
             break
 
         if args[0][:2] == '--':
+            if return_in_order and prog_args:
+                opts.append((None, prog_args))
+                prog_args = []
             opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
         elif args[0][:1] == '-' and args[0] != '-':
+            if return_in_order and prog_args:
+                opts.append((None, prog_args))
+                prog_args = []
             opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
         else:
             if all_options_first:
diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py
index 0675bcbb4e8247..ed967ad27619ae 100644
--- a/Lib/test/test_getopt.py
+++ b/Lib/test/test_getopt.py
@@ -173,6 +173,12 @@ def test_gnu_getopt(self):
         self.assertEqual(args, ['-'])
         self.assertEqual(opts, [('-a', ''), ('-b', '-')])
 
+        # Return positional arguments intermixed with options.
+        opts, args = getopt.gnu_getopt(cmdline, '-ab:', ['alpha', 'beta='])
+        self.assertEqual(args, ['arg2'])
+        self.assertEqual(opts, [('-a', ''), (None, ['arg1']), ('-b', '1'), 
('--alpha', ''),
+                                ('--beta', '2'), ('--beta', '3')])
+
         # Posix style via +
         opts, args = getopt.gnu_getopt(cmdline, '+ab:', ['alpha', 'beta='])
         self.assertEqual(opts, [('-a', '')])
diff --git 
a/Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst 
b/Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst
new file mode 100644
index 00000000000000..3b32bb512f6556
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-11-04-13-16-18.gh-issue-126390.Cxvqa5.rst
@@ -0,0 +1,2 @@
+Add support for returning intermixed options and non-option arguments in
+order in :func:`getopt.gnu_getopt`.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to