Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into unifyunion
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed Sep 29, 2024
2 parents 0785951 + 95581b3 commit 1a39ded
Show file tree
Hide file tree
Showing 31 changed files with 458 additions and 260 deletions.
2 changes: 1 addition & 1 deletion Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1823,7 +1823,7 @@ Sub-commands
>>>
>>> # create the parser for the "b" command
>>> parser_b = subparsers.add_parser('b', help='b help')
>>> parser_b.add_argument('--baz', choices='XYZ', help='baz help')
>>> parser_b.add_argument('--baz', choices=('X', 'Y', 'Z'), help='baz help')
>>>
>>> # parse some argument lists
>>> parser.parse_args(['a', '12'])
Expand Down
4 changes: 3 additions & 1 deletion Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2357,7 +2357,9 @@ types.

Backward-compatible usage::

# For creating a generic NamedTuple on Python 3.11 or lower
# For creating a generic NamedTuple on Python 3.11
T = TypeVar("T")

class Group(NamedTuple, Generic[T]):
key: T
group: list[T]
Expand Down
5 changes: 2 additions & 3 deletions Include/cpython/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ PyAPI_FUNC(int) _PyLong_Sign(PyObject *v);
absolute value of a long. For example, this returns 1 for 1 and -1, 2
for 2 and -2, and 2 for 3 and -3. It returns 0 for 0.
v must not be NULL, and must be a normalized long.
(uint64_t)-1 is returned and OverflowError set if the true result doesn't
fit in a size_t.
Always successful.
*/
PyAPI_FUNC(uint64_t) _PyLong_NumBits(PyObject *v);
PyAPI_FUNC(int64_t) _PyLong_NumBits(PyObject *v);

/* _PyLong_FromByteArray: View the n unsigned bytes as a binary integer in
base 256, and return a Python int with the same numeric value.
Expand Down
11 changes: 11 additions & 0 deletions Include/internal/pycore_codecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ extern void _PyCodec_Fini(PyInterpreterState *interp);

extern PyObject* _PyCodec_Lookup(const char *encoding);

/*
* Un-register the error handling callback function registered under
* the given 'name'. Only custom error handlers can be un-registered.
*
* - Return -1 and set an exception if 'name' refers to a built-in
* error handling name (e.g., 'strict'), or if an error occurred.
* - Return 0 if no custom error handler can be found for 'name'.
* - Return 1 if the custom error handler was successfully removed.
*/
extern int _PyCodec_UnregisterError(const char *name);

/* Text codec specific encoding and decoding API.
Checks the encoding against a list of codecs which do not
Expand Down
11 changes: 5 additions & 6 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ static inline PyObject* _PyLong_FromUnsignedChar(unsigned char i)
}

// _PyLong_Frexp returns a double x and an exponent e such that the
// true value is approximately equal to x * 2**e. e is >= 0. x is
// true value is approximately equal to x * 2**e. x is
// 0.0 if and only if the input is 0 (in which case, e and x are both
// zeroes); otherwise, 0.5 <= abs(x) < 1.0. On overflow, which is
// possible if the number of bits doesn't fit into a Py_ssize_t, sets
// OverflowError and returns -1.0 for x, 0 for e.
// zeroes); otherwise, 0.5 <= abs(x) < 1.0.
// Always successful.
//
// Export for 'math' shared extension
PyAPI_DATA(double) _PyLong_Frexp(PyLongObject *a, int64_t *e);
Expand All @@ -105,10 +104,10 @@ PyAPI_DATA(PyObject*) _PyLong_DivmodNear(PyObject *, PyObject *);
PyAPI_DATA(PyObject*) _PyLong_Format(PyObject *obj, int base);

// Export for 'math' shared extension
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, uint64_t);
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, int64_t);

// Export for 'math' shared extension
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, uint64_t);
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, int64_t);

PyAPI_FUNC(PyObject*) _PyLong_Add(PyLongObject *left, PyLongObject *right);
PyAPI_FUNC(PyObject*) _PyLong_Multiply(PyLongObject *left, PyLongObject *right);
Expand Down
95 changes: 46 additions & 49 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,8 @@ def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, key, value)

if arg_strings:
vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
if not hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
setattr(namespace, _UNRECOGNIZED_ARGS_ATTR, [])
getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)

class _ExtendAction(_AppendAction):
Expand Down Expand Up @@ -1927,11 +1928,11 @@ def _parse_known_args(self, arg_strings, namespace):
# otherwise, add the arg to the arg strings
# and note the index if it was an option
else:
option_tuple = self._parse_optional(arg_string)
if option_tuple is None:
option_tuples = self._parse_optional(arg_string)
if option_tuples is None:
pattern = 'A'
else:
option_string_indices[i] = option_tuple
option_string_indices[i] = option_tuples
pattern = 'O'
arg_string_pattern_parts.append(pattern)

Expand Down Expand Up @@ -1966,8 +1967,16 @@ def take_action(action, argument_strings, option_string=None):
def consume_optional(start_index):

# get the optional identified at this index
option_tuple = option_string_indices[start_index]
action, option_string, sep, explicit_arg = option_tuple
option_tuples = option_string_indices[start_index]
# if multiple actions match, the option string was ambiguous
if len(option_tuples) > 1:
options = ', '.join([option_string
for action, option_string, sep, explicit_arg in option_tuples])
args = {'option': arg_string, 'matches': options}
msg = _('ambiguous option: %(option)s could match %(matches)s')
raise ArgumentError(None, msg % args)

action, option_string, sep, explicit_arg = option_tuples[0]

# identify additional optionals in the same arg string
# (e.g. -xyz is the same as -x -y -z if no args are required)
Expand Down Expand Up @@ -2253,7 +2262,7 @@ def _parse_optional(self, arg_string):
# if the option string is present in the parser, return the action
if arg_string in self._option_string_actions:
action = self._option_string_actions[arg_string]
return action, arg_string, None, None
return [(action, arg_string, None, None)]

# if it's just a single character, it was meant to be positional
if len(arg_string) == 1:
Expand All @@ -2263,25 +2272,14 @@ def _parse_optional(self, arg_string):
option_string, sep, explicit_arg = arg_string.partition('=')
if sep and option_string in self._option_string_actions:
action = self._option_string_actions[option_string]
return action, option_string, sep, explicit_arg
return [(action, option_string, sep, explicit_arg)]

# 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, sep, explicit_arg in option_tuples])
args = {'option': arg_string, 'matches': options}
msg = _('ambiguous option: %(option)s could match %(matches)s')
raise ArgumentError(None, 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 option_tuples:
return option_tuples

# if it was not found as an option, but it looks like a negative
# number, it was meant to be positional
Expand All @@ -2296,7 +2294,7 @@ def _parse_optional(self, arg_string):

# it was meant to be an optional but there is no such option
# in this parser (though it might be a valid option in a subparser)
return None, arg_string, None, None
return [(None, arg_string, None, None)]

def _get_option_tuples(self, option_string):
result = []
Expand All @@ -2319,7 +2317,9 @@ def _get_option_tuples(self, option_string):
# but multiple character options always have to have their argument
# separate
elif option_string[0] in chars and option_string[1] not in chars:
option_prefix = option_string
option_prefix, sep, explicit_arg = option_string.partition('=')
if not sep:
sep = explicit_arg = None
short_option_prefix = option_string[:2]
short_explicit_arg = option_string[2:]

Expand All @@ -2328,9 +2328,9 @@ def _get_option_tuples(self, option_string):
action = self._option_string_actions[option_string]
tup = action, option_string, '', short_explicit_arg
result.append(tup)
elif option_string.startswith(option_prefix):
elif self.allow_abbrev and option_string.startswith(option_prefix):
action = self._option_string_actions[option_string]
tup = action, option_string, None, None
tup = action, option_string, sep, explicit_arg
result.append(tup)

# shouldn't ever get here
Expand All @@ -2344,43 +2344,40 @@ def _get_nargs_pattern(self, action):
# in all examples below, we have to allow for '--' args
# which are represented as '-' in the pattern
nargs = action.nargs
# if this is an optional action, -- is not allowed
option = action.option_strings

# the default (None) is assumed to be a single argument
if nargs is None:
nargs_pattern = '(-*A-*)'
nargs_pattern = '([A])' if option else '(-*A-*)'

# allow zero or one arguments
elif nargs == OPTIONAL:
nargs_pattern = '(-*A?-*)'
nargs_pattern = '(A?)' if option else '(-*A?-*)'

# allow zero or more arguments
elif nargs == ZERO_OR_MORE:
nargs_pattern = '(-*[A-]*)'
nargs_pattern = '(A*)' if option else '(-*[A-]*)'

# allow one or more arguments
elif nargs == ONE_OR_MORE:
nargs_pattern = '(-*A[A-]*)'
nargs_pattern = '(A+)' if option else '(-*A[A-]*)'

# allow any number of options or arguments
elif nargs == REMAINDER:
nargs_pattern = '([-AO]*)'
nargs_pattern = '([AO]*)' if option else '(.*)'

# allow one argument followed by any number of options or arguments
elif nargs == PARSER:
nargs_pattern = '(-*A[-AO]*)'
nargs_pattern = '(A[AO]*)' if option else '(-*A[-AO]*)'

# suppress action, like nargs=0
elif nargs == SUPPRESS:
nargs_pattern = '(-*-*)'
nargs_pattern = '()' if option else '(-*)'

# all others should be integers
else:
nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs)

# if this is an optional action, -- is not allowed
if action.option_strings:
nargs_pattern = nargs_pattern.replace('-*', '')
nargs_pattern = nargs_pattern.replace('-', '')
nargs_pattern = '([AO]{%d})' % nargs if option else '((?:-*A){%d}-*)' % nargs

# return the pattern
return nargs_pattern
Expand Down Expand Up @@ -2483,21 +2480,17 @@ def _get_values(self, action, arg_strings):
value = action.const
else:
value = action.default
if isinstance(value, str):
if isinstance(value, str) and value is not SUPPRESS:
value = self._get_value(action, value)
self._check_value(action, value)

# when nargs='*' on a positional, if there were no command-line
# args, use the default if it is anything other than None
elif (not arg_strings and action.nargs == ZERO_OR_MORE and
not action.option_strings):
if action.default is not None:
value = action.default
self._check_value(action, value)
else:
# since arg_strings is always [] at this point
# there is no need to use self._check_value(action, value)
value = arg_strings
value = []

# single argument or optional argument produces a single value
elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
Expand Down Expand Up @@ -2554,11 +2547,15 @@ def _get_value(self, action, arg_string):

def _check_value(self, action, value):
# converted value must be one of the choices (if specified)
if action.choices is not None and value not in action.choices:
args = {'value': value,
'choices': ', '.join(map(repr, action.choices))}
msg = _('invalid choice: %(value)r (choose from %(choices)s)')
raise ArgumentError(action, msg % args)
choices = action.choices
if choices is not None:
if isinstance(choices, str):
choices = iter(choices)
if value not in choices:
args = {'value': value,
'choices': ', '.join(map(repr, action.choices))}
msg = _('invalid choice: %(value)r (choose from %(choices)s)')
raise ArgumentError(action, msg % args)

# =======================
# Help-formatting methods
Expand Down
10 changes: 8 additions & 2 deletions Lib/asyncio/staggered.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class _Done(Exception):
pass

async def staggered_race(coro_fns, delay):
async def staggered_race(coro_fns, delay, *, loop=None):
"""Run coroutines with staggered start times and take the first to finish.
This method takes an iterable of coroutine functions. The first one is
Expand Down Expand Up @@ -82,7 +82,13 @@ async def run_one_coro(this_index, coro_fn, this_failed):
raise _Done

try:
async with taskgroups.TaskGroup() as tg:
tg = taskgroups.TaskGroup()
# Intentionally override the loop in the TaskGroup to avoid
# using the running loop, preserving backwards compatibility
# TaskGroup only starts using `_loop` after `__aenter__`
# so overriding it here is safe.
tg._loop = loop
async with tg:
for this_index, coro_fn in enumerate(coro_fns):
this_failed = locks.Event()
exceptions.append(None)
Expand Down
12 changes: 4 additions & 8 deletions Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@

from abc import get_cache_token
from collections import namedtuple
# import types, weakref # Deferred to single_dispatch()
# import weakref # Deferred to single_dispatch()
from operator import itemgetter
from reprlib import recursive_repr
from types import MethodType
from types import GenericAlias, MethodType, MappingProxyType, UnionType
from _thread import RLock

# Avoid importing types, so we can speedup import time
GenericAlias = type(list[int])
UnionType = type(int | str)

################################################################################
### update_wrapper() and wraps() decorator
################################################################################
Expand Down Expand Up @@ -901,7 +897,7 @@ def singledispatch(func):
# There are many programs that use functools without singledispatch, so we
# trade-off making singledispatch marginally slower for the benefit of
# making start-up of such applications slightly faster.
import types, weakref
import weakref

registry = {}
dispatch_cache = weakref.WeakKeyDictionary()
Expand Down Expand Up @@ -1002,7 +998,7 @@ def wrapper(*args, **kw):
registry[object] = func
wrapper.register = register
wrapper.dispatch = dispatch
wrapper.registry = types.MappingProxyType(registry)
wrapper.registry = MappingProxyType(registry)
wrapper._clear_cache = dispatch_cache.clear
update_wrapper(wrapper, func)
return wrapper
Expand Down
Loading

0 comments on commit 1a39ded

Please sign in to comment.