Merge tag 'v3.8.2rc2' into 3.8

Python 3.8.2rc2
diff --git a/Lib/argparse.py b/Lib/argparse.py
index fd61bc7..2dad5f1 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2144,24 +2144,23 @@
                 action = self._option_string_actions[option_string]
                 return action, option_string, explicit_arg
 
-        if self.allow_abbrev or not arg_string.startswith('--'):
-            # search through all possible prefixes of the option string
-            # and all actions in the parser for possible interpretations
-            option_tuples = self._get_option_tuples(arg_string)
+        # search through all possible prefixes of the option string
+        # and all actions in the parser for possible interpretations
+        option_tuples = self._get_option_tuples(arg_string)
 
-            # if multiple actions match, the option string was ambiguous
-            if len(option_tuples) > 1:
-                options = ', '.join([option_string
-                    for action, option_string, explicit_arg in option_tuples])
-                args = {'option': arg_string, 'matches': options}
-                msg = _('ambiguous option: %(option)s could match %(matches)s')
-                self.error(msg % args)
+        # if multiple actions match, the option string was ambiguous
+        if len(option_tuples) > 1:
+            options = ', '.join([option_string
+                for action, option_string, explicit_arg in option_tuples])
+            args = {'option': arg_string, 'matches': options}
+            msg = _('ambiguous option: %(option)s could match %(matches)s')
+            self.error(msg % args)
 
-            # if exactly one action matched, this segmentation is good,
-            # so return the parsed action
-            elif len(option_tuples) == 1:
-                option_tuple, = option_tuples
-                return option_tuple
+        # if exactly one action matched, this segmentation is good,
+        # so return the parsed action
+        elif len(option_tuples) == 1:
+            option_tuple, = option_tuples
+            return option_tuple
 
         # if it was not found as an option, but it looks like a negative
         # number, it was meant to be positional
@@ -2185,16 +2184,17 @@
         # split at the '='
         chars = self.prefix_chars
         if option_string[0] in chars and option_string[1] in chars:
-            if '=' in option_string:
-                option_prefix, explicit_arg = option_string.split('=', 1)
-            else:
-                option_prefix = option_string
-                explicit_arg = None
-            for option_string in self._option_string_actions:
-                if option_string.startswith(option_prefix):
-                    action = self._option_string_actions[option_string]
-                    tup = action, option_string, explicit_arg
-                    result.append(tup)
+            if self.allow_abbrev:
+                if '=' in option_string:
+                    option_prefix, explicit_arg = option_string.split('=', 1)
+                else:
+                    option_prefix = option_string
+                    explicit_arg = None
+                for option_string in self._option_string_actions:
+                    if option_string.startswith(option_prefix):
+                        action = self._option_string_actions[option_string]
+                        tup = action, option_string, explicit_arg
+                        result.append(tup)
 
         # single character options can be concatenated with their arguments
         # but multiple character options always have to have their argument
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 9cf5634..fe1706e 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -3,6 +3,8 @@
 ======================================
 
 
+bpo-39663: Add tests for pyparse find_good_parse_start().
+
 bpo-39600: Remove duplicate font names from configuration list.
 
 bpo-38792: Close a shell calltip if a :exc:`KeyboardInterrupt`
diff --git a/Lib/idlelib/idle_test/test_pyparse.py b/Lib/idlelib/idle_test/test_pyparse.py
index a2b13c3..f21baf7 100644
--- a/Lib/idlelib/idle_test/test_pyparse.py
+++ b/Lib/idlelib/idle_test/test_pyparse.py
@@ -58,6 +58,18 @@
         p = self.parser
         setcode = p.set_code
         start = p.find_good_parse_start
+        def char_in_string_false(index): return False
+
+        # First line starts with 'def' and ends with ':', then 0 is the pos.
+        setcode('def spam():\n')
+        eq(start(char_in_string_false), 0)
+
+        # First line begins with a keyword in the list and ends
+        # with an open brace, then 0 is the pos.  This is how
+        # hyperparser calls this function as the newline is not added
+        # in the editor, but rather on the call to setcode.
+        setcode('class spam( ' + ' \n')
+        eq(start(char_in_string_false), 0)
 
         # Split def across lines.
         setcode('"""This is a module docstring"""\n'
@@ -79,7 +91,7 @@
 
         # Make all text look like it's not in a string.  This means that it
         # found a good start position.
-        eq(start(is_char_in_string=lambda index: False), 44)
+        eq(start(char_in_string_false), 44)
 
         # If the beginning of the def line is not in a string, then it
         # returns that as the index.
@@ -98,7 +110,7 @@
                 '    def __init__(self, a, b=True):\n'
                 '        pass\n'
                 )
-        eq(start(is_char_in_string=lambda index: False), 44)
+        eq(start(char_in_string_false), 44)
         eq(start(is_char_in_string=lambda index: index > 44), 44)
         eq(start(is_char_in_string=lambda index: index >= 44), 33)
         # When the def line isn't split, this returns which doesn't match the
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 86ec6cc..0753a4b 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -786,6 +786,23 @@
     ]
 
 
+class TestOptionalsDisallowLongAbbreviationPrefixChars(ParserTestCase):
+    """Disallowing abbreviations works with alternative prefix characters"""
+
+    parser_signature = Sig(prefix_chars='+', allow_abbrev=False)
+    argument_signatures = [
+        Sig('++foo'),
+        Sig('++foodle', action='store_true'),
+        Sig('++foonly'),
+    ]
+    failures = ['+foon 3', '++foon 3', '++food', '++food ++foo 2']
+    successes = [
+        ('', NS(foo=None, foodle=False, foonly=None)),
+        ('++foo 3', NS(foo='3', foodle=False, foonly=None)),
+        ('++foonly 7 ++foodle ++foo 2', NS(foo='2', foodle=True, foonly='7')),
+    ]
+
+
 class TestDisallowLongAbbreviationAllowsShortGrouping(ParserTestCase):
     """Do not allow abbreviations of long options at all"""
 
@@ -804,6 +821,26 @@
         ('-ccrcc', NS(r='cc', c=2)),
     ]
 
+
+class TestDisallowLongAbbreviationAllowsShortGroupingPrefix(ParserTestCase):
+    """Short option grouping works with custom prefix and allow_abbrev=False"""
+
+    parser_signature = Sig(prefix_chars='+', allow_abbrev=False)
+    argument_signatures = [
+        Sig('+r'),
+        Sig('+c', action='count'),
+    ]
+    failures = ['+r', '+c +r']
+    successes = [
+        ('', NS(r=None, c=None)),
+        ('+ra', NS(r='a', c=None)),
+        ('+rcc', NS(r='cc', c=None)),
+        ('+cc', NS(r=None, c=2)),
+        ('+cc +ra', NS(r='a', c=2)),
+        ('+ccrcc', NS(r='cc', c=2)),
+    ]
+
+
 # ================
 # Positional tests
 # ================
diff --git a/Misc/ACKS b/Misc/ACKS
index 21ee85b..69ed251b 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1096,6 +1096,7 @@
 Alexis Métaireau
 Luke Mewburn
 Carl Meyer
+Kyle Meyer
 Mike Meyer
 Piotr Meyer
 Steven Miale
diff --git a/Misc/NEWS.d/next/IDLE/2020-02-17-21-09-03.bpo-39663.wexcsH.rst b/Misc/NEWS.d/next/IDLE/2020-02-17-21-09-03.bpo-39663.wexcsH.rst
new file mode 100644
index 0000000..19e1632
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2020-02-17-21-09-03.bpo-39663.wexcsH.rst
@@ -0,0 +1 @@
+Add tests for pyparse find_good_parse_start().
diff --git a/Misc/NEWS.d/next/Library/2020-02-03-15-12-51.bpo-39546._Kj0Pn.rst b/Misc/NEWS.d/next/Library/2020-02-03-15-12-51.bpo-39546._Kj0Pn.rst
new file mode 100644
index 0000000..8f035e7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-02-03-15-12-51.bpo-39546._Kj0Pn.rst
@@ -0,0 +1,3 @@
+Fix a regression in :class:`~argparse.ArgumentParser` where
+``allow_abbrev=False`` was ignored for long options that used a prefix
+character other than "-".