Skip to content

Commit 497a0bb

Browse files
Merge branch 'main' into pythongh-130804-pyrepl-unicode-chars
2 parents ef0d0ee + 813bc56 commit 497a0bb

18 files changed

+291
-64
lines changed

Include/cpython/unicodeobject.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,22 +212,21 @@ static inline unsigned int PyUnicode_IS_READY(PyObject* Py_UNUSED(op)) {
212212
#define PyUnicode_IS_READY(op) PyUnicode_IS_READY(_PyObject_CAST(op))
213213

214214
/* Return true if the string contains only ASCII characters, or 0 if not. The
215-
string may be compact (PyUnicode_IS_COMPACT_ASCII) or not, but must be
216-
ready. */
215+
string may be compact (PyUnicode_IS_COMPACT_ASCII) or not. */
217216
static inline unsigned int PyUnicode_IS_ASCII(PyObject *op) {
218217
return _PyASCIIObject_CAST(op)->state.ascii;
219218
}
220219
#define PyUnicode_IS_ASCII(op) PyUnicode_IS_ASCII(_PyObject_CAST(op))
221220

222221
/* Return true if the string is compact or 0 if not.
223-
No type checks or Ready calls are performed. */
222+
No type checks are performed. */
224223
static inline unsigned int PyUnicode_IS_COMPACT(PyObject *op) {
225224
return _PyASCIIObject_CAST(op)->state.compact;
226225
}
227226
#define PyUnicode_IS_COMPACT(op) PyUnicode_IS_COMPACT(_PyObject_CAST(op))
228227

229228
/* Return true if the string is a compact ASCII string (use PyASCIIObject
230-
structure), or 0 if not. No type checks or Ready calls are performed. */
229+
structure), or 0 if not. No type checks are performed. */
231230
static inline int PyUnicode_IS_COMPACT_ASCII(PyObject *op) {
232231
return (_PyASCIIObject_CAST(op)->state.ascii && PyUnicode_IS_COMPACT(op));
233232
}
@@ -319,7 +318,7 @@ static inline void PyUnicode_WRITE(int kind, void *data,
319318
(index), _Py_STATIC_CAST(Py_UCS4, value))
320319

321320
/* Read a code point from the string's canonical representation. No checks
322-
or ready calls are performed. */
321+
are performed. */
323322
static inline Py_UCS4 PyUnicode_READ(int kind,
324323
const void *data, Py_ssize_t index)
325324
{

Include/internal/pycore_traceback.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ extern const char* _Py_DumpTracebackThreads(
6666
/* Write a Unicode object into the file descriptor fd. Encode the string to
6767
ASCII using the backslashreplace error handler.
6868
69-
Do nothing if text is not a Unicode object. The function accepts Unicode
70-
string which is not ready (PyUnicode_WCHAR_KIND).
69+
Do nothing if text is not a Unicode object.
7170
7271
This function is signal safe. */
7372
extern void _Py_DumpASCII(int fd, PyObject *text);

Lib/test/_test_multiprocessing.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,14 +1622,13 @@ def f(cls, cond, sleeping, woken, timeout=None):
16221622
cond.release()
16231623

16241624
def assertReachesEventually(self, func, value):
1625-
for i in range(10):
1625+
for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
16261626
try:
16271627
if func() == value:
16281628
break
16291629
except NotImplementedError:
16301630
break
1631-
time.sleep(DELTA)
1632-
time.sleep(DELTA)
1631+
16331632
self.assertReturnsIfImplemented(value, func)
16341633

16351634
def check_invariant(self, cond):
@@ -1663,26 +1662,23 @@ def test_notify(self):
16631662
sleeping.acquire()
16641663

16651664
# check no process/thread has woken up
1666-
time.sleep(DELTA)
1667-
self.assertReturnsIfImplemented(0, get_value, woken)
1665+
self.assertReachesEventually(lambda: get_value(woken), 0)
16681666

16691667
# wake up one process/thread
16701668
cond.acquire()
16711669
cond.notify()
16721670
cond.release()
16731671

16741672
# check one process/thread has woken up
1675-
time.sleep(DELTA)
1676-
self.assertReturnsIfImplemented(1, get_value, woken)
1673+
self.assertReachesEventually(lambda: get_value(woken), 1)
16771674

16781675
# wake up another
16791676
cond.acquire()
16801677
cond.notify()
16811678
cond.release()
16821679

16831680
# check other has woken up
1684-
time.sleep(DELTA)
1685-
self.assertReturnsIfImplemented(2, get_value, woken)
1681+
self.assertReachesEventually(lambda: get_value(woken), 2)
16861682

16871683
# check state is not mucked up
16881684
self.check_invariant(cond)

Lib/test/test_genericalias.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,76 @@ def test_del_iter(self):
474474
iter_x = iter(t)
475475
del iter_x
476476

477+
def test_paramspec_specialization(self):
478+
# gh-124445
479+
T = TypeVar("T")
480+
U = TypeVar("U")
481+
type X[**P] = Callable[P, int]
482+
483+
generic = X[[T]]
484+
self.assertEqual(generic.__args__, ([T],))
485+
self.assertEqual(generic.__parameters__, (T,))
486+
specialized = generic[str]
487+
self.assertEqual(specialized.__args__, ([str],))
488+
self.assertEqual(specialized.__parameters__, ())
489+
490+
generic = X[(T,)]
491+
self.assertEqual(generic.__args__, (T,))
492+
self.assertEqual(generic.__parameters__, (T,))
493+
specialized = generic[str]
494+
self.assertEqual(specialized.__args__, (str,))
495+
self.assertEqual(specialized.__parameters__, ())
496+
497+
generic = X[[T, U]]
498+
self.assertEqual(generic.__args__, ([T, U],))
499+
self.assertEqual(generic.__parameters__, (T, U))
500+
specialized = generic[str, int]
501+
self.assertEqual(specialized.__args__, ([str, int],))
502+
self.assertEqual(specialized.__parameters__, ())
503+
504+
generic = X[(T, U)]
505+
self.assertEqual(generic.__args__, (T, U))
506+
self.assertEqual(generic.__parameters__, (T, U))
507+
specialized = generic[str, int]
508+
self.assertEqual(specialized.__args__, (str, int))
509+
self.assertEqual(specialized.__parameters__, ())
510+
511+
def test_nested_paramspec_specialization(self):
512+
# gh-124445
513+
type X[**P, T] = Callable[P, T]
514+
515+
x_list = X[[int, str], float]
516+
self.assertEqual(x_list.__args__, ([int, str], float))
517+
self.assertEqual(x_list.__parameters__, ())
518+
519+
x_tuple = X[(int, str), float]
520+
self.assertEqual(x_tuple.__args__, ((int, str), float))
521+
self.assertEqual(x_tuple.__parameters__, ())
522+
523+
U = TypeVar("U")
524+
V = TypeVar("V")
525+
526+
multiple_params_list = X[[int, U], V]
527+
self.assertEqual(multiple_params_list.__args__, ([int, U], V))
528+
self.assertEqual(multiple_params_list.__parameters__, (U, V))
529+
multiple_params_list_specialized = multiple_params_list[str, float]
530+
self.assertEqual(multiple_params_list_specialized.__args__, ([int, str], float))
531+
self.assertEqual(multiple_params_list_specialized.__parameters__, ())
532+
533+
multiple_params_tuple = X[(int, U), V]
534+
self.assertEqual(multiple_params_tuple.__args__, ((int, U), V))
535+
self.assertEqual(multiple_params_tuple.__parameters__, (U, V))
536+
multiple_params_tuple_specialized = multiple_params_tuple[str, float]
537+
self.assertEqual(multiple_params_tuple_specialized.__args__, ((int, str), float))
538+
self.assertEqual(multiple_params_tuple_specialized.__parameters__, ())
539+
540+
deeply_nested = X[[U, [V], int], V]
541+
self.assertEqual(deeply_nested.__args__, ([U, [V], int], V))
542+
self.assertEqual(deeply_nested.__parameters__, (U, V))
543+
deeply_nested_specialized = deeply_nested[str, float]
544+
self.assertEqual(deeply_nested_specialized.__args__, ([str, [float], int], float))
545+
self.assertEqual(deeply_nested_specialized.__parameters__, ())
546+
477547

478548
class TypeIterationTests(unittest.TestCase):
479549
_UNITERABLE_TYPES = (list, tuple)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix specialization of generic aliases that are generic over a
2+
:class:`typing.ParamSpec` and have been specialized with a
3+
nested type variable.

Modules/_io/textio.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself,
358358
out = PyUnicode_DATA(modified);
359359
PyUnicode_WRITE(kind, out, 0, '\r');
360360
memcpy(out + kind, PyUnicode_DATA(output), kind * output_len);
361-
Py_SETREF(output, modified); /* output remains ready */
361+
Py_SETREF(output, modified);
362362
self->pendingcr = 0;
363363
output_len++;
364364
}
@@ -1818,7 +1818,6 @@ textiowrapper_get_decoded_chars(textio *self, Py_ssize_t n)
18181818
if (self->decoded_chars == NULL)
18191819
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
18201820

1821-
/* decoded_chars is guaranteed to be "ready". */
18221821
avail = (PyUnicode_GET_LENGTH(self->decoded_chars)
18231822
- self->decoded_chars_used);
18241823

Modules/unicodedata.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ nfd_nfkd(PyObject *self, PyObject *input, int k)
591591
PyMem_Free(output);
592592
if (!result)
593593
return NULL;
594-
/* result is guaranteed to be ready, as it is compact. */
594+
595595
kind = PyUnicode_KIND(result);
596596
data = PyUnicode_DATA(result);
597597

@@ -655,7 +655,7 @@ nfc_nfkc(PyObject *self, PyObject *input, int k)
655655
result = nfd_nfkd(self, input, k);
656656
if (!result)
657657
return NULL;
658-
/* result will be "ready". */
658+
659659
kind = PyUnicode_KIND(result);
660660
data = PyUnicode_DATA(result);
661661
len = PyUnicode_GET_LENGTH(result);

Objects/genericaliasobject.c

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,22 @@ tuple_extend(PyObject **dst, Py_ssize_t dstindex,
180180
PyObject *
181181
_Py_make_parameters(PyObject *args)
182182
{
183+
assert(PyTuple_Check(args) || PyList_Check(args));
184+
const bool is_args_list = PyList_Check(args);
185+
PyObject *tuple_args = NULL;
186+
if (is_args_list) {
187+
args = tuple_args = PySequence_Tuple(args);
188+
if (args == NULL) {
189+
return NULL;
190+
}
191+
}
183192
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
184193
Py_ssize_t len = nargs;
185194
PyObject *parameters = PyTuple_New(len);
186-
if (parameters == NULL)
195+
if (parameters == NULL) {
196+
Py_XDECREF(tuple_args);
187197
return NULL;
198+
}
188199
Py_ssize_t iparam = 0;
189200
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
190201
PyObject *t = PyTuple_GET_ITEM(args, iarg);
@@ -195,6 +206,7 @@ _Py_make_parameters(PyObject *args)
195206
int rc = PyObject_HasAttrWithError(t, &_Py_ID(__typing_subst__));
196207
if (rc < 0) {
197208
Py_DECREF(parameters);
209+
Py_XDECREF(tuple_args);
198210
return NULL;
199211
}
200212
if (rc) {
@@ -205,8 +217,19 @@ _Py_make_parameters(PyObject *args)
205217
if (PyObject_GetOptionalAttr(t, &_Py_ID(__parameters__),
206218
&subparams) < 0) {
207219
Py_DECREF(parameters);
220+
Py_XDECREF(tuple_args);
208221
return NULL;
209222
}
223+
if (!subparams && (PyTuple_Check(t) || PyList_Check(t))) {
224+
// Recursively call _Py_make_parameters for lists/tuples and
225+
// add the results to the current parameters.
226+
subparams = _Py_make_parameters(t);
227+
if (subparams == NULL) {
228+
Py_DECREF(parameters);
229+
Py_XDECREF(tuple_args);
230+
return NULL;
231+
}
232+
}
210233
if (subparams && PyTuple_Check(subparams)) {
211234
Py_ssize_t len2 = PyTuple_GET_SIZE(subparams);
212235
Py_ssize_t needed = len2 - 1 - (iarg - iparam);
@@ -215,6 +238,7 @@ _Py_make_parameters(PyObject *args)
215238
if (_PyTuple_Resize(&parameters, len) < 0) {
216239
Py_DECREF(subparams);
217240
Py_DECREF(parameters);
241+
Py_XDECREF(tuple_args);
218242
return NULL;
219243
}
220244
}
@@ -229,9 +253,11 @@ _Py_make_parameters(PyObject *args)
229253
if (iparam < len) {
230254
if (_PyTuple_Resize(&parameters, iparam) < 0) {
231255
Py_XDECREF(parameters);
256+
Py_XDECREF(tuple_args);
232257
return NULL;
233258
}
234259
}
260+
Py_XDECREF(tuple_args);
235261
return parameters;
236262
}
237263

@@ -416,11 +442,22 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
416442
t = list[T]; t[int] -> newargs = [int]
417443
t = dict[str, T]; t[int] -> newargs = [str, int]
418444
t = dict[T, list[S]]; t[str, int] -> newargs = [str, list[int]]
445+
t = list[[T]]; t[str] -> newargs = [[str]]
419446
*/
447+
assert (PyTuple_Check(args) || PyList_Check(args));
448+
const bool is_args_list = PyList_Check(args);
449+
PyObject *tuple_args = NULL;
450+
if (is_args_list) {
451+
args = tuple_args = PySequence_Tuple(args);
452+
if (args == NULL) {
453+
return NULL;
454+
}
455+
}
420456
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
421457
PyObject *newargs = PyTuple_New(nargs);
422458
if (newargs == NULL) {
423459
Py_DECREF(item);
460+
Py_XDECREF(tuple_args);
424461
return NULL;
425462
}
426463
for (Py_ssize_t iarg = 0, jarg = 0; iarg < nargs; iarg++) {
@@ -430,17 +467,46 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
430467
jarg++;
431468
continue;
432469
}
433-
470+
// Recursively substitute params in lists/tuples.
471+
if (PyTuple_Check(arg) || PyList_Check(arg)) {
472+
PyObject *subargs = _Py_subs_parameters(self, arg, parameters, item);
473+
if (subargs == NULL) {
474+
Py_DECREF(newargs);
475+
Py_DECREF(item);
476+
Py_XDECREF(tuple_args);
477+
return NULL;
478+
}
479+
if (PyTuple_Check(arg)) {
480+
PyTuple_SET_ITEM(newargs, jarg, subargs);
481+
}
482+
else {
483+
// _Py_subs_parameters returns a tuple. If the original arg was a list,
484+
// convert subargs to a list as well.
485+
PyObject *subargs_list = PySequence_List(subargs);
486+
Py_DECREF(subargs);
487+
if (subargs_list == NULL) {
488+
Py_DECREF(newargs);
489+
Py_DECREF(item);
490+
Py_XDECREF(tuple_args);
491+
return NULL;
492+
}
493+
PyTuple_SET_ITEM(newargs, jarg, subargs_list);
494+
}
495+
jarg++;
496+
continue;
497+
}
434498
int unpack = _is_unpacked_typevartuple(arg);
435499
if (unpack < 0) {
436500
Py_DECREF(newargs);
437501
Py_DECREF(item);
502+
Py_XDECREF(tuple_args);
438503
return NULL;
439504
}
440505
PyObject *subst;
441506
if (PyObject_GetOptionalAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) {
442507
Py_DECREF(newargs);
443508
Py_DECREF(item);
509+
Py_XDECREF(tuple_args);
444510
return NULL;
445511
}
446512
if (subst) {
@@ -455,6 +521,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
455521
if (arg == NULL) {
456522
Py_DECREF(newargs);
457523
Py_DECREF(item);
524+
Py_XDECREF(tuple_args);
458525
return NULL;
459526
}
460527
if (unpack) {
@@ -463,6 +530,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
463530
Py_DECREF(arg);
464531
if (jarg < 0) {
465532
Py_DECREF(item);
533+
Py_XDECREF(tuple_args);
466534
return NULL;
467535
}
468536
}
@@ -473,6 +541,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
473541
}
474542

475543
Py_DECREF(item);
544+
Py_XDECREF(tuple_args);
476545
return newargs;
477546
}
478547

0 commit comments

Comments
 (0)