From 48eb8025215c2cb535f1837b06342f6df6d3d974 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Fri, 23 Sep 2022 16:58:13 -0400 Subject: [PATCH 001/151] Add an optional callback that is invoked whenever a function is modified JIT compilers may need to invalidate compiled code when a function is modified (e.g. if its code object is modified). This adds the ability to set a callback that, when set, is called each time a function is modified. --- Include/cpython/funcobject.h | 38 ++++++++++++ Lib/test/test_func_events.py | 44 ++++++++++++++ Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/func_events.c | 100 ++++++++++++++++++++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 3 + Objects/funcobject.c | 29 +++++++++ 7 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_func_events.py create mode 100644 Modules/_testcapi/func_events.c diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index dd8f20b2c20b39..74fdf9f963f1b1 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -131,6 +131,44 @@ PyAPI_DATA(PyTypeObject) PyStaticMethod_Type; PyAPI_FUNC(PyObject *) PyClassMethod_New(PyObject *); PyAPI_FUNC(PyObject *) PyStaticMethod_New(PyObject *); +#define FOREACH_FUNC_EVENT(V) \ + V(CREATED) \ + V(DESTROY) \ + V(MODIFY_CODE) \ + V(MODIFY_DEFAULTS) \ + V(MODIFY_KWDEFAULTS) + +typedef enum { + #define DEF_EVENT(EVENT) PYFUNC_EVENT_##EVENT, + FOREACH_FUNC_EVENT(DEF_EVENT) + #undef DEF_EVENT +} PyFunction_Event; + +/* + * A callback that is invoked for different events in a function's lifecycle. + * + * The callback is invoked with a borrowed reference to func, after it is + * created and before it is modified or destroyed. The callback should not + * modify func. + * + * When a function's code object, defaults, or kwdefaults are modified the + * callback will be invoked with the respective event and new_value will + * contain a borrowed reference to the new value that is about to be stored in + * the function. Otherwise the third argument is NULL. + */ +typedef void(*PyFunction_EventCallback)( + PyFunction_Event event, + PyFunctionObject *func, + PyObject *new_value); + +/* + * Set the callback that will be invoked for function lifecycle events. + * + * Pass NULL to clear the callback. + */ +PyAPI_FUNC(void) PyFunction_SetEventCallback(PyFunction_EventCallback callback); +PyAPI_FUNC(PyFunction_EventCallback) PyFunction_GetEventCallback(void); + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_func_events.py b/Lib/test/test_func_events.py new file mode 100644 index 00000000000000..a51c48efe6f239 --- /dev/null +++ b/Lib/test/test_func_events.py @@ -0,0 +1,44 @@ +import unittest +from _testcapi import ( + PYFUNC_EVENT_CREATED, + PYFUNC_EVENT_DESTROY, + PYFUNC_EVENT_MODIFY_CODE, + PYFUNC_EVENT_MODIFY_DEFAULTS, + PYFUNC_EVENT_MODIFY_KWDEFAULTS, + restore_func_event_callback, + set_func_event_callback, +) + + +class FuncEventsTest(unittest.TestCase): + def test_func_events_dispatched(self): + event = None + def handle_func_event(*args): + nonlocal event + event = args + set_func_event_callback(handle_func_event) + + try: + def myfunc(): + pass + self.assertEqual(event, (PYFUNC_EVENT_CREATED, myfunc, None)) + myfunc_id = id(myfunc) + + new_code = self.test_func_events_dispatched.__code__ + myfunc.__code__ = new_code + self.assertEqual(event, (PYFUNC_EVENT_MODIFY_CODE, myfunc, new_code)) + + new_defaults = (123,) + myfunc.__defaults__ = new_defaults + self.assertEqual(event, (PYFUNC_EVENT_MODIFY_DEFAULTS, myfunc, new_defaults)) + + new_kwdefaults = {"self": 123} + myfunc.__kwdefaults__ = new_kwdefaults + self.assertEqual(event, (PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults)) + + # Clear event's reference to func + event = None + del myfunc + self.assertEqual(event, (PYFUNC_EVENT_DESTROY, myfunc_id, None)) + finally: + restore_func_event_callback() diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index ac8959ebea5bf2..2003ede9076f36 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -169,7 +169,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/func_events.c # Some testing modules MUST be built as shared libraries. *shared* diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c new file mode 100644 index 00000000000000..eae04efadbaf88 --- /dev/null +++ b/Modules/_testcapi/func_events.c @@ -0,0 +1,100 @@ +#include "parts.h" + +static PyObject *pyfunc_callback = NULL; +static PyFunction_EventCallback orig_callback = NULL; + +static void +call_pyfunc_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +{ + PyObject *event_obj = PyLong_FromLong(event); + if (event_obj == NULL) { + PyErr_Clear(); + return; + } + if (new_value == NULL) { + new_value = Py_None; + } + Py_INCREF(new_value); + /* Don't expose a function that's about to be destroyed to managed code */ + PyObject *func_or_id = (PyObject *) func; + if (event == PYFUNC_EVENT_DESTROY) { + func_or_id = PyLong_FromLong((long) func); + if (func_or_id == NULL) { + Py_DECREF(new_value); + Py_DECREF(event_obj); + return; + } + } else { + Py_INCREF(func); + } + PyObject *stack[] = {event_obj, func_or_id, new_value}; + PyObject_Vectorcall(pyfunc_callback, stack, 3, NULL); + Py_DECREF(new_value); + Py_DECREF(event_obj); + Py_DECREF(func_or_id); +} + +static int +add_event(PyObject *module, const char *name, PyFunction_Event event) +{ + PyObject *value = PyLong_FromLong(event); + if (value == NULL) { + return -1; + } + int ok = PyModule_AddObjectRef(module, name, value); + Py_DECREF(value); + return ok; +} + +static PyObject * +set_func_event_callback(PyObject *self, PyObject *func) +{ + if (!PyFunction_Check(func)) { + PyErr_SetString(PyExc_TypeError, "'func' must be a function"); + return NULL; + } + if (pyfunc_callback != NULL) { + PyErr_SetString(PyExc_RuntimeError, "already set callback"); + return NULL; + } + Py_INCREF(func); + pyfunc_callback = func; + orig_callback = PyFunction_GetEventCallback(); + PyFunction_SetEventCallback(call_pyfunc_callback); + Py_RETURN_NONE; +} + +static PyObject * +restore_func_event_callback() { + if (pyfunc_callback == NULL) { + PyErr_SetString(PyExc_RuntimeError, "nothing to restore"); + return NULL; + } + PyFunction_SetEventCallback(orig_callback); + orig_callback = NULL; + Py_CLEAR(pyfunc_callback); + Py_RETURN_NONE; +} + +static PyMethodDef TestMethods[] = { + {"set_func_event_callback", set_func_event_callback, METH_O}, + {"restore_func_event_callback", restore_func_event_callback, METH_NOARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_FuncEvents(PyObject *m) { + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + + /* Expose each event as an attribute on the module */ +#define ADD_EVENT(event) \ + if (add_event(m, "PYFUNC_EVENT_" #event, PYFUNC_EVENT_##event)) { \ + return -1; \ + } + FOREACH_FUNC_EVENT(ADD_EVENT); +#undef ADD_EVENT + + return 0; +} diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 304e5922c0d50b..9d998aaa5a5a03 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -27,6 +27,7 @@ int _PyTestCapi_Init_Vectorcall(PyObject *module); int _PyTestCapi_Init_Heaptype(PyObject *module); int _PyTestCapi_Init_Unicode(PyObject *module); +int _PyTestCapi_Init_FuncEvents(PyObject *module); #ifdef LIMITED_API_AVAILABLE int _PyTestCapi_Init_VectorcallLimited(PyObject *module); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b8f71d47ed524f..629b688da1177a 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -6552,6 +6552,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Unicode(m) < 0) { return NULL; } + if (_PyTestCapi_Init_FuncEvents(m) < 0) { + return NULL; + } #ifndef LIMITED_API_AVAILABLE PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False); diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 7f257a9986987b..9e0843f0d22cd5 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -9,6 +9,29 @@ static uint32_t next_func_version = 1; +static PyFunction_EventCallback func_event_callback = NULL; + +static void +handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +{ + if (func_event_callback == NULL) { + return; + } + func_event_callback(event, func, new_value); +} + +void +PyFunction_SetEventCallback(PyFunction_EventCallback callback) +{ + func_event_callback = callback; +} + +PyFunction_EventCallback +PyFunction_GetEventCallback() +{ + return func_event_callback; +} + PyFunctionObject * _PyFunction_FromConstructor(PyFrameConstructor *constr) { @@ -40,6 +63,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->vectorcall = _PyFunction_Vectorcall; op->func_version = 0; _PyObject_GC_TRACK(op); + handle_func_event(PYFUNC_EVENT_CREATED, op, NULL); return op; } @@ -116,6 +140,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->vectorcall = _PyFunction_Vectorcall; op->func_version = 0; _PyObject_GC_TRACK(op); + handle_func_event(PYFUNC_EVENT_CREATED, op, NULL); return (PyObject *)op; error: @@ -402,6 +427,7 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored)) nclosure, nfree); return -1; } + handle_func_event(PYFUNC_EVENT_MODIFY_CODE, op, value); op->func_version = 0; Py_INCREF(value); Py_XSETREF(op->func_code, value); @@ -487,6 +513,7 @@ func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored return -1; } + handle_func_event(PYFUNC_EVENT_MODIFY_DEFAULTS, op, value); op->func_version = 0; Py_XINCREF(value); Py_XSETREF(op->func_defaults, value); @@ -529,6 +556,7 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor return -1; } + handle_func_event(PYFUNC_EVENT_MODIFY_KWDEFAULTS, op, value); op->func_version = 0; Py_XINCREF(value); Py_XSETREF(op->func_kwdefaults, value); @@ -712,6 +740,7 @@ func_clear(PyFunctionObject *op) static void func_dealloc(PyFunctionObject *op) { + handle_func_event(PYFUNC_EVENT_DESTROY, op, NULL); _PyObject_GC_UNTRACK(op); if (op->func_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) op); From 501c4dd6f625ad340b6c95227b8053df7dccc18d Mon Sep 17 00:00:00 2001 From: Matt Page Date: Tue, 4 Oct 2022 16:32:14 -0700 Subject: [PATCH 002/151] Fix the build on windows --- PCbuild/_testcapi.vcxproj | 3 ++- PCbuild/_testcapi.vcxproj.filters | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index b7d40c8cdc30eb..8a408095462993 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -98,6 +98,7 @@ + @@ -115,4 +116,4 @@ - \ No newline at end of file + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index fc2c4345fe142e..12361a78990525 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -24,10 +24,13 @@ Source Files + + Source Files + Resource Files - \ No newline at end of file + From b727aa2d24bfd03998d415663f7ecf8ce8d9a418 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Tue, 4 Oct 2022 16:39:48 -0700 Subject: [PATCH 003/151] Fix refcounting for the result of vectorcall --- Modules/_testcapi/func_events.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c index eae04efadbaf88..81c4a74836ca69 100644 --- a/Modules/_testcapi/func_events.c +++ b/Modules/_testcapi/func_events.c @@ -28,7 +28,8 @@ call_pyfunc_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *n Py_INCREF(func); } PyObject *stack[] = {event_obj, func_or_id, new_value}; - PyObject_Vectorcall(pyfunc_callback, stack, 3, NULL); + PyObject *res = PyObject_Vectorcall(pyfunc_callback, stack, 3, NULL); + Py_XDECREF(res); Py_DECREF(new_value); Py_DECREF(event_obj); Py_DECREF(func_or_id); From e3a8230ee2af375ffda5cedc05398acf2810b0a4 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Tue, 4 Oct 2022 17:33:21 -0700 Subject: [PATCH 004/151] Move callback into _PyRuntimeState The primary use case for this is a JIT, which will be per-runtime, not per-interpreter, so move the callback onto the runtime. --- Include/cpython/funcobject.h | 2 ++ Include/internal/pycore_runtime.h | 2 ++ Objects/funcobject.c | 13 +++++++------ Python/pystate.c | 8 ++++++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 74fdf9f963f1b1..4bf198add4757f 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -164,6 +164,8 @@ typedef void(*PyFunction_EventCallback)( /* * Set the callback that will be invoked for function lifecycle events. * + * This must only be called after the runtime has been initialized. + * * Pass NULL to clear the callback. */ PyAPI_FUNC(void) PyFunction_SetEventCallback(PyFunction_EventCallback callback); diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index d1fbc09f1ea206..25c8877a8a44f0 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -132,6 +132,8 @@ typedef struct pyruntimestate { /* All the objects that are shared by the runtime's interpreters. */ struct _Py_global_objects global_objects; + PyFunction_EventCallback func_event_callback; + /* The following fields are here to avoid allocation during init. The data is exposed through _PyRuntimeState pointer fields. These fields should not be accessed directly outside of init. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 9e0843f0d22cd5..ceac75ad0f0e9a 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -9,27 +9,28 @@ static uint32_t next_func_version = 1; -static PyFunction_EventCallback func_event_callback = NULL; - static void handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) { - if (func_event_callback == NULL) { + PyFunction_EventCallback handle_event = _PyRuntime.func_event_callback; + if (handle_event == NULL) { return; } - func_event_callback(event, func, new_value); + handle_event(event, func, new_value); } void PyFunction_SetEventCallback(PyFunction_EventCallback callback) { - func_event_callback = callback; + assert(_PyRuntime.initialized); + _PyRuntime.func_event_callback = callback; } PyFunction_EventCallback PyFunction_GetEventCallback() { - return func_event_callback; + assert(_PyRuntime.initialized); + return _PyRuntime.func_event_callback; } PyFunctionObject * diff --git a/Python/pystate.c b/Python/pystate.c index 23e9d24c591b63..077012f9e283de 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -107,7 +107,8 @@ init_runtime(_PyRuntimeState *runtime, PyThread_type_lock unicode_ids_mutex, PyThread_type_lock interpreters_mutex, PyThread_type_lock xidregistry_mutex, - PyThread_type_lock getargs_mutex) + PyThread_type_lock getargs_mutex, + PyFunction_EventCallback func_event_callback) { if (runtime->_initialized) { Py_FatalError("runtime already initialized"); @@ -137,6 +138,8 @@ init_runtime(_PyRuntimeState *runtime, runtime->unicode_ids.next_index = unicode_next_index; runtime->unicode_ids.lock = unicode_ids_mutex; + runtime->func_event_callback = func_event_callback; + runtime->_initialized = 1; } @@ -164,7 +167,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) memcpy(runtime, &initial, sizeof(*runtime)); } init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head, - unicode_next_index, lock1, lock2, lock3, lock4); + unicode_next_index, lock1, lock2, lock3, lock4, + runtime->func_event_callback); return _PyStatus_OK(); } From b2e20efd8d2a1f37aedcbf1be8eee47aaac3ac89 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Wed, 5 Oct 2022 10:51:38 -0700 Subject: [PATCH 005/151] Handle multiple events being dispatched --- Lib/test/test_func_events.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_func_events.py b/Lib/test/test_func_events.py index a51c48efe6f239..418f3b9a18377a 100644 --- a/Lib/test/test_func_events.py +++ b/Lib/test/test_func_events.py @@ -12,33 +12,32 @@ class FuncEventsTest(unittest.TestCase): def test_func_events_dispatched(self): - event = None + events = [] def handle_func_event(*args): - nonlocal event - event = args + events.append(args) set_func_event_callback(handle_func_event) try: def myfunc(): pass - self.assertEqual(event, (PYFUNC_EVENT_CREATED, myfunc, None)) + self.assertIn((PYFUNC_EVENT_CREATED, myfunc, None), events) myfunc_id = id(myfunc) new_code = self.test_func_events_dispatched.__code__ myfunc.__code__ = new_code - self.assertEqual(event, (PYFUNC_EVENT_MODIFY_CODE, myfunc, new_code)) + self.assertIn((PYFUNC_EVENT_MODIFY_CODE, myfunc, new_code), events) new_defaults = (123,) myfunc.__defaults__ = new_defaults - self.assertEqual(event, (PYFUNC_EVENT_MODIFY_DEFAULTS, myfunc, new_defaults)) + self.assertIn((PYFUNC_EVENT_MODIFY_DEFAULTS, myfunc, new_defaults), events) new_kwdefaults = {"self": 123} myfunc.__kwdefaults__ = new_kwdefaults - self.assertEqual(event, (PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults)) + self.assertIn((PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults), events) - # Clear event's reference to func - event = None + # Clear events reference to func + events = [] del myfunc - self.assertEqual(event, (PYFUNC_EVENT_DESTROY, myfunc_id, None)) + self.assertIn((PYFUNC_EVENT_DESTROY, myfunc_id, None), events) finally: restore_func_event_callback() From 0a30690f4973e25990885454cf09040db1f8ccbf Mon Sep 17 00:00:00 2001 From: Matt Page Date: Wed, 5 Oct 2022 11:44:58 -0700 Subject: [PATCH 006/151] Add NEWS entry --- .../2022-10-05-11-44-52.gh-issue-91053.f5Bo3p.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-44-52.gh-issue-91053.f5Bo3p.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-44-52.gh-issue-91053.f5Bo3p.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-44-52.gh-issue-91053.f5Bo3p.rst new file mode 100644 index 00000000000000..59bb12caef740d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-44-52.gh-issue-91053.f5Bo3p.rst @@ -0,0 +1,4 @@ +Optimizing interpreters and JIT compilers may need to invalidate internal +metadata when functions are modified. This change adds the ability to +provide a callback that will be invoked each time a function is created, +modified, or destroyed. From 73dd80916b04480e2869ef158cc6df143b4c3db0 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Wed, 5 Oct 2022 12:15:42 -0700 Subject: [PATCH 007/151] Make the callback per-interpreter, rather than per-runtime --- Include/cpython/funcobject.h | 5 ++--- Include/internal/pycore_interp.h | 3 +++ Include/internal/pycore_runtime.h | 2 -- Objects/funcobject.c | 13 ++++++++----- Python/pystate.c | 9 +++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 4bf198add4757f..26f407919e7c1c 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -162,9 +162,8 @@ typedef void(*PyFunction_EventCallback)( PyObject *new_value); /* - * Set the callback that will be invoked for function lifecycle events. - * - * This must only be called after the runtime has been initialized. + * Set the per-interpreter callback that will be invoked for function lifecycle + * events. * * Pass NULL to clear the callback. */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index b21708a388b339..b22cfb7121d549 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -175,6 +175,9 @@ struct _is { struct types_state types; struct callable_cache callable_cache; + + PyFunction_EventCallback func_event_callback; + /* The following fields are here to avoid allocation during init. The data is exposed through PyInterpreterState pointer fields. These fields should not be accessed directly outside of init. diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 25c8877a8a44f0..d1fbc09f1ea206 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -132,8 +132,6 @@ typedef struct pyruntimestate { /* All the objects that are shared by the runtime's interpreters. */ struct _Py_global_objects global_objects; - PyFunction_EventCallback func_event_callback; - /* The following fields are here to avoid allocation during init. The data is exposed through _PyRuntimeState pointer fields. These fields should not be accessed directly outside of init. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index ceac75ad0f0e9a..ee863507593d37 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -12,7 +12,8 @@ static uint32_t next_func_version = 1; static void handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) { - PyFunction_EventCallback handle_event = _PyRuntime.func_event_callback; + PyThreadState *tstate = _PyThreadState_GET(); + PyFunction_EventCallback handle_event = tstate->interp->func_event_callback; if (handle_event == NULL) { return; } @@ -22,15 +23,17 @@ handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_ void PyFunction_SetEventCallback(PyFunction_EventCallback callback) { - assert(_PyRuntime.initialized); - _PyRuntime.func_event_callback = callback; + PyThreadState *tstate = _PyThreadState_GET(); + assert(tstate->interp->_initialized); + tstate->interp->func_event_callback = callback; } PyFunction_EventCallback PyFunction_GetEventCallback() { - assert(_PyRuntime.initialized); - return _PyRuntime.func_event_callback; + PyThreadState *tstate = _PyThreadState_GET(); + assert(tstate->interp->_initialized); + return tstate->interp->func_event_callback; } PyFunctionObject * diff --git a/Python/pystate.c b/Python/pystate.c index 077012f9e283de..892ea68a09be18 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -107,8 +107,7 @@ init_runtime(_PyRuntimeState *runtime, PyThread_type_lock unicode_ids_mutex, PyThread_type_lock interpreters_mutex, PyThread_type_lock xidregistry_mutex, - PyThread_type_lock getargs_mutex, - PyFunction_EventCallback func_event_callback) + PyThread_type_lock getargs_mutex) { if (runtime->_initialized) { Py_FatalError("runtime already initialized"); @@ -138,8 +137,6 @@ init_runtime(_PyRuntimeState *runtime, runtime->unicode_ids.next_index = unicode_next_index; runtime->unicode_ids.lock = unicode_ids_mutex; - runtime->func_event_callback = func_event_callback; - runtime->_initialized = 1; } @@ -167,8 +164,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) memcpy(runtime, &initial, sizeof(*runtime)); } init_runtime(runtime, open_code_hook, open_code_userdata, audit_hook_head, - unicode_next_index, lock1, lock2, lock3, lock4, - runtime->func_event_callback); + unicode_next_index, lock1, lock2, lock3, lock4); return _PyStatus_OK(); } @@ -434,6 +430,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->after_forkers_parent); Py_CLEAR(interp->after_forkers_child); #endif + interp->func_event_callback = NULL; _PyAST_Fini(interp); _PyWarnings_Fini(interp); From 4fa1cc8e02cf5fe410fde8c8742243e0acfb2f47 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Wed, 5 Oct 2022 15:50:07 -0700 Subject: [PATCH 008/151] Call the builtin `id` to return the id for a function that's about to be deleted This should would across platforms and implementations. --- Modules/_testcapi/func_events.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c index 81c4a74836ca69..e29d09701e2c96 100644 --- a/Modules/_testcapi/func_events.c +++ b/Modules/_testcapi/func_events.c @@ -3,29 +3,49 @@ static PyObject *pyfunc_callback = NULL; static PyFunction_EventCallback orig_callback = NULL; +static PyObject *get_id(PyObject *obj) { + PyObject *builtins = PyEval_GetBuiltins(); + if (builtins == NULL) { + return NULL; + } + PyObject *id_str = PyUnicode_FromString("id"); + if (id_str == NULL) { + return NULL; + } + PyObject *id_func = PyObject_GetItem(builtins, id_str); + Py_DECREF(id_str); + if (id_func == NULL) { + return NULL; + } + PyObject *stack[] = {obj}; + PyObject *id = PyObject_Vectorcall(id_func, stack, 1, NULL); + Py_DECREF(id_func); + return id; +} + static void call_pyfunc_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) { PyObject *event_obj = PyLong_FromLong(event); if (event_obj == NULL) { - PyErr_Clear(); return; } if (new_value == NULL) { new_value = Py_None; } Py_INCREF(new_value); - /* Don't expose a function that's about to be destroyed to managed code */ - PyObject *func_or_id = (PyObject *) func; + PyObject *func_or_id = NULL; if (event == PYFUNC_EVENT_DESTROY) { - func_or_id = PyLong_FromLong((long) func); + /* Don't expose a function that's about to be destroyed to managed code */ + func_or_id = get_id((PyObject *) func); if (func_or_id == NULL) { - Py_DECREF(new_value); Py_DECREF(event_obj); + Py_DECREF(new_value); return; } } else { Py_INCREF(func); + func_or_id = (PyObject *) func; } PyObject *stack[] = {event_obj, func_or_id, new_value}; PyObject *res = PyObject_Vectorcall(pyfunc_callback, stack, 3, NULL); From 80f49f03f6c23aa50b4aa0cc9379d86b7a785358 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Wed, 5 Oct 2022 17:28:39 -0700 Subject: [PATCH 009/151] Fix prototype for restore_func_event_callback --- Modules/_testcapi/func_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c index e29d09701e2c96..af218b045427c2 100644 --- a/Modules/_testcapi/func_events.c +++ b/Modules/_testcapi/func_events.c @@ -86,7 +86,7 @@ set_func_event_callback(PyObject *self, PyObject *func) } static PyObject * -restore_func_event_callback() { +restore_func_event_callback(PyObject *self, PyObject *Py_UNUSED(ignored)) { if (pyfunc_callback == NULL) { PyErr_SetString(PyExc_RuntimeError, "nothing to restore"); return NULL; From 81fcd2c756020ec084118c95b943326ea7abd24e Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 4 Oct 2022 21:56:47 +0400 Subject: [PATCH 010/151] gh-93357: Start porting asyncio server test cases to IsolatedAsyncioTestCase (#93369) Lay the foundation for further work in `asyncio.test_streams`. --- Lib/test/test_asyncio/test_streams.py | 292 +++++++++++--------------- 1 file changed, 119 insertions(+), 173 deletions(-) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 0c49099bc499a5..d1f8aef4bb9cbd 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -566,46 +566,10 @@ def test_exception_cancel(self): test_utils.run_briefly(self.loop) self.assertIs(stream._waiter, None) - def test_start_server(self): - - class MyServer: - - def __init__(self, loop): - self.server = None - self.loop = loop - - async def handle_client(self, client_reader, client_writer): - data = await client_reader.readline() - client_writer.write(data) - await client_writer.drain() - client_writer.close() - await client_writer.wait_closed() - - def start(self): - sock = socket.create_server(('127.0.0.1', 0)) - self.server = self.loop.run_until_complete( - asyncio.start_server(self.handle_client, - sock=sock)) - return sock.getsockname() - - def handle_client_callback(self, client_reader, client_writer): - self.loop.create_task(self.handle_client(client_reader, - client_writer)) - - def start_callback(self): - sock = socket.create_server(('127.0.0.1', 0)) - addr = sock.getsockname() - sock.close() - self.server = self.loop.run_until_complete( - asyncio.start_server(self.handle_client_callback, - host=addr[0], port=addr[1])) - return addr - - def stop(self): - if self.server is not None: - self.server.close() - self.loop.run_until_complete(self.server.wait_closed()) - self.server = None + +class NewStreamTests(unittest.IsolatedAsyncioTestCase): + + async def test_start_server(self): async def client(addr): reader, writer = await asyncio.open_connection(*addr) @@ -617,61 +581,43 @@ async def client(addr): await writer.wait_closed() return msgback - messages = [] - self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - - # test the server variant with a coroutine as client handler - server = MyServer(self.loop) - addr = server.start() - msg = self.loop.run_until_complete(self.loop.create_task(client(addr))) - server.stop() - self.assertEqual(msg, b"hello world!\n") + async def handle_client(client_reader, client_writer): + data = await client_reader.readline() + client_writer.write(data) + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + with self.subTest(msg="coroutine"): + server = await asyncio.start_server( + handle_client, + host=socket_helper.HOSTv4 + ) + addr = server.sockets[0].getsockname() + msg = await client(addr) + server.close() + await server.wait_closed() + self.assertEqual(msg, b"hello world!\n") - # test the server variant with a callback as client handler - server = MyServer(self.loop) - addr = server.start_callback() - msg = self.loop.run_until_complete(self.loop.create_task(client(addr))) - server.stop() - self.assertEqual(msg, b"hello world!\n") + with self.subTest(msg="callback"): + async def handle_client_callback(client_reader, client_writer): + asyncio.get_running_loop().create_task( + handle_client(client_reader, client_writer) + ) - self.assertEqual(messages, []) + server = await asyncio.start_server( + handle_client_callback, + host=socket_helper.HOSTv4 + ) + addr = server.sockets[0].getsockname() + reader, writer = await asyncio.open_connection(*addr) + msg = await client(addr) + server.close() + await server.wait_closed() + self.assertEqual(msg, b"hello world!\n") @socket_helper.skip_unless_bind_unix_socket - def test_start_unix_server(self): - - class MyServer: - - def __init__(self, loop, path): - self.server = None - self.loop = loop - self.path = path - - async def handle_client(self, client_reader, client_writer): - data = await client_reader.readline() - client_writer.write(data) - await client_writer.drain() - client_writer.close() - await client_writer.wait_closed() - - def start(self): - self.server = self.loop.run_until_complete( - asyncio.start_unix_server(self.handle_client, - path=self.path)) - - def handle_client_callback(self, client_reader, client_writer): - self.loop.create_task(self.handle_client(client_reader, - client_writer)) - - def start_callback(self): - start = asyncio.start_unix_server(self.handle_client_callback, - path=self.path) - self.server = self.loop.run_until_complete(start) - - def stop(self): - if self.server is not None: - self.server.close() - self.loop.run_until_complete(self.server.wait_closed()) - self.server = None + async def test_start_unix_server(self): async def client(path): reader, writer = await asyncio.open_unix_connection(path) @@ -683,64 +629,42 @@ async def client(path): await writer.wait_closed() return msgback - messages = [] - self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - - # test the server variant with a coroutine as client handler - with test_utils.unix_socket_path() as path: - server = MyServer(self.loop, path) - server.start() - msg = self.loop.run_until_complete( - self.loop.create_task(client(path))) - server.stop() - self.assertEqual(msg, b"hello world!\n") - - # test the server variant with a callback as client handler - with test_utils.unix_socket_path() as path: - server = MyServer(self.loop, path) - server.start_callback() - msg = self.loop.run_until_complete( - self.loop.create_task(client(path))) - server.stop() - self.assertEqual(msg, b"hello world!\n") - - self.assertEqual(messages, []) + async def handle_client(client_reader, client_writer): + data = await client_reader.readline() + client_writer.write(data) + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + with self.subTest(msg="coroutine"): + with test_utils.unix_socket_path() as path: + server = await asyncio.start_unix_server( + handle_client, + path=path + ) + msg = await client(path) + server.close() + await server.wait_closed() + self.assertEqual(msg, b"hello world!\n") + + with self.subTest(msg="callback"): + async def handle_client_callback(client_reader, client_writer): + asyncio.get_running_loop().create_task( + handle_client(client_reader, client_writer) + ) + + with test_utils.unix_socket_path() as path: + server = await asyncio.start_unix_server( + handle_client_callback, + path=path + ) + msg = await client(path) + server.close() + await server.wait_closed() + self.assertEqual(msg, b"hello world!\n") @unittest.skipIf(ssl is None, 'No ssl module') - def test_start_tls(self): - - class MyServer: - - def __init__(self, loop): - self.server = None - self.loop = loop - - async def handle_client(self, client_reader, client_writer): - data1 = await client_reader.readline() - client_writer.write(data1) - await client_writer.drain() - assert client_writer.get_extra_info('sslcontext') is None - await client_writer.start_tls( - test_utils.simple_server_sslcontext()) - assert client_writer.get_extra_info('sslcontext') is not None - data2 = await client_reader.readline() - client_writer.write(data2) - await client_writer.drain() - client_writer.close() - await client_writer.wait_closed() - - def start(self): - sock = socket.create_server(('127.0.0.1', 0)) - self.server = self.loop.run_until_complete( - asyncio.start_server(self.handle_client, - sock=sock)) - return sock.getsockname() - - def stop(self): - if self.server is not None: - self.server.close() - self.loop.run_until_complete(self.server.wait_closed()) - self.server = None + async def test_start_tls(self): async def client(addr): reader, writer = await asyncio.open_connection(*addr) @@ -757,18 +681,49 @@ async def client(addr): await writer.wait_closed() return msgback1, msgback2 - messages = [] - self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - - server = MyServer(self.loop) - addr = server.start() - msg1, msg2 = self.loop.run_until_complete(client(addr)) - server.stop() - - self.assertEqual(messages, []) + async def handle_client(client_reader, client_writer): + data1 = await client_reader.readline() + client_writer.write(data1) + await client_writer.drain() + assert client_writer.get_extra_info('sslcontext') is None + await client_writer.start_tls( + test_utils.simple_server_sslcontext()) + assert client_writer.get_extra_info('sslcontext') is not None + + data2 = await client_reader.readline() + client_writer.write(data2) + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + server = await asyncio.start_server( + handle_client, + host=socket_helper.HOSTv4 + ) + addr = server.sockets[0].getsockname() + + msg1, msg2 = await client(addr) + server.close() + await server.wait_closed() self.assertEqual(msg1, b"hello world 1!\n") self.assertEqual(msg2, b"hello world 2!\n") + +class StreamTests2(test_utils.TestCase): + + def setUp(self): + super().setUp() + self.loop = asyncio.new_event_loop() + self.set_event_loop(self.loop) + + def tearDown(self): + # just in case if we have transport close callbacks + test_utils.run_briefly(self.loop) + + self.loop.close() + gc.collect() + super().tearDown() + @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") def test_read_all_from_pipe_reader(self): # See asyncio issue 168. This test is derived from the example @@ -986,22 +941,20 @@ def test_LimitOverrunError_pickleable(self): self.assertEqual(str(e), str(e2)) self.assertEqual(e.consumed, e2.consumed) - def test_wait_closed_on_close(self): - with test_utils.run_test_server() as httpd: + async def test_wait_closed_on_close(self): + async with test_utils.run_test_server() as httpd: rd, wr = self.loop.run_until_complete( asyncio.open_connection(*httpd.address)) wr.write(b'GET / HTTP/1.0\r\n\r\n') - f = rd.readline() - data = self.loop.run_until_complete(f) + data = await rd.readline() self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') - f = rd.read() - data = self.loop.run_until_complete(f) + await rd.read() self.assertTrue(data.endswith(b'\r\n\r\nTest message')) self.assertFalse(wr.is_closing()) wr.close() self.assertTrue(wr.is_closing()) - self.loop.run_until_complete(wr.wait_closed()) + await wr.wait_closed() def test_wait_closed_on_close_with_unread_data(self): with test_utils.run_test_server() as httpd: @@ -1057,15 +1010,10 @@ async def inner(httpd): self.assertEqual(messages, []) - def test_eof_feed_when_closing_writer(self): + async def test_eof_feed_when_closing_writer(self): # See http://bugs.python.org/issue35065 - messages = [] - self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - - with test_utils.run_test_server() as httpd: - rd, wr = self.loop.run_until_complete( - asyncio.open_connection(*httpd.address)) - + async with test_utils.run_test_server() as httpd: + rd, wr = await asyncio.open_connection(*httpd.address) wr.close() f = wr.wait_closed() self.loop.run_until_complete(f) @@ -1074,8 +1022,6 @@ def test_eof_feed_when_closing_writer(self): data = self.loop.run_until_complete(f) self.assertEqual(data, b'') - self.assertEqual(messages, []) - if __name__ == '__main__': unittest.main() From cc60df01d0b612cb84e9d5b638af997d55aab3ab Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 4 Oct 2022 11:36:20 -0700 Subject: [PATCH 011/151] GH-95913: Update what's new in 3.11 for asyncio (#97806) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: C.A.M. Gerlach Co-authored-by: Jelle Zijlstra --- Doc/whatsnew/3.11.rst | 56 ++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 19821e1b602994..0d38abfc000597 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -529,27 +529,51 @@ New Modules Improved Modules ================ +.. _whatsnew311-asyncio: + asyncio ------- -* Add raw datagram socket functions to the event loop: - :meth:`~asyncio.AbstractEventLoop.sock_sendto`, - :meth:`~asyncio.AbstractEventLoop.sock_recvfrom` and - :meth:`~asyncio.AbstractEventLoop.sock_recvfrom_into`. - (Contributed by Alex Grönholm in :issue:`46805`.) - -* Add :meth:`~asyncio.streams.StreamWriter.start_tls` method for upgrading - existing stream-based connections to TLS. (Contributed by Ian Good in - :issue:`34975`.) - -* Add :class:`~asyncio.Barrier` class to the synchronization primitives of - the asyncio library. (Contributed by Yves Duprat and Andrew Svetlov in - :gh:`87518`.) - -* Add :class:`~asyncio.TaskGroup` class, +* Added the :class:`~asyncio.TaskGroup` class, an :ref:`asynchronous context manager ` holding a group of tasks that will wait for all of them upon exit. - (Contributed by Yury Seliganov and others.) + For new code this is recommended over using + :func:`~asyncio.create_task` and :func:`~asyncio.gather` directly. + (Contributed by Yury Selivanov and others in :gh:`90908`.) + +* Added :func:`~asyncio.timeout`, an asynchronous context manager for + setting a timeout on asynchronous operations. For new code this is + recommended over using :func:`~asyncio.wait_for` directly. + (Contributed by Andrew Svetlov in :gh:`90927`.) + +* Added the :class:`~asyncio.Runner` class, which exposes the machinery + used by :func:`~asyncio.run`. + (Contributed by Andrew Svetlov in :gh:`91218`.) + +* Added the :class:`~asyncio.Barrier` class to the synchronization + primitives in the asyncio library, and the related + :exc:`~asyncio.BrokenBarrierError` exception. + (Contributed by Yves Duprat and Andrew Svetlov in :gh:`87518`.) + +* Added keyword argument *all_errors* to :meth:`asyncio.loop.create_connection` + so that multiple connection errors can be raised as an :exc:`ExceptionGroup`. + +* Added the :meth:`asyncio.StreamWriter.start_tls` method for + upgrading existing stream-based connections to TLS. + (Contributed by Ian Good in :issue:`34975`.) + +* Added raw datagram socket functions to the event loop: + :meth:`~asyncio.loop.sock_sendto`, + :meth:`~asyncio.loop.sock_recvfrom` and + :meth:`~asyncio.loop.sock_recvfrom_into`. + These have implementations in :class:`~asyncio.SelectorEventLoop` and + :class:`~asyncio.ProactorEventLoop`. + (Contributed by Alex Grönholm in :issue:`46805`.) + +* Added :meth:`~asyncio.Task.cancelling` and + :meth:`~asyncio.Task.uncancel` methods to :class:`~asyncio.Task`. + These are primarily intended for internal use, + notably by :class:`~asyncio.TaskGroup`. contextlib ---------- From 39bef0355405e807415a2147473873dfd1a5a129 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Wed, 5 Oct 2022 07:04:44 +0900 Subject: [PATCH 012/151] gh-90301: Doc: Add references to PEP 686 (#96816) Doc: Add references to PEP 686. --- Doc/library/io.rst | 16 +++++++++------- Doc/library/os.rst | 5 +++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 97a70646a93cab..7ec990c3212a3e 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -123,17 +123,19 @@ encoding is not UTF-8 for most Windows users. For example:: with open("README.md") as f: long_description = f.read() -Additionally, while there is no concrete plan as of yet, Python may change -the default text file encoding to UTF-8 in the future. - Accordingly, it is highly recommended that you specify the encoding explicitly when opening text files. If you want to use UTF-8, pass ``encoding="utf-8"``. To use the current locale encoding, -``encoding="locale"`` is supported in Python 3.10. +``encoding="locale"`` is supported since Python 3.10. + +.. seealso:: + + :ref:`utf8-mode` + Python UTF-8 Mode can be used to change the default encoding to + UTF-8 from locale-specific encoding. -When you need to run existing code on Windows that attempts to open -UTF-8 files using the default locale encoding, you can enable the UTF-8 -mode. See :ref:`UTF-8 mode on Windows `. + :pep:`686` + Python 3.15 will make :ref:`utf8-mode` default. .. _io-encoding-warning: diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 1436b324c2d875..cb06dc690b4d07 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -159,6 +159,11 @@ can be read from :data:`sys.flags.utf8_mode `. See also the :ref:`UTF-8 mode on Windows ` and the :term:`filesystem encoding and error handler`. +.. seealso:: + + :pep:`686` + Python 3.15 will make :ref:`utf8-mode` default. + .. _os-procinfo: From de65e64222aa7473195f3e150226d00c2c577e03 Mon Sep 17 00:00:00 2001 From: Daniel Giger Date: Tue, 4 Oct 2022 18:18:04 -0400 Subject: [PATCH 013/151] gh-96448: fix documentation for _thread.lock.acquire (#96449) * fix documentation for _thread.lock.acquire * update formatting of _thread.lock.acquire() doc --- Doc/library/_thread.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst index d61ce9039349db..9df9e7914e093b 100644 --- a/Doc/library/_thread.rst +++ b/Doc/library/_thread.rst @@ -157,21 +157,21 @@ This module defines the following constants and functions: Lock objects have the following methods: -.. method:: lock.acquire(waitflag=1, timeout=-1) +.. method:: lock.acquire(blocking=True, timeout=-1) Without any optional argument, this method acquires the lock unconditionally, if necessary waiting until it is released by another thread (only one thread at a time can acquire a lock --- that's their reason for existence). - If the integer *waitflag* argument is present, the action depends on its - value: if it is zero, the lock is only acquired if it can be acquired - immediately without waiting, while if it is nonzero, the lock is acquired + If the *blocking* argument is present, the action depends on its + value: if it is False, the lock is only acquired if it can be acquired + immediately without waiting, while if it is True, the lock is acquired unconditionally as above. If the floating-point *timeout* argument is present and positive, it specifies the maximum wait time in seconds before returning. A negative *timeout* argument specifies an unbounded wait. You cannot specify - a *timeout* if *waitflag* is zero. + a *timeout* if *blocking* is False. The return value is ``True`` if the lock is acquired successfully, ``False`` if not. From c00e5c56840d930a872bd6de0a30aba1dc78a975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 4 Oct 2022 15:31:16 -0700 Subject: [PATCH 014/151] gh-97008: Add a Python implementation of AttributeError and NameError suggestions (#97022) Relevant tests moved from test_exceptions to test_traceback to be able to compare both implementations. Co-authored-by: Carl Friedrich Bolz-Tereick --- .gitattributes | 1 + Lib/test/levenshtein_examples.json | 50002 ++++++++++++++++ Lib/test/test_exceptions.py | 503 +- Lib/test/test_traceback.py | 512 +- Lib/traceback.py | 127 + Makefile.pre.in | 7 +- ...2-10-04-00-43-43.gh-issue-97008.3rjtt6.rst | 5 + .../scripts/generate_levenshtein_examples.py | 70 + 8 files changed, 50707 insertions(+), 520 deletions(-) create mode 100644 Lib/test/levenshtein_examples.json create mode 100644 Misc/NEWS.d/next/Library/2022-10-04-00-43-43.gh-issue-97008.3rjtt6.rst create mode 100644 Tools/scripts/generate_levenshtein_examples.py diff --git a/.gitattributes b/.gitattributes index 79f7b712aa459e..d7e0ab2f36f5db 100644 --- a/.gitattributes +++ b/.gitattributes @@ -73,6 +73,7 @@ Include/internal/pycore_runtime_init_generated.h generated Include/opcode.h generated Include/token.h generated Lib/keyword.py generated +Lib/test/levenshtein_examples.json generated Lib/test/test_stable_abi_ctypes.py generated Lib/token.py generated Objects/typeslots.inc generated diff --git a/Lib/test/levenshtein_examples.json b/Lib/test/levenshtein_examples.json new file mode 100644 index 00000000000000..a32672cfdba88e --- /dev/null +++ b/Lib/test/levenshtein_examples.json @@ -0,0 +1,50002 @@ +[ + [ + "", + "", + 0 + ], + [ + "", + "AabBbb", + 12 + ], + [ + "", + "AbaC", + 8 + ], + [ + "", + "B", + 2 + ], + [ + "", + "Ba", + 4 + ], + [ + "", + "CBaACCCa", + 16 + ], + [ + "", + "CCbBC", + 10 + ], + [ + "", + "CcbCbaaAB", + 18 + ], + [ + "", + "bAa", + 6 + ], + [ + "", + "bAaCABb", + 14 + ], + [ + "A", + "A", + 0 + ], + [ + "A", + "AA", + 2 + ], + [ + "A", + "AABCAabcC", + 16 + ], + [ + "A", + "AACB", + 6 + ], + [ + "A", + "AACC", + 6 + ], + [ + "A", + "ABAaBbc", + 12 + ], + [ + "A", + "ABCcC", + 8 + ], + [ + "A", + "ABa", + 4 + ], + [ + "A", + "ABbBabcaa", + 16 + ], + [ + "A", + "ABbbCBcA", + 14 + ], + [ + "A", + "ACCBcBbBb", + 16 + ], + [ + "A", + "ACa", + 4 + ], + [ + "A", + "ACb", + 4 + ], + [ + "A", + "ACcbbaAB", + 14 + ], + [ + "A", + "AaAaccAb", + 14 + ], + [ + "A", + "AaabbCC", + 12 + ], + [ + "A", + "AabB", + 6 + ], + [ + "A", + "AbBa", + 6 + ], + [ + "A", + "AbBaA", + 8 + ], + [ + "A", + "AbCBBCaC", + 14 + ], + [ + "A", + "AbCb", + 6 + ], + [ + "A", + "AbcB", + 6 + ], + [ + "A", + "B", + 2 + ], + [ + "A", + "BA", + 2 + ], + [ + "A", + "BABbCc", + 10 + ], + [ + "A", + "BABca", + 8 + ], + [ + "A", + "BB", + 4 + ], + [ + "A", + "BBaBBaa", + 13 + ], + [ + "A", + "BBbbaAA", + 12 + ], + [ + "A", + "BCCAAc", + 10 + ], + [ + "A", + "BCa", + 5 + ], + [ + "A", + "BCcaC", + 9 + ], + [ + "A", + "BCccaa", + 11 + ], + [ + "A", + "BaacCbC", + 13 + ], + [ + "A", + "BabCC", + 9 + ], + [ + "A", + "Bac", + 5 + ], + [ + "A", + "BbCbb", + 10 + ], + [ + "A", + "BbaBAC", + 10 + ], + [ + "A", + "Bbc", + 6 + ], + [ + "A", + "BcB", + 6 + ], + [ + "A", + "BcCAbCBA", + 14 + ], + [ + "A", + "BcaaCbCB", + 15 + ], + [ + "A", + "C", + 2 + ], + [ + "A", + "CA", + 2 + ], + [ + "A", + "CABAacB", + 12 + ], + [ + "A", + "CABBABBc", + 14 + ], + [ + "A", + "CAaaBc", + 10 + ], + [ + "A", + "CBAA", + 6 + ], + [ + "A", + "CBCcBaBAB", + 16 + ], + [ + "A", + "CBcbc", + 10 + ], + [ + "A", + "CC", + 4 + ], + [ + "A", + "CCA", + 4 + ], + [ + "A", + "CCB", + 6 + ], + [ + "A", + "CCBaACcC", + 14 + ], + [ + "A", + "CCBcAAacb", + 16 + ], + [ + "A", + "CaCCaA", + 10 + ], + [ + "A", + "CbAB", + 6 + ], + [ + "A", + "CbBaAACC", + 14 + ], + [ + "A", + "CbCCbB", + 12 + ], + [ + "A", + "CbacBb", + 11 + ], + [ + "A", + "CbbABc", + 10 + ], + [ + "A", + "CcC", + 6 + ], + [ + "A", + "CcCacAAAC", + 16 + ], + [ + "A", + "CccCbaCcb", + 17 + ], + [ + "A", + "a", + 1 + ], + [ + "A", + "aABABBa", + 12 + ], + [ + "A", + "aABBA", + 8 + ], + [ + "A", + "aABCaBCa", + 14 + ], + [ + "A", + "aACBabAC", + 14 + ], + [ + "A", + "aBCb", + 7 + ], + [ + "A", + "aBaB", + 7 + ], + [ + "A", + "aBbBb", + 9 + ], + [ + "A", + "aC", + 3 + ], + [ + "A", + "aCA", + 4 + ], + [ + "A", + "aaBBb", + 9 + ], + [ + "A", + "aaC", + 5 + ], + [ + "A", + "aaCb", + 7 + ], + [ + "A", + "aaCbABbb", + 14 + ], + [ + "A", + "aaCcbb", + 11 + ], + [ + "A", + "aaaaCcAB", + 14 + ], + [ + "A", + "aaabb", + 9 + ], + [ + "A", + "aaacCabCC", + 17 + ], + [ + "A", + "aacBccCAC", + 16 + ], + [ + "A", + "aacC", + 7 + ], + [ + "A", + "abCAb", + 8 + ], + [ + "A", + "abbBcaccc", + 17 + ], + [ + "A", + "acCAB", + 8 + ], + [ + "A", + "acacBcAA", + 14 + ], + [ + "A", + "b", + 2 + ], + [ + "A", + "bABACaB", + 12 + ], + [ + "A", + "bAbA", + 6 + ], + [ + "A", + "bAbaBc", + 10 + ], + [ + "A", + "bBAa", + 6 + ], + [ + "A", + "bBa", + 5 + ], + [ + "A", + "bBaCAab", + 12 + ], + [ + "A", + "bBaCaCBbB", + 17 + ], + [ + "A", + "bBbAbaBa", + 14 + ], + [ + "A", + "bBbacabAb", + 16 + ], + [ + "A", + "bCCbA", + 8 + ], + [ + "A", + "bCCbB", + 10 + ], + [ + "A", + "bCbbACc", + 12 + ], + [ + "A", + "bCbbc", + 10 + ], + [ + "A", + "baACBCB", + 12 + ], + [ + "A", + "babABb", + 10 + ], + [ + "A", + "bacB", + 7 + ], + [ + "A", + "bacacA", + 10 + ], + [ + "A", + "bb", + 4 + ], + [ + "A", + "bbCa", + 7 + ], + [ + "A", + "bbCbaa", + 11 + ], + [ + "A", + "bbbAcb", + 10 + ], + [ + "A", + "bc", + 4 + ], + [ + "A", + "bcAAcCAb", + 14 + ], + [ + "A", + "bcC", + 6 + ], + [ + "A", + "bcbbaab", + 13 + ], + [ + "A", + "c", + 2 + ], + [ + "A", + "cAB", + 4 + ], + [ + "A", + "cACAc", + 8 + ], + [ + "A", + "cAaBccaC", + 14 + ], + [ + "A", + "cAcAaCAc", + 14 + ], + [ + "A", + "cBAaC", + 8 + ], + [ + "A", + "cBAac", + 8 + ], + [ + "A", + "cBCAABbc", + 14 + ], + [ + "A", + "cBaCbab", + 13 + ], + [ + "A", + "cBacA", + 8 + ], + [ + "A", + "cBb", + 6 + ], + [ + "A", + "cC", + 4 + ], + [ + "A", + "cCBABaC", + 12 + ], + [ + "A", + "cCCCa", + 9 + ], + [ + "A", + "cCa", + 5 + ], + [ + "A", + "cCaAaCaCB", + 16 + ], + [ + "A", + "cCabbbCa", + 15 + ], + [ + "A", + "caacCacAb", + 16 + ], + [ + "A", + "cabbC", + 9 + ], + [ + "A", + "cb", + 4 + ], + [ + "A", + "cbC", + 6 + ], + [ + "A", + "cbCAaAaAB", + 16 + ], + [ + "A", + "cbbaaAbbB", + 16 + ], + [ + "A", + "cc", + 4 + ], + [ + "A", + "ccCcCBaB", + 15 + ], + [ + "A", + "ccabbC", + 11 + ], + [ + "AA", + "AA", + 0 + ], + [ + "AA", + "ACAA", + 4 + ], + [ + "AA", + "BAc", + 4 + ], + [ + "AA", + "BC", + 4 + ], + [ + "AA", + "BaccaAA", + 10 + ], + [ + "AA", + "Bba", + 5 + ], + [ + "AA", + "BbcbcccAB", + 16 + ], + [ + "AA", + "CAb", + 4 + ], + [ + "AA", + "Cc", + 4 + ], + [ + "AA", + "CcBACCa", + 11 + ], + [ + "AA", + "aCBa", + 6 + ], + [ + "AA", + "aaBA", + 5 + ], + [ + "AA", + "aaBac", + 8 + ], + [ + "AA", + "bAAabBA", + 10 + ], + [ + "AA", + "bABbacbb", + 13 + ], + [ + "AA", + "bBbBbc", + 12 + ], + [ + "AA", + "bC", + 4 + ], + [ + "AA", + "bCabb", + 9 + ], + [ + "AA", + "baAB", + 5 + ], + [ + "AA", + "bbBaBcCBB", + 17 + ], + [ + "AA", + "bbBbAaaB", + 13 + ], + [ + "AA", + "c", + 4 + ], + [ + "AA", + "cACaCbab", + 13 + ], + [ + "AA", + "cBbCbBcA", + 14 + ], + [ + "AA", + "cBba", + 7 + ], + [ + "AA", + "cCbBBCca", + 15 + ], + [ + "AA", + "cacaca", + 10 + ], + [ + "AA", + "ccbccAa", + 11 + ], + [ + "AAA", + "B", + 6 + ], + [ + "AAA", + "C", + 6 + ], + [ + "AAA", + "aBBbcAaBa", + 14 + ], + [ + "AAA", + "aacbbBbbc", + 16 + ], + [ + "AAA", + "abAb", + 5 + ], + [ + "AAA", + "cccaAbBcB", + 15 + ], + [ + "AAAAABA", + "CaBcAaa", + 10 + ], + [ + "AAABACbC", + "BBBC", + 11 + ], + [ + "AAABAbaaA", + "CBbbacb", + 12 + ], + [ + "AAABa", + "AA", + 6 + ], + [ + "AAABc", + "cbBabcaBA", + 14 + ], + [ + "AAAC", + "acBcCCcC", + 13 + ], + [ + "AAACAbAaa", + "BCc", + 16 + ], + [ + "AAACBACc", + "CaCabAc", + 10 + ], + [ + "AAACCbbcA", + "bc", + 14 + ], + [ + "AAACa", + "A", + 8 + ], + [ + "AAACbCaa", + "acABAAa", + 9 + ], + [ + "AAACbaaa", + "BBa", + 13 + ], + [ + "AAAa", + "bBaaCc", + 9 + ], + [ + "AAAaAB", + "BCB", + 10 + ], + [ + "AAAaAcC", + "CAbBcBBC", + 12 + ], + [ + "AAAaCAAb", + "aBbAbaaA", + 12 + ], + [ + "AAAaCaA", + "cBcABaAb", + 11 + ], + [ + "AAAaaaaA", + "ABBBcaA", + 10 + ], + [ + "AAAb", + "AcC", + 6 + ], + [ + "AAAb", + "B", + 7 + ], + [ + "AAAbAB", + "cCBaAbcc", + 11 + ], + [ + "AAAbBcbc", + "C", + 15 + ], + [ + "AAAbaAABb", + "cCcAaca", + 15 + ], + [ + "AAAbbaA", + "Cbcc", + 12 + ], + [ + "AAAbbc", + "ABcaCCbca", + 11 + ], + [ + "AAAbcAcBA", + "bBaACAC", + 13 + ], + [ + "AAAbcbCa", + "cabCCbCba", + 10 + ], + [ + "AAAcAcCC", + "cbBCAAB", + 13 + ], + [ + "AAAcBAbCB", + "cbbABC", + 12 + ], + [ + "AAB", + "CB", + 4 + ], + [ + "AAB", + "accAC", + 7 + ], + [ + "AAB", + "bCCbaBbAc", + 15 + ], + [ + "AAB", + "bbb", + 5 + ], + [ + "AAB", + "bcCBcC", + 10 + ], + [ + "AABA", + "AAc", + 4 + ], + [ + "AABA", + "cBCAbA", + 7 + ], + [ + "AABABCcCc", + "abAC", + 12 + ], + [ + "AABAaB", + "BAB", + 6 + ], + [ + "AABAbAa", + "BcaB", + 11 + ], + [ + "AABAcb", + "cca", + 10 + ], + [ + "AABBA", + "AbAaCBb", + 8 + ], + [ + "AABBAACcC", + "A", + 16 + ], + [ + "AABBBcb", + "B", + 12 + ], + [ + "AABBCaB", + "bAbBA", + 8 + ], + [ + "AABBCabC", + "BCC", + 10 + ], + [ + "AABBCcB", + "cABBBAACc", + 10 + ], + [ + "AABBabAA", + "A", + 14 + ], + [ + "AABBac", + "BACCCB", + 10 + ], + [ + "AABBc", + "CAaBaA", + 7 + ], + [ + "AABCb", + "CcaCab", + 8 + ], + [ + "AABCbccb", + "B", + 14 + ], + [ + "AABCcA", + "BabC", + 8 + ], + [ + "AABCcB", + "BAB", + 8 + ], + [ + "AABCca", + "AbBBC", + 7 + ], + [ + "AABCcbBB", + "Bab", + 12 + ], + [ + "AABa", + "ACCcBA", + 7 + ], + [ + "AABa", + "BCcbaAa", + 11 + ], + [ + "AABa", + "C", + 8 + ], + [ + "AABaAc", + "Ccaa", + 9 + ], + [ + "AABaC", + "aA", + 7 + ], + [ + "AABaCccb", + "AABaAAbc", + 8 + ], + [ + "AABabcc", + "ccaABC", + 11 + ], + [ + "AABacB", + "cCbaa", + 9 + ], + [ + "AABacBac", + "AcAa", + 10 + ], + [ + "AABaccb", + "CcbBbbc", + 12 + ], + [ + "AABbBa", + "bBAcbaaB", + 10 + ], + [ + "AABbCc", + "AccbaB", + 8 + ], + [ + "AABba", + "cBbAc", + 7 + ], + [ + "AABbaBa", + "BBaAbb", + 10 + ], + [ + "AABc", + "BccAAA", + 10 + ], + [ + "AABcABaaA", + "cbB", + 14 + ], + [ + "AABcAC", + "acabAC", + 7 + ], + [ + "AABcBbcb", + "baAACAbAB", + 11 + ], + [ + "AABcCB", + "ca", + 10 + ], + [ + "AABcaBC", + "AaAaab", + 8 + ], + [ + "AAC", + "CBabAaB", + 11 + ], + [ + "AAC", + "aBA", + 5 + ], + [ + "AAC", + "aCBCBbac", + 13 + ], + [ + "AAC", + "aCa", + 5 + ], + [ + "AAC", + "baaBAba", + 11 + ], + [ + "AAC", + "bbAbc", + 7 + ], + [ + "AACA", + "aaCB", + 4 + ], + [ + "AACA", + "ab", + 7 + ], + [ + "AACA", + "cc", + 7 + ], + [ + "AACAba", + "CBAAaAB", + 9 + ], + [ + "AACAba", + "b", + 10 + ], + [ + "AACAbbc", + "AbaCABCcB", + 8 + ], + [ + "AACBACbcB", + "b", + 16 + ], + [ + "AACBC", + "Abbc", + 6 + ], + [ + "AACBC", + "aaCcAb", + 8 + ], + [ + "AACBCA", + "A", + 10 + ], + [ + "AACBCcbaa", + "cbBca", + 12 + ], + [ + "AACBaB", + "AAcABBA", + 7 + ], + [ + "AACBc", + "aABCaACA", + 10 + ], + [ + "AACC", + "bB", + 8 + ], + [ + "AACCA", + "b", + 10 + ], + [ + "AACCAC", + "Ccab", + 8 + ], + [ + "AACCBBAac", + "abbabb", + 14 + ], + [ + "AACCCCbc", + "BBCCaB", + 11 + ], + [ + "AACCCc", + "bcCbaBACA", + 14 + ], + [ + "AACCaBcA", + "BaC", + 13 + ], + [ + "AACCaca", + "ccbaBBaB", + 13 + ], + [ + "AACCc", + "b", + 10 + ], + [ + "AACa", + "BAC", + 4 + ], + [ + "AACaAaBA", + "AbcbB", + 11 + ], + [ + "AACaac", + "C", + 10 + ], + [ + "AACacB", + "CcbCBaBb", + 11 + ], + [ + "AACb", + "AC", + 4 + ], + [ + "AACb", + "ac", + 6 + ], + [ + "AACbAC", + "BBCCBCa", + 10 + ], + [ + "AACbC", + "bb", + 8 + ], + [ + "AACbbcaa", + "bbaaBCBb", + 14 + ], + [ + "AACbc", + "AbcbbBcCB", + 11 + ], + [ + "AACbc", + "aCCB", + 6 + ], + [ + "AACbcAaAb", + "BABcaBBAA", + 12 + ], + [ + "AACbcb", + "cabBc", + 8 + ], + [ + "AACc", + "BbCC", + 5 + ], + [ + "AACc", + "bbb", + 8 + ], + [ + "AACcAABAC", + "CBAcBaABa", + 11 + ], + [ + "AACcAb", + "CBCbCACcb", + 11 + ], + [ + "AACcCAAc", + "AbAACbCC", + 11 + ], + [ + "AACcabbC", + "cAAccbAB", + 9 + ], + [ + "AACcbA", + "BCCCcaCc", + 12 + ], + [ + "AAa", + "ACCC", + 6 + ], + [ + "AAa", + "BcAC", + 6 + ], + [ + "AAa", + "CACcaAAAb", + 13 + ], + [ + "AAa", + "CAaCA", + 6 + ], + [ + "AAa", + "aCcBACb", + 11 + ], + [ + "AAa", + "abACAA", + 7 + ], + [ + "AAa", + "b", + 6 + ], + [ + "AAa", + "cAAaBA", + 6 + ], + [ + "AAa", + "cBaB", + 6 + ], + [ + "AAaA", + "ca", + 6 + ], + [ + "AAaACcb", + "ABCcb", + 6 + ], + [ + "AAaB", + "aCAC", + 6 + ], + [ + "AAaB", + "cbAb", + 6 + ], + [ + "AAaBA", + "bBAc", + 8 + ], + [ + "AAaBB", + "ABCCaCBb", + 9 + ], + [ + "AAaBa", + "AcB", + 6 + ], + [ + "AAaBaB", + "aCcb", + 9 + ], + [ + "AAaBacBaC", + "CacABc", + 12 + ], + [ + "AAaBbAb", + "cC", + 14 + ], + [ + "AAaBbcaa", + "caB", + 12 + ], + [ + "AAaC", + "aCbBcBCB", + 13 + ], + [ + "AAaCACAB", + "cBa", + 14 + ], + [ + "AAaCCaCaA", + "baACABCCA", + 10 + ], + [ + "AAaCCcbb", + "aa", + 13 + ], + [ + "AAaCaccb", + "AAA", + 11 + ], + [ + "AAaCbAAAc", + "babcbBccb", + 14 + ], + [ + "AAaCcB", + "aA", + 9 + ], + [ + "AAaa", + "Abcbb", + 8 + ], + [ + "AAaaA", + "ACaCAcaa", + 9 + ], + [ + "AAaaB", + "cAaaAB", + 4 + ], + [ + "AAaaBbaC", + "bbCaCBc", + 12 + ], + [ + "AAaaCBabC", + "bBb", + 14 + ], + [ + "AAaacacca", + "C", + 17 + ], + [ + "AAaaccbA", + "CB", + 14 + ], + [ + "AAabAbac", + "cbABacbbc", + 11 + ], + [ + "AAabBBaA", + "caCcAAB", + 13 + ], + [ + "AAabCA", + "CbACAA", + 9 + ], + [ + "AAabCbCA", + "ACb", + 10 + ], + [ + "AAabb", + "B", + 9 + ], + [ + "AAabc", + "cBc", + 7 + ], + [ + "AAacA", + "CBBcB", + 8 + ], + [ + "AAacA", + "Cc", + 8 + ], + [ + "AAacCBCBc", + "bAaacCc", + 9 + ], + [ + "AAacCaBa", + "a", + 14 + ], + [ + "AAacCbCCC", + "BaB", + 15 + ], + [ + "AAaccABAA", + "bBCb", + 16 + ], + [ + "AAb", + "AbB", + 3 + ], + [ + "AAb", + "B", + 5 + ], + [ + "AAb", + "aCBbBBa", + 11 + ], + [ + "AAb", + "bAAc", + 4 + ], + [ + "AAb", + "bABA", + 5 + ], + [ + "AAb", + "bBCbBbaCA", + 16 + ], + [ + "AAb", + "bbBBaB", + 10 + ], + [ + "AAb", + "bbCcAb", + 8 + ], + [ + "AAbA", + "Bbb", + 6 + ], + [ + "AAbAAaBCC", + "b", + 16 + ], + [ + "AAbABB", + "Ca", + 11 + ], + [ + "AAbAaBBcB", + "BABaCB", + 10 + ], + [ + "AAbBBb", + "a", + 11 + ], + [ + "AAbBCCb", + "bA", + 12 + ], + [ + "AAbBbBcC", + "C", + 14 + ], + [ + "AAbBbbBcc", + "aCA", + 16 + ], + [ + "AAbC", + "cBbBCBBb", + 12 + ], + [ + "AAbCbBcC", + "BaABcCaCc", + 11 + ], + [ + "AAbCcCbC", + "a", + 15 + ], + [ + "AAba", + "cBCabCC", + 11 + ], + [ + "AAba", + "cBcBCcaBc", + 15 + ], + [ + "AAbaCabC", + "Aa", + 12 + ], + [ + "AAbabb", + "cbBAca", + 10 + ], + [ + "AAbb", + "AcbaaAB", + 9 + ], + [ + "AAbb", + "bccb", + 6 + ], + [ + "AAbbAcAB", + "cbacaBBBA", + 14 + ], + [ + "AAbbBAaAC", + "bcbaabc", + 11 + ], + [ + "AAbbbB", + "bCa", + 10 + ], + [ + "AAbcBbA", + "aaccCbB", + 8 + ], + [ + "AAbcaBBBA", + "ABb", + 13 + ], + [ + "AAbcacaB", + "cAAAbBb", + 12 + ], + [ + "AAbcbacC", + "abbbAaCCa", + 10 + ], + [ + "AAc", + "ACAc", + 2 + ], + [ + "AAc", + "AbB", + 4 + ], + [ + "AAc", + "BAabA", + 7 + ], + [ + "AAc", + "aAbc", + 3 + ], + [ + "AAc", + "aaaaacAA", + 12 + ], + [ + "AAc", + "aab", + 4 + ], + [ + "AAc", + "c", + 4 + ], + [ + "AAcA", + "AAbAAA", + 6 + ], + [ + "AAcABCA", + "bBBAbab", + 11 + ], + [ + "AAcAC", + "cCAAa", + 8 + ], + [ + "AAcAac", + "cAcA", + 6 + ], + [ + "AAcAbBcB", + "AAAAB", + 8 + ], + [ + "AAcAccAb", + "Ccac", + 11 + ], + [ + "AAcBBBcCB", + "A", + 16 + ], + [ + "AAcBbA", + "Aa", + 9 + ], + [ + "AAcC", + "bCbcaCCa", + 12 + ], + [ + "AAcCabaB", + "cACAC", + 11 + ], + [ + "AAcaACCBA", + "abcBCca", + 11 + ], + [ + "AAcaCBAcB", + "cCaBCcC", + 11 + ], + [ + "AAcab", + "A", + 8 + ], + [ + "AAcab", + "AcaabCccc", + 12 + ], + [ + "AAcb", + "aCBCcCBcC", + 14 + ], + [ + "AAcbAccc", + "CcccbA", + 12 + ], + [ + "AAcbCCAcB", + "BbbCaACc", + 11 + ], + [ + "AAcbCca", + "a", + 12 + ], + [ + "AAcbc", + "caCcb", + 7 + ], + [ + "AAcc", + "CcaBbcC", + 10 + ], + [ + "AAccB", + "cCca", + 7 + ], + [ + "AAccb", + "aA", + 7 + ], + [ + "AAccccbAA", + "caAaa", + 14 + ], + [ + "AB", + "A", + 2 + ], + [ + "AB", + "AACca", + 8 + ], + [ + "AB", + "ABcAc", + 6 + ], + [ + "AB", + "ACCACCbC", + 13 + ], + [ + "AB", + "BAA", + 4 + ], + [ + "AB", + "BAbac", + 7 + ], + [ + "AB", + "BC", + 4 + ], + [ + "AB", + "BbaCAc", + 10 + ], + [ + "AB", + "Bbac", + 7 + ], + [ + "AB", + "BcA", + 6 + ], + [ + "AB", + "CAAacB", + 8 + ], + [ + "AB", + "CC", + 4 + ], + [ + "AB", + "CCaabBcb", + 13 + ], + [ + "AB", + "CCcB", + 6 + ], + [ + "AB", + "CbA", + 5 + ], + [ + "AB", + "aCAC", + 6 + ], + [ + "AB", + "bBA", + 4 + ], + [ + "AB", + "bCbB", + 6 + ], + [ + "AB", + "bCccbbb", + 13 + ], + [ + "AB", + "bbb", + 5 + ], + [ + "AB", + "bcAbAAA", + 11 + ], + [ + "AB", + "bcbC", + 7 + ], + [ + "AB", + "c", + 4 + ], + [ + "AB", + "cCabccbCB", + 15 + ], + [ + "AB", + "caCBB", + 7 + ], + [ + "AB", + "caaac", + 9 + ], + [ + "AB", + "cababCa", + 12 + ], + [ + "ABA", + "A", + 4 + ], + [ + "ABA", + "ACBcBa", + 7 + ], + [ + "ABA", + "AcAb", + 4 + ], + [ + "ABA", + "abbBaaC", + 10 + ], + [ + "ABA", + "c", + 6 + ], + [ + "ABA", + "caAaCCbc", + 13 + ], + [ + "ABAAB", + "cbB", + 7 + ], + [ + "ABAABacbC", + "Ba", + 14 + ], + [ + "ABAAaCAa", + "cABb", + 14 + ], + [ + "ABAAaaBb", + "BCACaB", + 8 + ], + [ + "ABAAabb", + "bAcABc", + 9 + ], + [ + "ABAAabbCC", + "cbcBc", + 15 + ], + [ + "ABAAbBCb", + "aCACb", + 9 + ], + [ + "ABAAc", + "AbCc", + 5 + ], + [ + "ABAAcC", + "A", + 10 + ], + [ + "ABABBCaBb", + "aAcBcc", + 12 + ], + [ + "ABABCAB", + "cbBBCaA", + 8 + ], + [ + "ABABaaCc", + "BBBBBAB", + 11 + ], + [ + "ABABbCCab", + "aAC", + 13 + ], + [ + "ABAC", + "abaAC", + 4 + ], + [ + "ABACAccCC", + "BBCBcbCAc", + 11 + ], + [ + "ABACCAACA", + "aAacA", + 11 + ], + [ + "ABACaa", + "BA", + 8 + ], + [ + "ABACb", + "b", + 8 + ], + [ + "ABACbaCcC", + "cCBBbCbC", + 12 + ], + [ + "ABAa", + "A", + 6 + ], + [ + "ABAaAbbc", + "ccaCBA", + 13 + ], + [ + "ABAaB", + "bb", + 8 + ], + [ + "ABAaCCAB", + "acba", + 12 + ], + [ + "ABAaaBCBB", + "BaCC", + 12 + ], + [ + "ABAacbAbA", + "ABAb", + 10 + ], + [ + "ABAb", + "AcA", + 4 + ], + [ + "ABAb", + "cCab", + 5 + ], + [ + "ABAbACCB", + "cBCB", + 10 + ], + [ + "ABAbBC", + "BBAacabC", + 9 + ], + [ + "ABAbCCb", + "BAA", + 10 + ], + [ + "ABAbaCBB", + "aaCbbbBB", + 9 + ], + [ + "ABAbaCcC", + "bac", + 10 + ], + [ + "ABAc", + "CBACCbBA", + 11 + ], + [ + "ABAcAcB", + "Ac", + 10 + ], + [ + "ABAca", + "aCcCaAbC", + 12 + ], + [ + "ABAcaBc", + "bCaabABAb", + 14 + ], + [ + "ABAcaac", + "BcA", + 9 + ], + [ + "ABAcabA", + "aa", + 11 + ], + [ + "ABAcb", + "cCa", + 9 + ], + [ + "ABB", + "acaccCcA", + 15 + ], + [ + "ABB", + "bAaAAC", + 10 + ], + [ + "ABB", + "bBbAA", + 7 + ], + [ + "ABB", + "bac", + 6 + ], + [ + "ABBAAaCc", + "CaCaCAaCc", + 9 + ], + [ + "ABBAAbb", + "acBbCba", + 9 + ], + [ + "ABBAAc", + "bBccabAcB", + 11 + ], + [ + "ABBAbbAA", + "abAaaa", + 10 + ], + [ + "ABBAcBaa", + "Ab", + 13 + ], + [ + "ABBAcbAc", + "abBccbA", + 6 + ], + [ + "ABBBAa", + "ccBbcBa", + 9 + ], + [ + "ABBBAbAcc", + "abCaCAbcc", + 10 + ], + [ + "ABBBB", + "cAaCBCbCb", + 12 + ], + [ + "ABBBCaB", + "BaAbBabBa", + 12 + ], + [ + "ABBCC", + "aBBbaBAab", + 13 + ], + [ + "ABBCCc", + "aAcAbB", + 11 + ], + [ + "ABBCCcab", + "AcBcbcbAc", + 10 + ], + [ + "ABBa", + "bA", + 6 + ], + [ + "ABBaACA", + "cbBcAB", + 9 + ], + [ + "ABBaACCcb", + "bbCCcCb", + 10 + ], + [ + "ABBaa", + "bcabCCC", + 12 + ], + [ + "ABBabAbAC", + "AbcbbAcCc", + 10 + ], + [ + "ABBaba", + "BCcbAaBCA", + 13 + ], + [ + "ABBacb", + "CAbcBAbBa", + 11 + ], + [ + "ABBb", + "Aabc", + 5 + ], + [ + "ABBb", + "aAcAb", + 6 + ], + [ + "ABBbBAbCA", + "CCBACAAb", + 14 + ], + [ + "ABBbCbb", + "bccbcAc", + 11 + ], + [ + "ABBbb", + "aAabbCbCC", + 11 + ], + [ + "ABBcBBC", + "bCaB", + 10 + ], + [ + "ABBcCCC", + "bABbaCCC", + 5 + ], + [ + "ABBcb", + "aCc", + 7 + ], + [ + "ABBcbcC", + "AACBbAC", + 8 + ], + [ + "ABBccBcAa", + "ABa", + 12 + ], + [ + "ABBccCBA", + "CCCACa", + 12 + ], + [ + "ABBccc", + "B", + 10 + ], + [ + "ABC", + "BAaACA", + 8 + ], + [ + "ABC", + "BCc", + 4 + ], + [ + "ABC", + "CAa", + 6 + ], + [ + "ABC", + "CBca", + 5 + ], + [ + "ABC", + "CbCbbCB", + 11 + ], + [ + "ABC", + "bacacB", + 10 + ], + [ + "ABC", + "c", + 5 + ], + [ + "ABCAB", + "bAaAc", + 8 + ], + [ + "ABCAC", + "BaCBaC", + 7 + ], + [ + "ABCAa", + "aABbb", + 8 + ], + [ + "ABCAbAacA", + "cbCAa", + 11 + ], + [ + "ABCBC", + "AB", + 6 + ], + [ + "ABCBa", + "CcAACBC", + 8 + ], + [ + "ABCBa", + "caBbccC", + 10 + ], + [ + "ABCCAa", + "A", + 10 + ], + [ + "ABCCC", + "CC", + 6 + ], + [ + "ABCCbcBc", + "AbCBcBB", + 6 + ], + [ + "ABCCcCBB", + "bbccAbBBc", + 11 + ], + [ + "ABCCccaB", + "aCcACCCa", + 10 + ], + [ + "ABCaBa", + "BBb", + 8 + ], + [ + "ABCaBa", + "bbAcAA", + 10 + ], + [ + "ABCaC", + "bcCcab", + 8 + ], + [ + "ABCaa", + "AbCcaAb", + 6 + ], + [ + "ABCab", + "b", + 8 + ], + [ + "ABCabB", + "AAc", + 9 + ], + [ + "ABCb", + "CBBBC", + 7 + ], + [ + "ABCb", + "aABCcBC", + 7 + ], + [ + "ABCbAA", + "Ba", + 9 + ], + [ + "ABCbBBbc", + "cabbbCbcB", + 11 + ], + [ + "ABCbC", + "cc", + 8 + ], + [ + "ABCbCaCCc", + "aABB", + 15 + ], + [ + "ABCbaaCbb", + "CBbBBABBB", + 12 + ], + [ + "ABCbaaac", + "aaCaCCCca", + 13 + ], + [ + "ABCbbA", + "BCaAa", + 7 + ], + [ + "ABCbcC", + "ccbBBa", + 11 + ], + [ + "ABCc", + "Aa", + 6 + ], + [ + "ABCcAabbB", + "cCCaBBc", + 11 + ], + [ + "ABCcBcbC", + "Bcbcc", + 8 + ], + [ + "ABCcb", + "CacBAb", + 9 + ], + [ + "ABCccaAcC", + "BaAcBbC", + 12 + ], + [ + "ABa", + "AaabCa", + 7 + ], + [ + "ABa", + "CaA", + 5 + ], + [ + "ABaABc", + "caC", + 9 + ], + [ + "ABaAC", + "Caca", + 8 + ], + [ + "ABaAb", + "bAaC", + 7 + ], + [ + "ABaB", + "c", + 8 + ], + [ + "ABaBa", + "aacCAc", + 10 + ], + [ + "ABaBb", + "acbaBCcb", + 8 + ], + [ + "ABaBccB", + "BacbBBBcb", + 11 + ], + [ + "ABaCAb", + "aba", + 8 + ], + [ + "ABaCCC", + "aCAaacbc", + 10 + ], + [ + "ABaCc", + "bB", + 8 + ], + [ + "ABaa", + "CCcCC", + 10 + ], + [ + "ABabAbb", + "BCAcB", + 9 + ], + [ + "ABabBC", + "acc", + 9 + ], + [ + "ABabBaAc", + "AbBCbaBBB", + 12 + ], + [ + "ABababAC", + "aabaaBAA", + 8 + ], + [ + "ABabb", + "bBb", + 6 + ], + [ + "ABabcBacC", + "aC", + 14 + ], + [ + "ABabcBcc", + "BAcB", + 9 + ], + [ + "ABacCac", + "bbaCBcbcB", + 11 + ], + [ + "ABacbAAab", + "bC", + 16 + ], + [ + "ABacbbaCC", + "CbCbcbBAA", + 13 + ], + [ + "ABb", + "AaAcB", + 7 + ], + [ + "ABb", + "B", + 4 + ], + [ + "ABb", + "BB", + 3 + ], + [ + "ABb", + "BBaCCAAA", + 14 + ], + [ + "ABb", + "bBBaA", + 7 + ], + [ + "ABb", + "baCaaBaa", + 13 + ], + [ + "ABbA", + "CaACAABa", + 12 + ], + [ + "ABbACAC", + "BAcBBaB", + 12 + ], + [ + "ABbACAaAa", + "Cca", + 14 + ], + [ + "ABbAaa", + "cbacaa", + 7 + ], + [ + "ABbAaaB", + "BBc", + 11 + ], + [ + "ABbAacbAC", + "bAbAaacaa", + 11 + ], + [ + "ABbAbA", + "aaAaB", + 9 + ], + [ + "ABbBAb", + "a", + 11 + ], + [ + "ABbBcCbcA", + "acaAacBAB", + 15 + ], + [ + "ABbCC", + "caACcc", + 9 + ], + [ + "ABbCaAbb", + "b", + 14 + ], + [ + "ABbCb", + "aABCBBca", + 10 + ], + [ + "ABbCbA", + "A", + 10 + ], + [ + "ABbCbA", + "AbCbB", + 4 + ], + [ + "ABbCbbBa", + "B", + 14 + ], + [ + "ABbCcB", + "AaCccab", + 8 + ], + [ + "ABbaACBCc", + "baBCbCbcB", + 11 + ], + [ + "ABbaabcCC", + "aaaacAAAA", + 15 + ], + [ + "ABbacAAA", + "CAAccb", + 13 + ], + [ + "ABbb", + "cAaC", + 8 + ], + [ + "ABbbA", + "BCcbb", + 8 + ], + [ + "ABbbC", + "ABBc", + 4 + ], + [ + "ABbbbB", + "BCabaCc", + 12 + ], + [ + "ABbc", + "bBcAAac", + 10 + ], + [ + "ABbcc", + "AbBABaaa", + 11 + ], + [ + "ABbccBA", + "cACaCcCC", + 11 + ], + [ + "ABc", + "a", + 5 + ], + [ + "ABc", + "aA", + 5 + ], + [ + "ABc", + "b", + 5 + ], + [ + "ABcA", + "CB", + 6 + ], + [ + "ABcAa", + "BcbAC", + 6 + ], + [ + "ABcAaB", + "ab", + 9 + ], + [ + "ABcAbAa", + "AcAABbb", + 9 + ], + [ + "ABcAc", + "c", + 8 + ], + [ + "ABcAcCAc", + "cAcBcCaC", + 8 + ], + [ + "ABcBBcCCB", + "BaAcBbC", + 12 + ], + [ + "ABcBbAb", + "aAbACcAca", + 13 + ], + [ + "ABcBcCCbB", + "cbAC", + 13 + ], + [ + "ABcC", + "Cabc", + 6 + ], + [ + "ABcC", + "c", + 6 + ], + [ + "ABcCAaCc", + "C", + 14 + ], + [ + "ABcCAbCba", + "CcBa", + 12 + ], + [ + "ABcCBb", + "CAb", + 8 + ], + [ + "ABcCCa", + "bCaca", + 7 + ], + [ + "ABcCCcBa", + "Cbc", + 12 + ], + [ + "ABcCbbBbC", + "b", + 16 + ], + [ + "ABcaBC", + "Bcc", + 7 + ], + [ + "ABcaBb", + "CB", + 9 + ], + [ + "ABcb", + "a", + 7 + ], + [ + "ABcbAcc", + "AbBa", + 9 + ], + [ + "ABcbBC", + "cCcAa", + 10 + ], + [ + "ABcbCacCb", + "ba", + 14 + ], + [ + "ABcc", + "bAAcbCa", + 9 + ], + [ + "ABccA", + "CA", + 7 + ], + [ + "ABcccb", + "CB", + 10 + ], + [ + "AC", + "A", + 2 + ], + [ + "AC", + "AAb", + 4 + ], + [ + "AC", + "AB", + 2 + ], + [ + "AC", + "ABBc", + 5 + ], + [ + "AC", + "ABaaaBc", + 11 + ], + [ + "AC", + "ACBaAbb", + 10 + ], + [ + "AC", + "AaBcBC", + 8 + ], + [ + "AC", + "AaCCCB", + 8 + ], + [ + "AC", + "AabB", + 6 + ], + [ + "AC", + "AacAaBc", + 11 + ], + [ + "AC", + "AbBCacbA", + 12 + ], + [ + "AC", + "AbbbcbACb", + 14 + ], + [ + "AC", + "B", + 4 + ], + [ + "AC", + "BBbA", + 8 + ], + [ + "AC", + "BCaa", + 6 + ], + [ + "AC", + "BbCaaA", + 10 + ], + [ + "AC", + "BcAab", + 8 + ], + [ + "AC", + "C", + 2 + ], + [ + "AC", + "CAcb", + 5 + ], + [ + "AC", + "Ca", + 4 + ], + [ + "AC", + "CabAB", + 8 + ], + [ + "AC", + "aABaABAB", + 14 + ], + [ + "AC", + "aAacbbcAC", + 14 + ], + [ + "AC", + "aBCaCBba", + 13 + ], + [ + "AC", + "aa", + 3 + ], + [ + "AC", + "aaaBBabAb", + 16 + ], + [ + "AC", + "baccAbcB", + 13 + ], + [ + "AC", + "c", + 3 + ], + [ + "AC", + "cACCaBA", + 10 + ], + [ + "AC", + "cCaaAacaA", + 15 + ], + [ + "AC", + "cbcbbcC", + 12 + ], + [ + "ACA", + "Aa", + 3 + ], + [ + "ACA", + "C", + 4 + ], + [ + "ACA", + "CbaaBc", + 11 + ], + [ + "ACA", + "aAABAAcb", + 12 + ], + [ + "ACA", + "cACCbaAaC", + 12 + ], + [ + "ACAA", + "AcaAAb", + 5 + ], + [ + "ACAABBaC", + "A", + 14 + ], + [ + "ACAABcA", + "bCBACacC", + 10 + ], + [ + "ACAAC", + "bB", + 10 + ], + [ + "ACAAC", + "cBaaCabaC", + 11 + ], + [ + "ACAAa", + "CAa", + 4 + ], + [ + "ACAAaCaA", + "AAacb", + 9 + ], + [ + "ACAAbcaAa", + "bba", + 14 + ], + [ + "ACABA", + "bAB", + 6 + ], + [ + "ACABA", + "c", + 9 + ], + [ + "ACABABA", + "CcC", + 12 + ], + [ + "ACABACaB", + "bcBAaAB", + 8 + ], + [ + "ACABbCaBc", + "cA", + 15 + ], + [ + "ACAC", + "aaBAA", + 7 + ], + [ + "ACACB", + "aaCcbaBC", + 11 + ], + [ + "ACACBB", + "cCb", + 8 + ], + [ + "ACACBbccA", + "BABbAc", + 10 + ], + [ + "ACACC", + "BaAA", + 8 + ], + [ + "ACACa", + "Bccc", + 8 + ], + [ + "ACACbB", + "aCaacAaC", + 11 + ], + [ + "ACACbab", + "cC", + 11 + ], + [ + "ACAaA", + "Aaabbc", + 9 + ], + [ + "ACAaB", + "caBBcB", + 9 + ], + [ + "ACAaBA", + "CAbCC", + 8 + ], + [ + "ACAaCBccb", + "c", + 16 + ], + [ + "ACAabbCAc", + "BB", + 16 + ], + [ + "ACAb", + "AaaCC", + 7 + ], + [ + "ACAbBCaCA", + "A", + 16 + ], + [ + "ACAbbaaBA", + "aacacABbc", + 15 + ], + [ + "ACAbcB", + "bcBCB", + 7 + ], + [ + "ACAc", + "CCbcaabA", + 12 + ], + [ + "ACAcBCc", + "AcaAbcb", + 8 + ], + [ + "ACAcaabA", + "CACbBC", + 10 + ], + [ + "ACB", + "ACbbaBb", + 8 + ], + [ + "ACB", + "aCbbc", + 6 + ], + [ + "ACB", + "baBacBAB", + 12 + ], + [ + "ACB", + "cabbCcBA", + 11 + ], + [ + "ACBA", + "CbCCCaCAc", + 14 + ], + [ + "ACBAC", + "CaABbbcBA", + 13 + ], + [ + "ACBACBB", + "cAABCbBC", + 9 + ], + [ + "ACBACCba", + "ACBCcCb", + 5 + ], + [ + "ACBAbCBc", + "bC", + 12 + ], + [ + "ACBAba", + "aAaCaC", + 10 + ], + [ + "ACBAcb", + "aCACc", + 6 + ], + [ + "ACBB", + "bAbb", + 6 + ], + [ + "ACBBAB", + "BbBCb", + 8 + ], + [ + "ACBBABBa", + "bbabA", + 11 + ], + [ + "ACBBBaA", + "bccbA", + 10 + ], + [ + "ACBBBaCA", + "bCCCB", + 12 + ], + [ + "ACBBcAccc", + "ab", + 16 + ], + [ + "ACBCCC", + "a", + 11 + ], + [ + "ACBCbaa", + "ACCAbCCb", + 10 + ], + [ + "ACBaA", + "BcBcBcA", + 9 + ], + [ + "ACBaBa", + "AaAa", + 6 + ], + [ + "ACBabaa", + "abcAbbb", + 10 + ], + [ + "ACBabbab", + "CAa", + 11 + ], + [ + "ACBac", + "BcbcCb", + 9 + ], + [ + "ACBb", + "bcCAca", + 10 + ], + [ + "ACBbCaaBB", + "Cc", + 15 + ], + [ + "ACBba", + "CcaAbbc", + 10 + ], + [ + "ACBbaAbAC", + "aCabbAC", + 7 + ], + [ + "ACBbcAcB", + "cb", + 13 + ], + [ + "ACBcAB", + "cb", + 9 + ], + [ + "ACBcaaAAb", + "ACBA", + 10 + ], + [ + "ACC", + "BcccCaAba", + 15 + ], + [ + "ACC", + "Ccb", + 5 + ], + [ + "ACC", + "aB", + 5 + ], + [ + "ACC", + "bBcABBbAC", + 14 + ], + [ + "ACC", + "cABA", + 6 + ], + [ + "ACC", + "caACC", + 4 + ], + [ + "ACCA", + "BaaBc", + 9 + ], + [ + "ACCA", + "BacAabAAa", + 14 + ], + [ + "ACCACABB", + "ABAAbBaBa", + 11 + ], + [ + "ACCAbB", + "CcbAacC", + 11 + ], + [ + "ACCAccAB", + "ACbCC", + 10 + ], + [ + "ACCBAAC", + "Ac", + 11 + ], + [ + "ACCBAaBC", + "a", + 14 + ], + [ + "ACCBB", + "BcCccabbA", + 13 + ], + [ + "ACCBBcac", + "CCB", + 10 + ], + [ + "ACCBCBC", + "AcaaCacba", + 11 + ], + [ + "ACCBac", + "aCACaCc", + 7 + ], + [ + "ACCBb", + "BaCac", + 8 + ], + [ + "ACCBbcbAC", + "AAaCaCBCC", + 12 + ], + [ + "ACCBcCBB", + "aBBABb", + 10 + ], + [ + "ACCBcaaCa", + "CBb", + 14 + ], + [ + "ACCBcc", + "bbAa", + 11 + ], + [ + "ACCCAaCcC", + "CBAc", + 12 + ], + [ + "ACCCBac", + "CcCBACBB", + 9 + ], + [ + "ACCCC", + "CACCACc", + 5 + ], + [ + "ACCCb", + "CbBaCcb", + 9 + ], + [ + "ACCCbaCAc", + "abACCC", + 11 + ], + [ + "ACCaAACa", + "C", + 14 + ], + [ + "ACCaACAa", + "a", + 14 + ], + [ + "ACCaB", + "A", + 8 + ], + [ + "ACCaCA", + "a", + 10 + ], + [ + "ACCaaAc", + "A", + 12 + ], + [ + "ACCac", + "a", + 8 + ], + [ + "ACCbA", + "aCCBa", + 3 + ], + [ + "ACCbB", + "BaAa", + 10 + ], + [ + "ACCbC", + "CcbaCC", + 7 + ], + [ + "ACCbCba", + "a", + 12 + ], + [ + "ACCbaBbb", + "aA", + 14 + ], + [ + "ACCbba", + "aaCBCbccB", + 11 + ], + [ + "ACCbbbC", + "BCbABAbaA", + 13 + ], + [ + "ACCbcBa", + "aCBaBcbC", + 9 + ], + [ + "ACCbcbccB", + "caAAbaCcb", + 13 + ], + [ + "ACCcAccCc", + "AccAb", + 11 + ], + [ + "ACCcBcB", + "AcBcAAca", + 9 + ], + [ + "ACCcC", + "a", + 9 + ], + [ + "ACCcaC", + "cCA", + 8 + ], + [ + "ACCcacC", + "BBaaAcAab", + 14 + ], + [ + "ACa", + "AcbCAbaB", + 10 + ], + [ + "ACa", + "Bca", + 3 + ], + [ + "ACa", + "ba", + 4 + ], + [ + "ACa", + "baAAaba", + 10 + ], + [ + "ACaA", + "A", + 6 + ], + [ + "ACaA", + "ABCAba", + 6 + ], + [ + "ACaA", + "acbCc", + 8 + ], + [ + "ACaA", + "cc", + 7 + ], + [ + "ACaAC", + "cABCBAc", + 7 + ], + [ + "ACaAcABa", + "cCAACC", + 10 + ], + [ + "ACaB", + "ABAbccB", + 9 + ], + [ + "ACaB", + "bAAAaAbAA", + 13 + ], + [ + "ACaBBBCB", + "AaAC", + 10 + ], + [ + "ACaBBaAa", + "AcaBcBbcB", + 9 + ], + [ + "ACaBCB", + "cc", + 10 + ], + [ + "ACaBCBb", + "b", + 12 + ], + [ + "ACaBacCc", + "bb", + 15 + ], + [ + "ACaBcb", + "BcCc", + 9 + ], + [ + "ACaCACc", + "c", + 12 + ], + [ + "ACaCBccBc", + "CcABCcBcb", + 9 + ], + [ + "ACaCCBA", + "AAcacC", + 8 + ], + [ + "ACaa", + "BAAc", + 7 + ], + [ + "ACaaBAbc", + "ACAccba", + 9 + ], + [ + "ACaabb", + "BCAb", + 7 + ], + [ + "ACaacBa", + "cBCBAc", + 11 + ], + [ + "ACabABBA", + "CAcCbB", + 10 + ], + [ + "ACabC", + "B", + 9 + ], + [ + "ACabac", + "BACcbCcbB", + 10 + ], + [ + "ACabbcA", + "aCacc", + 7 + ], + [ + "ACaccaa", + "AA", + 11 + ], + [ + "ACb", + "A", + 4 + ], + [ + "ACb", + "cBAab", + 6 + ], + [ + "ACb", + "ccbccbB", + 11 + ], + [ + "ACbA", + "AaBacA", + 7 + ], + [ + "ACbA", + "BaCCbA", + 5 + ], + [ + "ACbA", + "abACa", + 7 + ], + [ + "ACbACcbaB", + "ccc", + 14 + ], + [ + "ACbAbaCBC", + "acbaba", + 9 + ], + [ + "ACbB", + "aCACBAbb", + 9 + ], + [ + "ACbBA", + "CACB", + 6 + ], + [ + "ACbBCC", + "bcbCaAB", + 11 + ], + [ + "ACbBa", + "B", + 8 + ], + [ + "ACbBaCab", + "cbB", + 11 + ], + [ + "ACbCCbBb", + "aaca", + 14 + ], + [ + "ACbCaA", + "bAbC", + 8 + ], + [ + "ACbCb", + "c", + 9 + ], + [ + "ACbCc", + "ABc", + 5 + ], + [ + "ACbCcbA", + "accAAAaCc", + 15 + ], + [ + "ACba", + "acABAcbC", + 11 + ], + [ + "ACba", + "bCabba", + 6 + ], + [ + "ACbaAaA", + "BaaAbcC", + 12 + ], + [ + "ACbaAbB", + "AaBbbbb", + 8 + ], + [ + "ACbaAc", + "ABaBbCBcB", + 12 + ], + [ + "ACbaCAC", + "bBcaC", + 8 + ], + [ + "ACbaCaB", + "cbaA", + 8 + ], + [ + "ACbaaaB", + "CAcA", + 10 + ], + [ + "ACbabABB", + "BCBABBAc", + 11 + ], + [ + "ACbabcac", + "ABBA", + 11 + ], + [ + "ACbbBB", + "cAbbaC", + 8 + ], + [ + "ACbbBb", + "abAAbCab", + 10 + ], + [ + "ACbbCc", + "AccCbBAAB", + 11 + ], + [ + "ACbbca", + "cbABC", + 9 + ], + [ + "ACbbccB", + "b", + 12 + ], + [ + "ACbcBBc", + "CBacBbCbA", + 11 + ], + [ + "ACbcCba", + "abBBc", + 10 + ], + [ + "ACbcCccA", + "BBaA", + 13 + ], + [ + "ACc", + "AABbabcbb", + 14 + ], + [ + "ACc", + "ABbCcC", + 6 + ], + [ + "ACc", + "ABcBaB", + 8 + ], + [ + "ACc", + "a", + 5 + ], + [ + "ACc", + "b", + 6 + ], + [ + "ACc", + "baCBbbCbc", + 13 + ], + [ + "ACc", + "bcAcC", + 6 + ], + [ + "ACc", + "c", + 4 + ], + [ + "ACcA", + "CbcBBcbb", + 13 + ], + [ + "ACcA", + "aBBA", + 5 + ], + [ + "ACcAAb", + "ccAa", + 6 + ], + [ + "ACcABac", + "cA", + 10 + ], + [ + "ACcAc", + "baC", + 8 + ], + [ + "ACcBCBbbB", + "aA", + 17 + ], + [ + "ACcBa", + "ABBbBAbca", + 12 + ], + [ + "ACcBa", + "abbaca", + 9 + ], + [ + "ACcBba", + "baaAcAcA", + 12 + ], + [ + "ACcBbaa", + "CbCcCCBAC", + 12 + ], + [ + "ACcC", + "AcCABAC", + 8 + ], + [ + "ACcCACb", + "CABB", + 9 + ], + [ + "ACcCC", + "ccabBBC", + 11 + ], + [ + "ACcCa", + "Bcb", + 8 + ], + [ + "ACcCacAa", + "CbCba", + 10 + ], + [ + "ACcCc", + "bbBCC", + 7 + ], + [ + "ACca", + "cBBCBcbC", + 12 + ], + [ + "ACcaCAb", + "aaA", + 9 + ], + [ + "ACcaaAb", + "CaCAc", + 8 + ], + [ + "ACcabc", + "AAACb", + 8 + ], + [ + "ACcac", + "abBCBCc", + 9 + ], + [ + "ACcbB", + "caBaa", + 10 + ], + [ + "ACcbC", + "aCA", + 7 + ], + [ + "ACcbaBb", + "aCACCCAaA", + 12 + ], + [ + "ACcbbC", + "cacb", + 8 + ], + [ + "ACcbcCBB", + "A", + 14 + ], + [ + "ACcc", + "Cca", + 4 + ], + [ + "ACcca", + "accBbB", + 8 + ], + [ + "ACccabBC", + "Bb", + 14 + ], + [ + "ACccb", + "C", + 8 + ], + [ + "ACccbCccb", + "BbCacaA", + 14 + ], + [ + "ACccca", + "A", + 10 + ], + [ + "Aa", + "A", + 2 + ], + [ + "Aa", + "AAcac", + 6 + ], + [ + "Aa", + "ABBa", + 4 + ], + [ + "Aa", + "AbAabCcB", + 12 + ], + [ + "Aa", + "AbCCCCbcC", + 16 + ], + [ + "Aa", + "AbccAA", + 9 + ], + [ + "Aa", + "B", + 4 + ], + [ + "Aa", + "BA", + 3 + ], + [ + "Aa", + "BB", + 4 + ], + [ + "Aa", + "BBaccBAc", + 14 + ], + [ + "Aa", + "BCAaBb", + 8 + ], + [ + "Aa", + "BaBBAc", + 10 + ], + [ + "Aa", + "BaBcCaBc", + 13 + ], + [ + "Aa", + "BaCc", + 6 + ], + [ + "Aa", + "BabcCcAb", + 14 + ], + [ + "Aa", + "BbBB", + 8 + ], + [ + "Aa", + "C", + 4 + ], + [ + "Aa", + "CAabcaaAB", + 14 + ], + [ + "Aa", + "CAbbCbCB", + 14 + ], + [ + "Aa", + "CBBaBccb", + 14 + ], + [ + "Aa", + "CBCAccba", + 12 + ], + [ + "Aa", + "a", + 2 + ], + [ + "Aa", + "aAc", + 4 + ], + [ + "Aa", + "aacb", + 5 + ], + [ + "Aa", + "acAAacaB", + 12 + ], + [ + "Aa", + "acaA", + 5 + ], + [ + "Aa", + "acbc", + 7 + ], + [ + "Aa", + "b", + 4 + ], + [ + "Aa", + "bBA", + 5 + ], + [ + "Aa", + "bBABbc", + 10 + ], + [ + "Aa", + "baABcc", + 10 + ], + [ + "Aa", + "babbc", + 8 + ], + [ + "Aa", + "bbabBCac", + 13 + ], + [ + "Aa", + "cAAB", + 5 + ], + [ + "Aa", + "cC", + 4 + ], + [ + "Aa", + "caCAB", + 8 + ], + [ + "Aa", + "ccabB", + 8 + ], + [ + "AaA", + "Acbc", + 6 + ], + [ + "AaA", + "BBAcB", + 8 + ], + [ + "AaA", + "BaAbb", + 6 + ], + [ + "AaA", + "CAcBB", + 8 + ], + [ + "AaA", + "Cc", + 6 + ], + [ + "AaA", + "a", + 4 + ], + [ + "AaA", + "bAcaab", + 7 + ], + [ + "AaA", + "bCbCac", + 10 + ], + [ + "AaA", + "bCcaBAa", + 10 + ], + [ + "AaA", + "cAbaCAbB", + 10 + ], + [ + "AaAA", + "bcacBb", + 10 + ], + [ + "AaAABca", + "aACbAbaa", + 9 + ], + [ + "AaAAbA", + "CAbA", + 6 + ], + [ + "AaAAbBCcc", + "bbcAb", + 14 + ], + [ + "AaAAcbcbB", + "aAcCCBba", + 11 + ], + [ + "AaABbA", + "BBaACcACA", + 12 + ], + [ + "AaABc", + "cCcaBB", + 9 + ], + [ + "AaABcBAbb", + "BABAbaB", + 11 + ], + [ + "AaACCAb", + "C", + 12 + ], + [ + "AaACCaac", + "aabBbAbCA", + 13 + ], + [ + "AaACa", + "BBb", + 10 + ], + [ + "AaAaABB", + "CABcCCcCA", + 16 + ], + [ + "AaAaBbCc", + "B", + 14 + ], + [ + "AaAaa", + "cba", + 8 + ], + [ + "AaAabb", + "CACAA", + 9 + ], + [ + "AaAaccCB", + "bAaacBbaA", + 12 + ], + [ + "AaAb", + "cABAcb", + 6 + ], + [ + "AaAbAaAb", + "a", + 14 + ], + [ + "AaAbAbbB", + "aA", + 12 + ], + [ + "AaAbBba", + "CCBac", + 12 + ], + [ + "AaAbb", + "BbaB", + 8 + ], + [ + "AaAbbc", + "cAAcCC", + 8 + ], + [ + "AaAbc", + "a", + 8 + ], + [ + "AaAc", + "aAaCA", + 6 + ], + [ + "AaAcA", + "BCAAAba", + 8 + ], + [ + "AaAccaBbc", + "baAcAACaa", + 11 + ], + [ + "AaB", + "AbbAbb", + 8 + ], + [ + "AaB", + "Bca", + 6 + ], + [ + "AaB", + "bbaAb", + 7 + ], + [ + "AaB", + "bcABBBCc", + 12 + ], + [ + "AaB", + "caAb", + 5 + ], + [ + "AaBA", + "cBAB", + 6 + ], + [ + "AaBACC", + "BCAAbB", + 10 + ], + [ + "AaBACaBA", + "cAc", + 13 + ], + [ + "AaBAa", + "c", + 10 + ], + [ + "AaBAc", + "CcACba", + 10 + ], + [ + "AaBAcBcaa", + "cb", + 15 + ], + [ + "AaBBAC", + "ACAca", + 9 + ], + [ + "AaBBAcbcc", + "CbCcBb", + 14 + ], + [ + "AaBBbcA", + "A", + 12 + ], + [ + "AaBCcbCB", + "CCcAb", + 11 + ], + [ + "AaBa", + "abA", + 4 + ], + [ + "AaBa", + "bbbCabA", + 10 + ], + [ + "AaBaaaCBa", + "A", + 16 + ], + [ + "AaBabBCb", + "aBac", + 9 + ], + [ + "AaBbB", + "bBaa", + 8 + ], + [ + "AaBba", + "ab", + 6 + ], + [ + "AaBbba", + "caAbBAa", + 7 + ], + [ + "AaBbbaac", + "Cbcca", + 12 + ], + [ + "AaBbbbA", + "AabcBAa", + 7 + ], + [ + "AaBc", + "bAaaBaCCB", + 11 + ], + [ + "AaBcA", + "aCbCbB", + 9 + ], + [ + "AaBcAbBC", + "CBCACaBBA", + 12 + ], + [ + "AaBccbA", + "BBcaaccb", + 10 + ], + [ + "AaC", + "AACcabcAa", + 13 + ], + [ + "AaC", + "ABAaabA", + 10 + ], + [ + "AaC", + "Acc", + 3 + ], + [ + "AaC", + "aCCB", + 5 + ], + [ + "AaC", + "bbbbbbAA", + 15 + ], + [ + "AaCA", + "CCc", + 6 + ], + [ + "AaCAAAcac", + "ABB", + 16 + ], + [ + "AaCAAbaa", + "CCbABc", + 11 + ], + [ + "AaCACCa", + "BCcBACc", + 10 + ], + [ + "AaCACCbcc", + "CAacc", + 10 + ], + [ + "AaCAcAcB", + "bbA", + 14 + ], + [ + "AaCB", + "BbBC", + 8 + ], + [ + "AaCB", + "bCCbBAA", + 10 + ], + [ + "AaCBa", + "ACAbC", + 7 + ], + [ + "AaCBaC", + "Ba", + 8 + ], + [ + "AaCBaaAAC", + "CaBc", + 13 + ], + [ + "AaCBbBBa", + "CBbAcCBb", + 12 + ], + [ + "AaCCAAC", + "ACaBb", + 9 + ], + [ + "AaCCAbcBB", + "BCaab", + 13 + ], + [ + "AaCCCB", + "AaaAA", + 8 + ], + [ + "AaCCCBB", + "bB", + 11 + ], + [ + "AaCCcbB", + "BCcaCcB", + 9 + ], + [ + "AaCCcc", + "cbc", + 9 + ], + [ + "AaCaAAa", + "ccccBbcaC", + 15 + ], + [ + "AaCaCccb", + "aCB", + 11 + ], + [ + "AaCaaaA", + "a", + 12 + ], + [ + "AaCaac", + "CBabAA", + 10 + ], + [ + "AaCac", + "AbaBC", + 7 + ], + [ + "AaCbbbBaB", + "ccC", + 16 + ], + [ + "AaCbcCb", + "ccAAaB", + 12 + ], + [ + "AaCc", + "a", + 6 + ], + [ + "AaCcABAa", + "B", + 14 + ], + [ + "AaCcAbC", + "Bb", + 12 + ], + [ + "AaCcBBcc", + "cBb", + 11 + ], + [ + "AaCcaCBaA", + "ABca", + 12 + ], + [ + "AaCcab", + "bBcB", + 9 + ], + [ + "AaCcacA", + "BBBAcbC", + 13 + ], + [ + "AaCcbBbCa", + "BBBAaabCb", + 14 + ], + [ + "AaCcc", + "abcBAb", + 10 + ], + [ + "AaCccBBCb", + "cB", + 14 + ], + [ + "AaCccbC", + "baaaaAb", + 11 + ], + [ + "Aaa", + "BaAA", + 5 + ], + [ + "Aaa", + "CbabcBBB", + 14 + ], + [ + "Aaa", + "aAa", + 2 + ], + [ + "Aaa", + "aBB", + 5 + ], + [ + "Aaa", + "bAAbBaBcc", + 13 + ], + [ + "Aaa", + "ca", + 4 + ], + [ + "AaaA", + "aBabbB", + 9 + ], + [ + "AaaA", + "baCb", + 6 + ], + [ + "AaaAAC", + "a", + 10 + ], + [ + "AaaAAaC", + "b", + 14 + ], + [ + "AaaACAcbA", + "CBAcAC", + 12 + ], + [ + "AaaACa", + "BABcccC", + 11 + ], + [ + "AaaAbBAab", + "bacccCc", + 16 + ], + [ + "AaaAcBCB", + "Bbbb", + 14 + ], + [ + "AaaAcb", + "AcabAA", + 8 + ], + [ + "AaaB", + "aaCAC", + 6 + ], + [ + "AaaBABa", + "bBaC", + 11 + ], + [ + "AaaBCaCC", + "c", + 15 + ], + [ + "AaaBa", + "CBCBc", + 8 + ], + [ + "AaaC", + "aaAc", + 3 + ], + [ + "AaaC", + "b", + 8 + ], + [ + "AaaC", + "caaBCAABc", + 12 + ], + [ + "AaaCB", + "ABcbBCCA", + 12 + ], + [ + "AaaCcbcc", + "B", + 15 + ], + [ + "Aaaa", + "abCBCBa", + 11 + ], + [ + "AaaaA", + "CccAB", + 9 + ], + [ + "AaaaBcb", + "BCabBBBC", + 11 + ], + [ + "AaaaCCC", + "BBcaacaCB", + 11 + ], + [ + "AaaacbC", + "CcAcBACcb", + 13 + ], + [ + "AaabA", + "cB", + 9 + ], + [ + "AaabACBca", + "bAc", + 12 + ], + [ + "AaabCAC", + "bCaacCbB", + 10 + ], + [ + "Aaaba", + "BbccCaa", + 12 + ], + [ + "AaabaBba", + "aBaB", + 9 + ], + [ + "Aaababb", + "BcbBCcc", + 13 + ], + [ + "AaabcBb", + "abab", + 8 + ], + [ + "Aaac", + "BAbaab", + 6 + ], + [ + "AaacACBc", + "ACbbCaABA", + 12 + ], + [ + "AaaccCBcb", + "CBabaCACb", + 11 + ], + [ + "AaacccCc", + "c", + 14 + ], + [ + "Aab", + "BcBaaCBC", + 12 + ], + [ + "Aab", + "CBBaACcBA", + 15 + ], + [ + "Aab", + "a", + 4 + ], + [ + "AabA", + "acCbacA", + 9 + ], + [ + "AabAAcBBB", + "aa", + 15 + ], + [ + "AabAbBbcB", + "AcCcA", + 14 + ], + [ + "AabAccB", + "AcabBCb", + 8 + ], + [ + "AabBABc", + "AAabbCcCc", + 9 + ], + [ + "AabBCAaC", + "aBcCbc", + 10 + ], + [ + "AabBCC", + "A", + 10 + ], + [ + "AabBaBcBB", + "cbcc", + 14 + ], + [ + "AabBacAaa", + "aAc", + 13 + ], + [ + "AabBba", + "bcabb", + 8 + ], + [ + "AabBbc", + "CcAaA", + 12 + ], + [ + "AabBcBca", + "Ca", + 13 + ], + [ + "AabBcbc", + "ac", + 10 + ], + [ + "AabCbA", + "B", + 11 + ], + [ + "AabCbC", + "ccBbc", + 8 + ], + [ + "Aaba", + "BBaa", + 6 + ], + [ + "AabaACCC", + "aaBBcccCa", + 10 + ], + [ + "AabaCCccc", + "BaB", + 15 + ], + [ + "AabbA", + "AB", + 7 + ], + [ + "AabbABCBb", + "aBB", + 12 + ], + [ + "AabbB", + "AA", + 7 + ], + [ + "AabbB", + "bCCC", + 10 + ], + [ + "AabbbbbC", + "bBbBaB", + 10 + ], + [ + "AabbcCACc", + "AAa", + 14 + ], + [ + "Aabc", + "bBCbc", + 6 + ], + [ + "Aabc", + "bbA", + 6 + ], + [ + "AabcAAbCc", + "Aa", + 14 + ], + [ + "AabcBBb", + "bAbAcCBB", + 8 + ], + [ + "AabcBb", + "bb", + 8 + ], + [ + "AabcC", + "BbbBcbbC", + 10 + ], + [ + "AabcbCa", + "CB", + 12 + ], + [ + "Aac", + "Bccc", + 6 + ], + [ + "Aac", + "CBABCbaA", + 12 + ], + [ + "Aac", + "Cb", + 6 + ], + [ + "Aac", + "a", + 4 + ], + [ + "Aac", + "aBbCCaaa", + 13 + ], + [ + "Aac", + "baBAcA", + 8 + ], + [ + "Aac", + "c", + 4 + ], + [ + "Aac", + "caAABac", + 8 + ], + [ + "AacA", + "ACcBA", + 4 + ], + [ + "AacA", + "BaaAccBcA", + 11 + ], + [ + "AacAABbaa", + "a", + 16 + ], + [ + "AacABAB", + "CCc", + 12 + ], + [ + "AacAcbbC", + "cbCa", + 12 + ], + [ + "AacBBCc", + "BcBAA", + 10 + ], + [ + "AacBaA", + "AAa", + 7 + ], + [ + "AacBbaCBB", + "CcbC", + 12 + ], + [ + "AacBbcccc", + "CbACCaBab", + 18 + ], + [ + "AacC", + "BcBcAbb", + 12 + ], + [ + "AacCaBcaB", + "CAAb", + 13 + ], + [ + "AacCacC", + "CaABC", + 9 + ], + [ + "AacCcCcAC", + "CCBBabBA", + 16 + ], + [ + "AacCcc", + "Aab", + 8 + ], + [ + "AacaaCc", + "bAaA", + 10 + ], + [ + "AacacB", + "aaBBB", + 7 + ], + [ + "AacacBC", + "CBBcAcba", + 10 + ], + [ + "AacacBb", + "AAabbcaC", + 10 + ], + [ + "Aacb", + "c", + 6 + ], + [ + "AacbAAA", + "BbcBaCCCb", + 14 + ], + [ + "Aacbaa", + "aCBbbCaAc", + 12 + ], + [ + "Aacc", + "ACAA", + 6 + ], + [ + "Aacc", + "CBAcB", + 7 + ], + [ + "AaccAB", + "baAcAcbcc", + 11 + ], + [ + "AaccABc", + "bB", + 12 + ], + [ + "AaccAaAb", + "acb", + 10 + ], + [ + "AaccB", + "ac", + 6 + ], + [ + "AaccBcc", + "BC", + 11 + ], + [ + "AaccC", + "bAbacBAa", + 10 + ], + [ + "AaccaAc", + "Bc", + 12 + ], + [ + "AaccaBa", + "B", + 12 + ], + [ + "AaccaCcBb", + "bABCca", + 13 + ], + [ + "Aaccac", + "c", + 10 + ], + [ + "AacccAa", + "AC", + 11 + ], + [ + "AacccAcC", + "ab", + 14 + ], + [ + "AacccBBcC", + "Bb", + 15 + ], + [ + "Ab", + "A", + 2 + ], + [ + "Ab", + "AA", + 2 + ], + [ + "Ab", + "AaabCAAA", + 12 + ], + [ + "Ab", + "AbACAaCAA", + 14 + ], + [ + "Ab", + "AbCCAAACc", + 14 + ], + [ + "Ab", + "AbCbAa", + 8 + ], + [ + "Ab", + "Ac", + 2 + ], + [ + "Ab", + "AcaABbc", + 10 + ], + [ + "Ab", + "BAcCa", + 8 + ], + [ + "Ab", + "BBc", + 5 + ], + [ + "Ab", + "BbcAcCCaa", + 16 + ], + [ + "Ab", + "CABa", + 5 + ], + [ + "Ab", + "CAaB", + 5 + ], + [ + "Ab", + "CCBcAbbc", + 12 + ], + [ + "Ab", + "CaBc", + 6 + ], + [ + "Ab", + "CbBCAbaBc", + 14 + ], + [ + "Ab", + "aCAccA", + 10 + ], + [ + "Ab", + "aab", + 3 + ], + [ + "Ab", + "acaCBAa", + 12 + ], + [ + "Ab", + "bA", + 4 + ], + [ + "Ab", + "bCBc", + 7 + ], + [ + "Ab", + "c", + 4 + ], + [ + "Ab", + "cBBB", + 7 + ], + [ + "Ab", + "cC", + 4 + ], + [ + "Ab", + "cCAaBbbBC", + 14 + ], + [ + "Ab", + "cCaAcc", + 10 + ], + [ + "Ab", + "caA", + 5 + ], + [ + "Ab", + "caCacCcBC", + 16 + ], + [ + "Ab", + "cbcacAC", + 12 + ], + [ + "Ab", + "ccAbB", + 6 + ], + [ + "Ab", + "ccb", + 4 + ], + [ + "AbA", + "BCBAaC", + 9 + ], + [ + "AbA", + "BbBAaA", + 8 + ], + [ + "AbA", + "CcBa", + 6 + ], + [ + "AbA", + "aAC", + 5 + ], + [ + "AbA", + "b", + 4 + ], + [ + "AbA", + "caABB", + 7 + ], + [ + "AbA", + "ccc", + 6 + ], + [ + "AbAA", + "CBba", + 6 + ], + [ + "AbAAC", + "BAAC", + 3 + ], + [ + "AbAAC", + "bbab", + 7 + ], + [ + "AbAACaAb", + "C", + 14 + ], + [ + "AbAACbbBB", + "BCbaa", + 13 + ], + [ + "AbAAbC", + "caCaAAAbA", + 11 + ], + [ + "AbAAcbBa", + "BabBBCa", + 11 + ], + [ + "AbAB", + "Ab", + 4 + ], + [ + "AbAB", + "Bcb", + 6 + ], + [ + "AbAB", + "ac", + 7 + ], + [ + "AbABCa", + "cC", + 10 + ], + [ + "AbABa", + "BAaaCB", + 9 + ], + [ + "AbACABc", + "aBCACA", + 8 + ], + [ + "AbACBCA", + "aBB", + 10 + ], + [ + "AbACaa", + "AcAba", + 6 + ], + [ + "AbACbA", + "A", + 10 + ], + [ + "AbACbAA", + "aACA", + 7 + ], + [ + "AbACccccb", + "cCbbCBA", + 15 + ], + [ + "AbAaABB", + "BcaaA", + 9 + ], + [ + "AbAaAb", + "CB", + 11 + ], + [ + "AbAaC", + "AacbbACba", + 11 + ], + [ + "AbAaCAab", + "CabaacCaA", + 9 + ], + [ + "AbAaaa", + "AaC", + 8 + ], + [ + "AbAacaaac", + "abACABAc", + 8 + ], + [ + "AbAb", + "bbccBB", + 9 + ], + [ + "AbAbCba", + "BCbA", + 8 + ], + [ + "AbAbabac", + "bc", + 12 + ], + [ + "AbAbbBaab", + "bbabb", + 10 + ], + [ + "AbAc", + "BcbBCbC", + 11 + ], + [ + "AbAcACAa", + "cBcBBA", + 11 + ], + [ + "AbAcB", + "c", + 8 + ], + [ + "AbAcbCbca", + "AcBaaB", + 13 + ], + [ + "AbAccAbBc", + "aACcAACbA", + 11 + ], + [ + "AbAccBCa", + "BACCAaA", + 10 + ], + [ + "AbB", + "AABC", + 4 + ], + [ + "AbB", + "AbCbCCc", + 9 + ], + [ + "AbB", + "BcaAbAAAb", + 13 + ], + [ + "AbB", + "bBBA", + 5 + ], + [ + "AbB", + "cAaaaB", + 8 + ], + [ + "AbB", + "cbbABB", + 7 + ], + [ + "AbBAcCAC", + "CAAbcCC", + 9 + ], + [ + "AbBB", + "AAAAC", + 8 + ], + [ + "AbBBabc", + "Bcb", + 10 + ], + [ + "AbBBbCaA", + "AaCccAAA", + 11 + ], + [ + "AbBBcaAaa", + "bcbBbcCb", + 13 + ], + [ + "AbBCAacC", + "c", + 14 + ], + [ + "AbBCAcaa", + "aa", + 12 + ], + [ + "AbBCBbA", + "abbcBb", + 5 + ], + [ + "AbBaABC", + "AabAAbc", + 6 + ], + [ + "AbBaBB", + "AAaAca", + 9 + ], + [ + "AbBaCC", + "CAb", + 10 + ], + [ + "AbBaaAb", + "ACcBcca", + 11 + ], + [ + "AbBabBa", + "bccBAbb", + 10 + ], + [ + "AbBb", + "A", + 6 + ], + [ + "AbBb", + "BbaAB", + 7 + ], + [ + "AbBbA", + "babCBABbb", + 11 + ], + [ + "AbBbAABc", + "AbAa", + 9 + ], + [ + "AbBbCb", + "bcAACCcac", + 15 + ], + [ + "AbBbb", + "aCAC", + 9 + ], + [ + "AbBbbacbC", + "CcbccAb", + 14 + ], + [ + "AbBbcb", + "Ab", + 8 + ], + [ + "AbBbcbabC", + "Ba", + 14 + ], + [ + "AbBcBaaCc", + "BbcAB", + 13 + ], + [ + "AbBcaaCCA", + "aba", + 13 + ], + [ + "AbBcb", + "BAaAcb", + 6 + ], + [ + "AbBcc", + "AaccBc", + 6 + ], + [ + "AbBcccB", + "cB", + 10 + ], + [ + "AbC", + "BccBcC", + 9 + ], + [ + "AbC", + "C", + 4 + ], + [ + "AbCAA", + "CCcCcacBa", + 14 + ], + [ + "AbCABa", + "ACBC", + 6 + ], + [ + "AbCACa", + "BcaBacAA", + 12 + ], + [ + "AbCACcBbB", + "C", + 16 + ], + [ + "AbCAc", + "BbCbcBCAb", + 12 + ], + [ + "AbCB", + "Caa", + 8 + ], + [ + "AbCB", + "acbAc", + 7 + ], + [ + "AbCB", + "caaaba", + 10 + ], + [ + "AbCBbB", + "BacABACaB", + 13 + ], + [ + "AbCBbbC", + "cACb", + 10 + ], + [ + "AbCCcAbc", + "abB", + 12 + ], + [ + "AbCCca", + "C", + 10 + ], + [ + "AbCCcacba", + "aaAaabcb", + 13 + ], + [ + "AbCa", + "CcAaCAaA", + 10 + ], + [ + "AbCa", + "aBcccBBa", + 11 + ], + [ + "AbCaA", + "bBbAB", + 8 + ], + [ + "AbCaAAbab", + "BCbCcb", + 13 + ], + [ + "AbCaC", + "AbABC", + 4 + ], + [ + "AbCaabBCB", + "Cb", + 14 + ], + [ + "AbCabbc", + "BcCaB", + 9 + ], + [ + "AbCacAacb", + "aaAAAAC", + 12 + ], + [ + "AbCb", + "A", + 6 + ], + [ + "AbCb", + "BbaBcaCb", + 10 + ], + [ + "AbCbC", + "Cc", + 7 + ], + [ + "AbCbaB", + "aCac", + 7 + ], + [ + "AbCc", + "bAaCac", + 6 + ], + [ + "AbCcBBACC", + "cACC", + 10 + ], + [ + "AbCcbaBCA", + "cccCAbbCA", + 11 + ], + [ + "Aba", + "ACA", + 3 + ], + [ + "Aba", + "C", + 6 + ], + [ + "Aba", + "aB", + 4 + ], + [ + "Aba", + "bccccA", + 11 + ], + [ + "Aba", + "cabbcC", + 9 + ], + [ + "AbaAACBCb", + "CACabC", + 13 + ], + [ + "AbaAabca", + "CBCBCA", + 12 + ], + [ + "AbaAb", + "cb", + 8 + ], + [ + "AbaB", + "aCaB", + 3 + ], + [ + "AbaBAaCbC", + "cbbacCbA", + 10 + ], + [ + "AbaBAbaB", + "cbaC", + 12 + ], + [ + "AbaBBcbAA", + "bCaaC", + 14 + ], + [ + "AbaBCa", + "B", + 10 + ], + [ + "AbaBCbaB", + "acBcb", + 10 + ], + [ + "AbaBaca", + "acAAABBB", + 13 + ], + [ + "AbaBcCba", + "accBCABAB", + 12 + ], + [ + "AbaC", + "BAAb", + 7 + ], + [ + "AbaC", + "aA", + 6 + ], + [ + "AbaCb", + "B", + 9 + ], + [ + "AbaCbBc", + "ACBAaCAcA", + 11 + ], + [ + "Abaa", + "aAcBb", + 8 + ], + [ + "Abaa", + "cbbAaabCc", + 12 + ], + [ + "AbaaAbab", + "CCaaBc", + 11 + ], + [ + "AbaaAc", + "bAba", + 8 + ], + [ + "AbaaabCBb", + "aa", + 14 + ], + [ + "Abab", + "bbbBb", + 6 + ], + [ + "Abab", + "cab", + 4 + ], + [ + "AbabBBb", + "CaAAc", + 12 + ], + [ + "AbabCC", + "CACcAabA", + 12 + ], + [ + "AbabcCbb", + "AbcCcbB", + 7 + ], + [ + "AbacAa", + "bCAAabcCa", + 10 + ], + [ + "AbacB", + "CBaCBAcBa", + 11 + ], + [ + "AbacBCc", + "B", + 12 + ], + [ + "AbacBbBCC", + "bC", + 14 + ], + [ + "AbacaaCB", + "B", + 14 + ], + [ + "AbacaaaBC", + "AacAa", + 9 + ], + [ + "Abacb", + "AcccbAb", + 8 + ], + [ + "Abb", + "Aba", + 2 + ], + [ + "Abb", + "Ac", + 4 + ], + [ + "Abb", + "BCcba", + 8 + ], + [ + "AbbA", + "BCb", + 6 + ], + [ + "AbbABab", + "baacCBb", + 11 + ], + [ + "AbbACbab", + "ccAcA", + 12 + ], + [ + "AbbAcA", + "CabbA", + 7 + ], + [ + "AbbB", + "b", + 6 + ], + [ + "AbbBbCbAC", + "bCCaBA", + 13 + ], + [ + "AbbBbb", + "BBcAbaAbB", + 11 + ], + [ + "AbbBcaaBa", + "aAAAaCAba", + 13 + ], + [ + "AbbCACaB", + "CbBBAbcB", + 9 + ], + [ + "AbbCB", + "AAC", + 6 + ], + [ + "AbbCBbCA", + "aAa", + 14 + ], + [ + "AbbCCA", + "bBBcbaAcC", + 13 + ], + [ + "AbbCCaBCB", + "AabbCB", + 9 + ], + [ + "AbbCCcAAb", + "ABCbbBCC", + 13 + ], + [ + "AbbCaCCa", + "B", + 15 + ], + [ + "AbbCbBC", + "Cc", + 11 + ], + [ + "AbbCcA", + "BaaCcBac", + 11 + ], + [ + "AbbaBacBC", + "BaCaCBcC", + 10 + ], + [ + "Abbaa", + "aCbBbc", + 8 + ], + [ + "Abbb", + "bBab", + 5 + ], + [ + "Abbbba", + "BbbaA", + 6 + ], + [ + "AbbcBbA", + "cCbCcaC", + 11 + ], + [ + "Abc", + "CBAaABCCc", + 13 + ], + [ + "Abc", + "Cab", + 5 + ], + [ + "Abc", + "ba", + 4 + ], + [ + "Abc", + "cCBCa", + 8 + ], + [ + "AbcAAAa", + "CBcBBAabc", + 11 + ], + [ + "AbcABaa", + "cCcBCb", + 10 + ], + [ + "AbcAcA", + "B", + 11 + ], + [ + "AbcB", + "AbaCBBaA", + 9 + ], + [ + "AbcBAA", + "BAAb", + 8 + ], + [ + "AbcBBaBB", + "CCBACAA", + 13 + ], + [ + "AbcBCcAA", + "abbABCcCa", + 8 + ], + [ + "AbcCACAbB", + "aBcCb", + 10 + ], + [ + "AbcCAacab", + "a", + 16 + ], + [ + "AbcCa", + "cBAaab", + 9 + ], + [ + "AbcCbbc", + "ACccaBbAB", + 10 + ], + [ + "AbcaA", + "cac", + 6 + ], + [ + "AbcaBABac", + "aA", + 14 + ], + [ + "AbcaBbBcA", + "CBbA", + 11 + ], + [ + "AbcaCAAAB", + "ac", + 15 + ], + [ + "AbcacabA", + "ABbaAC", + 10 + ], + [ + "Abcb", + "AbCCcbb", + 6 + ], + [ + "AbcbA", + "b", + 8 + ], + [ + "AbcbABc", + "bCBCabbBb", + 12 + ], + [ + "AbcbB", + "A", + 8 + ], + [ + "AbcbbC", + "bABbBAa", + 10 + ], + [ + "Abcc", + "bAABaA", + 9 + ], + [ + "Abcc", + "cAABAAa", + 11 + ], + [ + "AbccB", + "cA", + 8 + ], + [ + "Abccc", + "bba", + 8 + ], + [ + "Ac", + "ACbAa", + 7 + ], + [ + "Ac", + "AbB", + 4 + ], + [ + "Ac", + "BCB", + 5 + ], + [ + "Ac", + "BCacCBaBb", + 15 + ], + [ + "Ac", + "BaaCB", + 8 + ], + [ + "Ac", + "BacBbABCa", + 15 + ], + [ + "Ac", + "Bacc", + 5 + ], + [ + "Ac", + "BbaBaACcC", + 14 + ], + [ + "Ac", + "BccaAa", + 10 + ], + [ + "Ac", + "CA", + 4 + ], + [ + "Ac", + "CABacA", + 8 + ], + [ + "Ac", + "CABbbCA", + 11 + ], + [ + "Ac", + "CAa", + 4 + ], + [ + "Ac", + "CBCaAcBaB", + 14 + ], + [ + "Ac", + "CCA", + 5 + ], + [ + "Ac", + "CCcbBBc", + 12 + ], + [ + "Ac", + "CbBBBBbBA", + 18 + ], + [ + "Ac", + "Cbba", + 8 + ], + [ + "Ac", + "aCaa", + 6 + ], + [ + "Ac", + "aCbca", + 7 + ], + [ + "Ac", + "aaBC", + 6 + ], + [ + "Ac", + "aaCAa", + 8 + ], + [ + "Ac", + "aaaA", + 7 + ], + [ + "Ac", + "aabca", + 7 + ], + [ + "Ac", + "abcAAaB", + 11 + ], + [ + "Ac", + "acaBBbaAC", + 15 + ], + [ + "Ac", + "bAaa", + 6 + ], + [ + "Ac", + "bCCCCC", + 11 + ], + [ + "Ac", + "baAcB", + 6 + ], + [ + "Ac", + "bc", + 2 + ], + [ + "Ac", + "cB", + 4 + ], + [ + "Ac", + "cBaaaAAAb", + 16 + ], + [ + "Ac", + "cC", + 3 + ], + [ + "Ac", + "cCAcbCA", + 10 + ], + [ + "Ac", + "cCcCAAA", + 12 + ], + [ + "Ac", + "cbAAcc", + 8 + ], + [ + "AcA", + "ABAACCbc", + 12 + ], + [ + "AcA", + "BBBb", + 8 + ], + [ + "AcA", + "CcabCab", + 11 + ], + [ + "AcA", + "acA", + 1 + ], + [ + "AcA", + "bAbCc", + 7 + ], + [ + "AcA", + "bccBB", + 8 + ], + [ + "AcA", + "cCCA", + 5 + ], + [ + "AcAAAb", + "Acba", + 7 + ], + [ + "AcAABAc", + "bCaa", + 11 + ], + [ + "AcAABBc", + "c", + 12 + ], + [ + "AcAACA", + "cCb", + 8 + ], + [ + "AcAACBb", + "AbaA", + 9 + ], + [ + "AcAAbb", + "abBbA", + 9 + ], + [ + "AcAAcBaB", + "caB", + 10 + ], + [ + "AcABCCba", + "Ca", + 12 + ], + [ + "AcABa", + "aAbBBCA", + 9 + ], + [ + "AcAC", + "c", + 6 + ], + [ + "AcACB", + "aBBCbBabC", + 13 + ], + [ + "AcACCBA", + "c", + 12 + ], + [ + "AcACCaACA", + "aCAAAcABC", + 12 + ], + [ + "AcACaA", + "Ac", + 8 + ], + [ + "AcACcC", + "bABBAcC", + 8 + ], + [ + "AcACccA", + "CaacccB", + 8 + ], + [ + "AcAa", + "b", + 8 + ], + [ + "AcAaaCBA", + "abB", + 12 + ], + [ + "AcAac", + "BBCCC", + 9 + ], + [ + "AcAb", + "cCBBcBCAc", + 14 + ], + [ + "AcAbA", + "cCbbBcb", + 11 + ], + [ + "AcAbBabb", + "BBCBa", + 12 + ], + [ + "AcAbBcABC", + "baCcCcb", + 15 + ], + [ + "AcAbCB", + "ccCCaC", + 10 + ], + [ + "AcAba", + "CcBcBcC", + 11 + ], + [ + "AcAbb", + "bAAbc", + 6 + ], + [ + "AcAcB", + "aAcBAbC", + 8 + ], + [ + "AcAcCB", + "bc", + 10 + ], + [ + "AcAcCCCbb", + "BAaBC", + 14 + ], + [ + "AcAcbacA", + "cb", + 12 + ], + [ + "AcB", + "AbCbCa", + 8 + ], + [ + "AcB", + "CCABca", + 8 + ], + [ + "AcB", + "aBA", + 5 + ], + [ + "AcB", + "b", + 5 + ], + [ + "AcB", + "bCB", + 3 + ], + [ + "AcB", + "cBBaB", + 8 + ], + [ + "AcB", + "caBcC", + 7 + ], + [ + "AcB", + "caCCCaA", + 12 + ], + [ + "AcBAAa", + "ac", + 9 + ], + [ + "AcBAbbAA", + "ACCB", + 12 + ], + [ + "AcBB", + "AAAccBcA", + 10 + ], + [ + "AcBB", + "BaccCCACc", + 15 + ], + [ + "AcBB", + "bBCA", + 8 + ], + [ + "AcBB", + "cCAaaBaBb", + 12 + ], + [ + "AcBBAC", + "AaCA", + 8 + ], + [ + "AcBBACA", + "aAA", + 9 + ], + [ + "AcBBACCaC", + "abBcCAC", + 8 + ], + [ + "AcBBaB", + "aC", + 10 + ], + [ + "AcBBbbaBc", + "CC", + 16 + ], + [ + "AcBBbcb", + "ABBAAc", + 8 + ], + [ + "AcBBcabc", + "a", + 14 + ], + [ + "AcBC", + "ACabcCbBB", + 12 + ], + [ + "AcBC", + "AacAcBCcA", + 10 + ], + [ + "AcBC", + "bCbcAbbA", + 13 + ], + [ + "AcBCAcb", + "AB", + 10 + ], + [ + "AcBCCaAbc", + "ca", + 14 + ], + [ + "AcBCCb", + "aCcCAccc", + 11 + ], + [ + "AcBCaccAA", + "cBAcc", + 9 + ], + [ + "AcBCb", + "aBbaBac", + 11 + ], + [ + "AcBCcbCb", + "AAABCa", + 11 + ], + [ + "AcBCcbbaB", + "bBbC", + 14 + ], + [ + "AcBa", + "BBbCBAA", + 10 + ], + [ + "AcBaCCbCa", + "bbBBb", + 14 + ], + [ + "AcBaCCc", + "aaCcca", + 8 + ], + [ + "AcBaacCb", + "BBbbCbcC", + 13 + ], + [ + "AcBbAb", + "BabcbBbc", + 11 + ], + [ + "AcBbAc", + "cbAbC", + 7 + ], + [ + "AcBbaac", + "bA", + 11 + ], + [ + "AcBbbbB", + "ACaa", + 11 + ], + [ + "AcBc", + "abAcbb", + 7 + ], + [ + "AcBcAcA", + "aaCBAaAAa", + 11 + ], + [ + "AcBcBBB", + "a", + 13 + ], + [ + "AcBcCba", + "BCb", + 8 + ], + [ + "AcBca", + "Ab", + 7 + ], + [ + "AcBcbbA", + "ABb", + 8 + ], + [ + "AcBcbbA", + "BB", + 11 + ], + [ + "AcBcbcA", + "B", + 12 + ], + [ + "AcBccbbbC", + "CbBAAbB", + 13 + ], + [ + "AcBcccC", + "b", + 13 + ], + [ + "AcC", + "ACAbc", + 6 + ], + [ + "AcC", + "bBBA", + 8 + ], + [ + "AcC", + "bbCBACbcA", + 14 + ], + [ + "AcC", + "c", + 4 + ], + [ + "AcC", + "cBBa", + 8 + ], + [ + "AcCACC", + "bbAcCCcc", + 8 + ], + [ + "AcCACaCA", + "CBcCCB", + 11 + ], + [ + "AcCAb", + "bCBAACBC", + 12 + ], + [ + "AcCAbA", + "Bac", + 11 + ], + [ + "AcCAbcaBb", + "Cb", + 14 + ], + [ + "AcCAcCCCA", + "AcCC", + 10 + ], + [ + "AcCBA", + "a", + 9 + ], + [ + "AcCBBaBb", + "AC", + 12 + ], + [ + "AcCBba", + "CaaBCAa", + 10 + ], + [ + "AcCCAA", + "BcA", + 8 + ], + [ + "AcCCBcc", + "bbBACCa", + 13 + ], + [ + "AcCCCbb", + "AcaAc", + 9 + ], + [ + "AcCCb", + "bb", + 8 + ], + [ + "AcCCba", + "BC", + 10 + ], + [ + "AcCCcBACB", + "cb", + 15 + ], + [ + "AcCCccCB", + "baaCaAaB", + 12 + ], + [ + "AcCa", + "AAaCcC", + 8 + ], + [ + "AcCaAcccA", + "AaaaBaCBB", + 13 + ], + [ + "AcCaBCACC", + "a", + 16 + ], + [ + "AcCaC", + "CAcC", + 6 + ], + [ + "AcCaa", + "Ccab", + 6 + ], + [ + "AcCabb", + "BbCBa", + 9 + ], + [ + "AcCb", + "A", + 6 + ], + [ + "AcCbA", + "BaCcaC", + 9 + ], + [ + "AcCbA", + "aA", + 7 + ], + [ + "AcCbBb", + "CCAcb", + 7 + ], + [ + "AcCba", + "cBBbCbCc", + 12 + ], + [ + "AcCbac", + "cCBa", + 5 + ], + [ + "AcCbbbc", + "BbBbABCBc", + 12 + ], + [ + "AcCbc", + "ABBaaAc", + 10 + ], + [ + "AcCc", + "BBBAA", + 10 + ], + [ + "AcCc", + "CABBBCaCb", + 13 + ], + [ + "AcCc", + "acCbACCcc", + 11 + ], + [ + "AcCcA", + "AbCbabBc", + 11 + ], + [ + "AcCcABb", + "cCBaAc", + 9 + ], + [ + "AcCcBB", + "AAaCAaA", + 10 + ], + [ + "AcCcabBCB", + "BcA", + 15 + ], + [ + "AcCcb", + "bC", + 8 + ], + [ + "AcCccABB", + "bbCbbCB", + 12 + ], + [ + "AcCccbBBb", + "ACABAa", + 12 + ], + [ + "Aca", + "BaAA", + 6 + ], + [ + "Aca", + "CCBCCB", + 11 + ], + [ + "Aca", + "cA", + 3 + ], + [ + "AcaAAA", + "CcbbaB", + 9 + ], + [ + "AcaABAaCb", + "CcCACC", + 12 + ], + [ + "AcaABCa", + "bacAcA", + 9 + ], + [ + "AcaAcBC", + "ccbAaa", + 10 + ], + [ + "AcaBACCb", + "ACacBaBAb", + 8 + ], + [ + "AcaBAabCb", + "ACACAbaCB", + 9 + ], + [ + "AcaBC", + "ABacC", + 4 + ], + [ + "AcaBCa", + "AbBC", + 6 + ], + [ + "AcaBacc", + "CBB", + 11 + ], + [ + "AcaBcba", + "AbAbCBcC", + 10 + ], + [ + "AcaC", + "Aca", + 2 + ], + [ + "AcaCB", + "C", + 8 + ], + [ + "AcaCBB", + "cbacccC", + 11 + ], + [ + "AcaCBb", + "CCcaBC", + 8 + ], + [ + "AcaCa", + "bCA", + 7 + ], + [ + "AcaCaC", + "BB", + 12 + ], + [ + "AcaCbcC", + "aBBCccaC", + 9 + ], + [ + "AcaCc", + "CaaCBBb", + 10 + ], + [ + "AcaCc", + "aBaCb", + 5 + ], + [ + "AcaCcAc", + "acA", + 8 + ], + [ + "Acaa", + "BACcCA", + 7 + ], + [ + "AcaaA", + "bacbCca", + 10 + ], + [ + "Acaaa", + "ACccca", + 6 + ], + [ + "AcaaaaaBA", + "b", + 17 + ], + [ + "AcaaabbAC", + "AACbc", + 12 + ], + [ + "AcaabBaAc", + "aCAbBBCaA", + 10 + ], + [ + "AcaacaAAB", + "CA", + 15 + ], + [ + "AcaacbaC", + "A", + 14 + ], + [ + "Acab", + "AB", + 5 + ], + [ + "AcabA", + "BCcca", + 8 + ], + [ + "AcabBAcB", + "B", + 14 + ], + [ + "AcabCaab", + "cCCaBB", + 9 + ], + [ + "AcabcC", + "AC", + 8 + ], + [ + "Acabca", + "cCCC", + 9 + ], + [ + "AcacaAbbb", + "abB", + 13 + ], + [ + "AcacaBC", + "bbaCB", + 9 + ], + [ + "AcacaCCCc", + "aBbbcACA", + 14 + ], + [ + "AcaccB", + "aaBc", + 7 + ], + [ + "Acb", + "BcbA", + 4 + ], + [ + "Acb", + "BccCaB", + 9 + ], + [ + "Acb", + "aC", + 4 + ], + [ + "Acb", + "bAccAa", + 8 + ], + [ + "Acb", + "ccAcbaB", + 8 + ], + [ + "AcbA", + "cA", + 4 + ], + [ + "AcbAAcaA", + "Cba", + 11 + ], + [ + "AcbAB", + "cbBbAa", + 8 + ], + [ + "AcbACBAaA", + "cc", + 15 + ], + [ + "AcbB", + "C", + 7 + ], + [ + "AcbBa", + "aAABbACcB", + 13 + ], + [ + "AcbBaCb", + "bCcacBbA", + 12 + ], + [ + "AcbBaba", + "CbbCBaB", + 9 + ], + [ + "AcbBb", + "abBbA", + 5 + ], + [ + "AcbC", + "bbCaA", + 8 + ], + [ + "AcbCAC", + "CcACABBb", + 10 + ], + [ + "AcbCAc", + "aaBCaAbAa", + 12 + ], + [ + "AcbCBcc", + "aA", + 13 + ], + [ + "AcbCaCb", + "ccAABC", + 10 + ], + [ + "AcbCcBCa", + "bbbcAcB", + 11 + ], + [ + "AcbCcbCa", + "CcaABBcCa", + 11 + ], + [ + "Acba", + "aACbbBcc", + 11 + ], + [ + "AcbaCAA", + "bAaBbaca", + 10 + ], + [ + "Acbab", + "acBc", + 6 + ], + [ + "AcbbAcCa", + "cCb", + 12 + ], + [ + "AcbbC", + "B", + 9 + ], + [ + "Acbbb", + "cCbCAACA", + 13 + ], + [ + "AcbbcAB", + "cca", + 9 + ], + [ + "Acbc", + "AbCaCB", + 8 + ], + [ + "Acbc", + "Bac", + 6 + ], + [ + "AcbcBB", + "ABBBB", + 5 + ], + [ + "AcbcBcA", + "AaaBaba", + 11 + ], + [ + "AcbcCbcbA", + "ac", + 15 + ], + [ + "Acc", + "BbBCABBb", + 14 + ], + [ + "Acc", + "aAbBbBca", + 12 + ], + [ + "Acc", + "aa", + 5 + ], + [ + "Acc", + "aaBcC", + 6 + ], + [ + "Acc", + "bCbAaCab", + 13 + ], + [ + "AccA", + "BCBA", + 5 + ], + [ + "AccA", + "aC", + 6 + ], + [ + "AccAB", + "Bcb", + 7 + ], + [ + "AccABcbBC", + "AAcBcAcc", + 9 + ], + [ + "AccACBbA", + "c", + 14 + ], + [ + "AccAbcC", + "cbbB", + 10 + ], + [ + "AccBbab", + "bcAaaaA", + 10 + ], + [ + "AccC", + "BBbca", + 8 + ], + [ + "AccCBBACa", + "b", + 17 + ], + [ + "AccCCCcaa", + "aabC", + 15 + ], + [ + "AccCcAA", + "CCbAaABCA", + 13 + ], + [ + "Acca", + "C", + 7 + ], + [ + "Acca", + "aB", + 7 + ], + [ + "AccaCABA", + "CBBBc", + 13 + ], + [ + "AccaaAbbC", + "BaAB", + 13 + ], + [ + "Accb", + "baBaaCaCa", + 15 + ], + [ + "Accbbbb", + "bA", + 12 + ], + [ + "Accbcc", + "aaAAcb", + 9 + ], + [ + "Accc", + "ABbAB", + 8 + ], + [ + "AcccAC", + "BAAbCacBb", + 13 + ], + [ + "AcccAbbB", + "BaBCa", + 14 + ], + [ + "AcccBAAbC", + "CCbCb", + 13 + ], + [ + "AcccCACac", + "BAbccaC", + 11 + ], + [ + "AcccCabab", + "bBAAaBaA", + 13 + ], + [ + "AcccCb", + "BACacbcC", + 9 + ], + [ + "AcccaCb", + "CBBCcaCB", + 8 + ], + [ + "Acccac", + "acCaAcaCb", + 9 + ], + [ + "B", + "A", + 2 + ], + [ + "B", + "AAABB", + 8 + ], + [ + "B", + "AAAcc", + 10 + ], + [ + "B", + "AABBcC", + 10 + ], + [ + "B", + "AABCBb", + 10 + ], + [ + "B", + "AAC", + 6 + ], + [ + "B", + "AACA", + 8 + ], + [ + "B", + "AACaaaa", + 14 + ], + [ + "B", + "AAcaBa", + 10 + ], + [ + "B", + "AAcacbab", + 15 + ], + [ + "B", + "ABBc", + 6 + ], + [ + "B", + "ABCBcBCB", + 14 + ], + [ + "B", + "ABaAba", + 10 + ], + [ + "B", + "ABaB", + 6 + ], + [ + "B", + "ABaBba", + 10 + ], + [ + "B", + "ABaaCcBa", + 14 + ], + [ + "B", + "ABc", + 4 + ], + [ + "B", + "ACB", + 4 + ], + [ + "B", + "ACCCCCBc", + 14 + ], + [ + "B", + "ACbbC", + 9 + ], + [ + "B", + "ACbbCA", + 11 + ], + [ + "B", + "AaA", + 6 + ], + [ + "B", + "AaACbbaaB", + 16 + ], + [ + "B", + "AaAaCCbaa", + 17 + ], + [ + "B", + "AaBaBcba", + 14 + ], + [ + "B", + "AabcAaa", + 13 + ], + [ + "B", + "AacCacAb", + 15 + ], + [ + "B", + "AaccbBacB", + 16 + ], + [ + "B", + "Ab", + 3 + ], + [ + "B", + "AbBc", + 6 + ], + [ + "B", + "AbaBCcaBa", + 16 + ], + [ + "B", + "Abc", + 5 + ], + [ + "B", + "AcA", + 6 + ], + [ + "B", + "AcBA", + 6 + ], + [ + "B", + "AcBaBbCc", + 14 + ], + [ + "B", + "AcCc", + 8 + ], + [ + "B", + "AcaC", + 8 + ], + [ + "B", + "AccBacc", + 12 + ], + [ + "B", + "B", + 0 + ], + [ + "B", + "BAA", + 4 + ], + [ + "B", + "BBAbCc", + 10 + ], + [ + "B", + "BBAccA", + 10 + ], + [ + "B", + "BBBCb", + 8 + ], + [ + "B", + "BBa", + 4 + ], + [ + "B", + "BBaAcacab", + 16 + ], + [ + "B", + "BBc", + 4 + ], + [ + "B", + "BC", + 2 + ], + [ + "B", + "BCC", + 4 + ], + [ + "B", + "BCCbaBA", + 12 + ], + [ + "B", + "BCbCb", + 8 + ], + [ + "B", + "BCcC", + 6 + ], + [ + "B", + "BCcCccC", + 12 + ], + [ + "B", + "BCcb", + 6 + ], + [ + "B", + "BCcc", + 6 + ], + [ + "B", + "Ba", + 2 + ], + [ + "B", + "BaBCAaCAa", + 16 + ], + [ + "B", + "Baca", + 6 + ], + [ + "B", + "BacbcaCaC", + 16 + ], + [ + "B", + "BbcCCbAbb", + 16 + ], + [ + "B", + "BcACbAAC", + 14 + ], + [ + "B", + "BcACbbbc", + 14 + ], + [ + "B", + "BcabCBbab", + 16 + ], + [ + "B", + "C", + 2 + ], + [ + "B", + "CAAb", + 7 + ], + [ + "B", + "CAAba", + 9 + ], + [ + "B", + "CACcA", + 10 + ], + [ + "B", + "CB", + 2 + ], + [ + "B", + "CBCcAcbb", + 14 + ], + [ + "B", + "CBc", + 4 + ], + [ + "B", + "CBcA", + 6 + ], + [ + "B", + "CBcCC", + 8 + ], + [ + "B", + "CCaaCaac", + 16 + ], + [ + "B", + "CCcBBCb", + 12 + ], + [ + "B", + "Ca", + 4 + ], + [ + "B", + "CaCC", + 8 + ], + [ + "B", + "Caa", + 6 + ], + [ + "B", + "Cab", + 5 + ], + [ + "B", + "Cacbc", + 9 + ], + [ + "B", + "Cb", + 3 + ], + [ + "B", + "Cbc", + 5 + ], + [ + "B", + "CbcaAaACC", + 17 + ], + [ + "B", + "CbccCbca", + 15 + ], + [ + "B", + "Cc", + 4 + ], + [ + "B", + "CcAacCAA", + 16 + ], + [ + "B", + "CcBb", + 6 + ], + [ + "B", + "CcbBb", + 8 + ], + [ + "B", + "Ccc", + 6 + ], + [ + "B", + "a", + 2 + ], + [ + "B", + "aAA", + 6 + ], + [ + "B", + "aAACAb", + 11 + ], + [ + "B", + "aABCB", + 8 + ], + [ + "B", + "aACaCCBbA", + 16 + ], + [ + "B", + "aB", + 2 + ], + [ + "B", + "aBBbc", + 8 + ], + [ + "B", + "aBabc", + 8 + ], + [ + "B", + "aBcbCA", + 10 + ], + [ + "B", + "aBccCCBC", + 14 + ], + [ + "B", + "aC", + 4 + ], + [ + "B", + "aCBBAAc", + 12 + ], + [ + "B", + "aCBcA", + 8 + ], + [ + "B", + "aCCaAabB", + 14 + ], + [ + "B", + "aCCcbbac", + 15 + ], + [ + "B", + "aCa", + 6 + ], + [ + "B", + "aCaBABACa", + 16 + ], + [ + "B", + "aCcbCA", + 11 + ], + [ + "B", + "aaBBac", + 10 + ], + [ + "B", + "aaBCC", + 8 + ], + [ + "B", + "aab", + 5 + ], + [ + "B", + "aabBcbBa", + 14 + ], + [ + "B", + "aacbAB", + 10 + ], + [ + "B", + "abA", + 5 + ], + [ + "B", + "abAAA", + 9 + ], + [ + "B", + "abBBCBBcb", + 16 + ], + [ + "B", + "abC", + 5 + ], + [ + "B", + "abCcacAC", + 15 + ], + [ + "B", + "ac", + 4 + ], + [ + "B", + "acAbAB", + 10 + ], + [ + "B", + "acBcBba", + 12 + ], + [ + "B", + "b", + 1 + ], + [ + "B", + "bA", + 3 + ], + [ + "B", + "bAAbcbb", + 13 + ], + [ + "B", + "bABAacc", + 12 + ], + [ + "B", + "bACA", + 7 + ], + [ + "B", + "bAaAbBbcb", + 16 + ], + [ + "B", + "bAc", + 5 + ], + [ + "B", + "bB", + 2 + ], + [ + "B", + "bC", + 3 + ], + [ + "B", + "bCAbccaCA", + 17 + ], + [ + "B", + "bCCA", + 7 + ], + [ + "B", + "bCabCB", + 10 + ], + [ + "B", + "bCabcCa", + 13 + ], + [ + "B", + "ba", + 3 + ], + [ + "B", + "baba", + 7 + ], + [ + "B", + "babaa", + 9 + ], + [ + "B", + "bb", + 3 + ], + [ + "B", + "bbc", + 5 + ], + [ + "B", + "bbcbCCCb", + 15 + ], + [ + "B", + "bc", + 3 + ], + [ + "B", + "bcCBCacb", + 14 + ], + [ + "B", + "bcaAcbbB", + 14 + ], + [ + "B", + "c", + 2 + ], + [ + "B", + "cABAAb", + 10 + ], + [ + "B", + "cAaaACc", + 14 + ], + [ + "B", + "cAcCA", + 10 + ], + [ + "B", + "cAccC", + 10 + ], + [ + "B", + "cB", + 2 + ], + [ + "B", + "cBBBa", + 8 + ], + [ + "B", + "cBaCCba", + 12 + ], + [ + "B", + "cBbbaAaBC", + 16 + ], + [ + "B", + "cBcCaA", + 10 + ], + [ + "B", + "cC", + 4 + ], + [ + "B", + "cCABB", + 8 + ], + [ + "B", + "cCBAacA", + 12 + ], + [ + "B", + "cCbBBAccB", + 16 + ], + [ + "B", + "cCbcaBaca", + 16 + ], + [ + "B", + "cCcBaC", + 10 + ], + [ + "B", + "cCcbcaCcc", + 17 + ], + [ + "B", + "cabBca", + 10 + ], + [ + "B", + "cabccaAAB", + 16 + ], + [ + "B", + "cb", + 3 + ], + [ + "B", + "cbAACcCCb", + 17 + ], + [ + "B", + "cbAC", + 7 + ], + [ + "B", + "cbBbCC", + 10 + ], + [ + "B", + "cbC", + 5 + ], + [ + "B", + "cbCaba", + 11 + ], + [ + "B", + "cbCbBBaa", + 14 + ], + [ + "B", + "cbaaAAA", + 13 + ], + [ + "B", + "cbaaCAb", + 13 + ], + [ + "B", + "cbbCaccA", + 15 + ], + [ + "B", + "cbbb", + 7 + ], + [ + "B", + "cbcCa", + 9 + ], + [ + "B", + "ccABaB", + 10 + ], + [ + "B", + "ccAc", + 8 + ], + [ + "B", + "ccCbCbc", + 13 + ], + [ + "B", + "ccabaC", + 11 + ], + [ + "B", + "ccb", + 5 + ], + [ + "BA", + "A", + 2 + ], + [ + "BA", + "ABaabAbAc", + 14 + ], + [ + "BA", + "B", + 2 + ], + [ + "BA", + "BBABbCAaB", + 14 + ], + [ + "BA", + "BBcaC", + 7 + ], + [ + "BA", + "BBcacAbcb", + 14 + ], + [ + "BA", + "BCbCbbcAB", + 14 + ], + [ + "BA", + "CACCAaac", + 14 + ], + [ + "BA", + "CAaaacb", + 12 + ], + [ + "BA", + "CBC", + 4 + ], + [ + "BA", + "aACbbcba", + 14 + ], + [ + "BA", + "ab", + 4 + ], + [ + "BA", + "abCAa", + 7 + ], + [ + "BA", + "b", + 3 + ], + [ + "BA", + "bABaAca", + 10 + ], + [ + "BA", + "baCCBbC", + 12 + ], + [ + "BA", + "bbBcB", + 8 + ], + [ + "BA", + "bbbCC", + 9 + ], + [ + "BA", + "bbbaBA", + 8 + ], + [ + "BA", + "bbbbCBb", + 12 + ], + [ + "BA", + "cBABaC", + 8 + ], + [ + "BA", + "cBBbbA", + 8 + ], + [ + "BA", + "cC", + 4 + ], + [ + "BA", + "ccB", + 6 + ], + [ + "BA", + "ccCa", + 7 + ], + [ + "BAA", + "A", + 4 + ], + [ + "BAA", + "abA", + 4 + ], + [ + "BAA", + "acCAc", + 8 + ], + [ + "BAA", + "bB", + 5 + ], + [ + "BAA", + "bbcACCCAB", + 13 + ], + [ + "BAA", + "bcBC", + 7 + ], + [ + "BAA", + "cBaCaBc", + 10 + ], + [ + "BAAA", + "B", + 6 + ], + [ + "BAAA", + "BBCacaC", + 10 + ], + [ + "BAAA", + "bBAc", + 5 + ], + [ + "BAAAAAA", + "CbcbAc", + 12 + ], + [ + "BAAAACb", + "cbcACabBc", + 13 + ], + [ + "BAAAC", + "cbCBcbCC", + 12 + ], + [ + "BAAAaCCAB", + "bcCB", + 12 + ], + [ + "BAAAabC", + "CCaaaAcc", + 11 + ], + [ + "BAAAbcBa", + "aCcAAAcc", + 12 + ], + [ + "BAABC", + "bccab", + 9 + ], + [ + "BAABaC", + "Cbb", + 11 + ], + [ + "BAABabAb", + "ACC", + 14 + ], + [ + "BAAC", + "bB", + 7 + ], + [ + "BAAC", + "ccaBab", + 10 + ], + [ + "BAAC", + "ccbC", + 6 + ], + [ + "BAACAA", + "a", + 11 + ], + [ + "BAACaC", + "Cac", + 7 + ], + [ + "BAAaA", + "a", + 8 + ], + [ + "BAAaACaC", + "bb", + 15 + ], + [ + "BAAaB", + "ccAcbA", + 9 + ], + [ + "BAAaa", + "b", + 9 + ], + [ + "BAAaa", + "cacCaBc", + 11 + ], + [ + "BAAaaac", + "c", + 12 + ], + [ + "BAAaabB", + "aaAAA", + 9 + ], + [ + "BAAaac", + "BCaCcCAB", + 12 + ], + [ + "BAAacA", + "ccBAcCAc", + 11 + ], + [ + "BAAb", + "AA", + 4 + ], + [ + "BAAbAC", + "BbAccCba", + 10 + ], + [ + "BAAbCbbC", + "CCCBBc", + 11 + ], + [ + "BAAbacbC", + "aBAabc", + 9 + ], + [ + "BAAbbACC", + "CCABb", + 11 + ], + [ + "BAAcCBa", + "ACcA", + 9 + ], + [ + "BAAcaBBCa", + "AccB", + 12 + ], + [ + "BAAcac", + "Bb", + 10 + ], + [ + "BAAcc", + "CACbabA", + 11 + ], + [ + "BAB", + "AcbBaaBa", + 11 + ], + [ + "BAB", + "BB", + 2 + ], + [ + "BAB", + "Caa", + 5 + ], + [ + "BAB", + "cc", + 6 + ], + [ + "BABA", + "abbccc", + 11 + ], + [ + "BABA", + "caCca", + 8 + ], + [ + "BABB", + "bBCCBcAcb", + 13 + ], + [ + "BABBAAc", + "caAaa", + 10 + ], + [ + "BABBbCA", + "bAca", + 9 + ], + [ + "BABBc", + "BCCcAcCC", + 11 + ], + [ + "BABCB", + "AB", + 6 + ], + [ + "BABCcBbA", + "CBb", + 10 + ], + [ + "BABa", + "abbbaccAa", + 14 + ], + [ + "BABabAB", + "b", + 12 + ], + [ + "BABacA", + "abcBA", + 8 + ], + [ + "BABb", + "cC", + 8 + ], + [ + "BABbC", + "BaA", + 7 + ], + [ + "BABbb", + "Ccc", + 10 + ], + [ + "BABc", + "aB", + 5 + ], + [ + "BABcACCaC", + "CbbBaCbaC", + 10 + ], + [ + "BABcBA", + "CA", + 9 + ], + [ + "BABcBCaBc", + "CBAaBCC", + 11 + ], + [ + "BABcBcBa", + "bCBACcc", + 11 + ], + [ + "BABcaBB", + "bAaabb", + 7 + ], + [ + "BABcb", + "CcCAaC", + 11 + ], + [ + "BABcbcaa", + "BbbCcAAab", + 11 + ], + [ + "BABccba", + "cCbbbA", + 10 + ], + [ + "BAC", + "BB", + 4 + ], + [ + "BAC", + "BBacAbC", + 8 + ], + [ + "BAC", + "BaacBaC", + 9 + ], + [ + "BAC", + "BbbBaB", + 9 + ], + [ + "BAC", + "CAAaa", + 8 + ], + [ + "BAC", + "CBBbBccbc", + 15 + ], + [ + "BAC", + "b", + 5 + ], + [ + "BAC", + "babAAcA", + 10 + ], + [ + "BACACcCcc", + "cbB", + 16 + ], + [ + "BACB", + "AaCABa", + 7 + ], + [ + "BACBAACba", + "BaA", + 13 + ], + [ + "BACBCAC", + "A", + 12 + ], + [ + "BACBaB", + "Bcbc", + 8 + ], + [ + "BACBbCbB", + "ACaAAaCb", + 12 + ], + [ + "BACC", + "ab", + 7 + ], + [ + "BACCBcbaa", + "ab", + 15 + ], + [ + "BACCCAaCA", + "bCCcBb", + 12 + ], + [ + "BACCbABA", + "AAAAcAB", + 10 + ], + [ + "BACCbAac", + "AcBCaacB", + 10 + ], + [ + "BACCc", + "BbAAcA", + 7 + ], + [ + "BACCc", + "aBbbBBCa", + 12 + ], + [ + "BACaC", + "CB", + 8 + ], + [ + "BACab", + "bCA", + 6 + ], + [ + "BACb", + "AcBC", + 6 + ], + [ + "BACbAbCbC", + "BbC", + 12 + ], + [ + "BACbbaaAc", + "ACabC", + 11 + ], + [ + "BACcBbcCb", + "AcbB", + 11 + ], + [ + "BACcbBbc", + "AC", + 12 + ], + [ + "BACccabb", + "bAcAAa", + 10 + ], + [ + "BACcccA", + "aacC", + 10 + ], + [ + "BAa", + "AC", + 4 + ], + [ + "BAa", + "AbCBcA", + 9 + ], + [ + "BAa", + "CabbAabb", + 11 + ], + [ + "BAa", + "cBaAa", + 4 + ], + [ + "BAaA", + "BCB", + 6 + ], + [ + "BAaABbB", + "ABabABcbC", + 9 + ], + [ + "BAaAbCc", + "acb", + 10 + ], + [ + "BAaAcCb", + "AbbC", + 10 + ], + [ + "BAaAcbB", + "Aa", + 10 + ], + [ + "BAaBBaAb", + "bBaBcaAAa", + 9 + ], + [ + "BAaBaB", + "A", + 10 + ], + [ + "BAaBbCc", + "cacBaAcaB", + 13 + ], + [ + "BAaBbabc", + "A", + 14 + ], + [ + "BAaBcCaaa", + "ab", + 15 + ], + [ + "BAaBcCb", + "aCAA", + 11 + ], + [ + "BAaCB", + "BBb", + 7 + ], + [ + "BAaCB", + "aABACA", + 7 + ], + [ + "BAaCaA", + "acB", + 9 + ], + [ + "BAaCabCCa", + "b", + 16 + ], + [ + "BAaCbAC", + "bA", + 10 + ], + [ + "BAaCbB", + "aCcbbAAA", + 13 + ], + [ + "BAaCcc", + "bc", + 9 + ], + [ + "BAaaACaA", + "Ccbcaa", + 12 + ], + [ + "BAaaCCb", + "bCBC", + 11 + ], + [ + "BAab", + "c", + 8 + ], + [ + "BAab", + "ccaba", + 6 + ], + [ + "BAabAbBB", + "ABaBaaBa", + 10 + ], + [ + "BAabCA", + "Cb", + 10 + ], + [ + "BAabCCA", + "CCbBcb", + 11 + ], + [ + "BAac", + "bBbcaca", + 8 + ], + [ + "BAacBb", + "cABAca", + 9 + ], + [ + "BAacbBbCb", + "bBAca", + 13 + ], + [ + "BAacbaBC", + "ABCbcaCAc", + 12 + ], + [ + "BAaccAba", + "BAbBbCA", + 11 + ], + [ + "BAb", + "Aa", + 4 + ], + [ + "BAb", + "AbaccbCbC", + 14 + ], + [ + "BAb", + "BaCa", + 5 + ], + [ + "BAb", + "CCaBA", + 8 + ], + [ + "BAb", + "CbCb", + 5 + ], + [ + "BAb", + "caAAC", + 8 + ], + [ + "BAb", + "ccCAAAABC", + 15 + ], + [ + "BAbABaBbC", + "CAAcAb", + 11 + ], + [ + "BAbACcA", + "aBaaCcc", + 8 + ], + [ + "BAbAa", + "ABbCbabac", + 11 + ], + [ + "BAbAa", + "c", + 10 + ], + [ + "BAbAc", + "cBc", + 7 + ], + [ + "BAbAcABa", + "ac", + 13 + ], + [ + "BAbAcbB", + "aCAcBB", + 6 + ], + [ + "BAbBA", + "BcaCBcaBa", + 11 + ], + [ + "BAbBAB", + "ABBCCb", + 8 + ], + [ + "BAbBBaB", + "abaA", + 9 + ], + [ + "BAbBCc", + "aaAcAcAAC", + 14 + ], + [ + "BAbBc", + "ABcAAABb", + 10 + ], + [ + "BAbCCbBC", + "bc", + 13 + ], + [ + "BAbCbBacB", + "acBca", + 12 + ], + [ + "BAbCc", + "BBbcBAab", + 11 + ], + [ + "BAbCcB", + "CbacAaaa", + 14 + ], + [ + "BAbCcBB", + "ccaBBcACC", + 14 + ], + [ + "BAba", + "Bc", + 6 + ], + [ + "BAbaBCAB", + "bbCbBa", + 11 + ], + [ + "BAbaBbBc", + "baAC", + 11 + ], + [ + "BAbaCc", + "C", + 10 + ], + [ + "BAbabca", + "cBabaCb", + 8 + ], + [ + "BAbacCaa", + "CCa", + 11 + ], + [ + "BAbb", + "baBaBaB", + 9 + ], + [ + "BAbbBAA", + "ACbcccc", + 12 + ], + [ + "BAbc", + "B", + 6 + ], + [ + "BAbc", + "bABCB", + 5 + ], + [ + "BAbc", + "bcBAb", + 6 + ], + [ + "BAbcAcA", + "ccA", + 8 + ], + [ + "BAbcaBab", + "CaACACbcB", + 13 + ], + [ + "BAbccAcB", + "BA", + 12 + ], + [ + "BAbccaBA", + "CCCcaa", + 10 + ], + [ + "BAc", + "AAAccAa", + 10 + ], + [ + "BAc", + "Bbc", + 2 + ], + [ + "BAc", + "ba", + 4 + ], + [ + "BAcA", + "Ac", + 4 + ], + [ + "BAcAA", + "acbCAA", + 7 + ], + [ + "BAcAACc", + "accA", + 9 + ], + [ + "BAcAAb", + "AbC", + 10 + ], + [ + "BAcAB", + "aAacCcbac", + 13 + ], + [ + "BAcACCb", + "cccBC", + 10 + ], + [ + "BAcACcBA", + "BCAbccA", + 7 + ], + [ + "BAcAcC", + "aAcBb", + 8 + ], + [ + "BAcBa", + "CCAbBCc", + 10 + ], + [ + "BAcBcba", + "aaa", + 11 + ], + [ + "BAcCBCbaA", + "bCb", + 13 + ], + [ + "BAcCC", + "ACCaC", + 5 + ], + [ + "BAcCCBAbA", + "b", + 16 + ], + [ + "BAcCaB", + "cCBBCaa", + 10 + ], + [ + "BAcCbC", + "CAB", + 9 + ], + [ + "BAcCbaC", + "CCBBaCabB", + 14 + ], + [ + "BAcCbc", + "Cb", + 8 + ], + [ + "BAcCcAACa", + "aacaCaA", + 11 + ], + [ + "BAcaBBBc", + "a", + 14 + ], + [ + "BAcaBc", + "CAbaa", + 8 + ], + [ + "BAcaC", + "cAcaCA", + 4 + ], + [ + "BAcaab", + "ACbA", + 8 + ], + [ + "BAcab", + "BaCbca", + 7 + ], + [ + "BAcbA", + "AbA", + 4 + ], + [ + "BAcbA", + "bACBCbbCA", + 10 + ], + [ + "BAcbAcCB", + "ac", + 13 + ], + [ + "BAcbBCC", + "bBaba", + 11 + ], + [ + "BAcbCC", + "c", + 10 + ], + [ + "BAcbCc", + "B", + 10 + ], + [ + "BAcbabcB", + "aAa", + 12 + ], + [ + "BAcbbba", + "Cb", + 11 + ], + [ + "BAcbcABbA", + "baABcBCa", + 10 + ], + [ + "BAccAAA", + "AaaC", + 10 + ], + [ + "BAccAAca", + "BBCbbCcab", + 11 + ], + [ + "BAccAbCB", + "Cba", + 13 + ], + [ + "BAccCAb", + "cCBcCcac", + 11 + ], + [ + "BAccCAbC", + "BCabCBA", + 11 + ], + [ + "BAccCCcA", + "CbbBaCB", + 14 + ], + [ + "BAccCcCB", + "bcCAB", + 9 + ], + [ + "BAcca", + "cBcCBBcbC", + 13 + ], + [ + "BAccaAaC", + "cbBcAaA", + 10 + ], + [ + "BAcccCbb", + "Bb", + 12 + ], + [ + "BB", + "A", + 4 + ], + [ + "BB", + "AACBAacBB", + 14 + ], + [ + "BB", + "ABaA", + 6 + ], + [ + "BB", + "ACbbCcc", + 12 + ], + [ + "BB", + "AbAABC", + 9 + ], + [ + "BB", + "AbbbBBa", + 10 + ], + [ + "BB", + "BC", + 2 + ], + [ + "BB", + "Ba", + 2 + ], + [ + "BB", + "BbcAaACB", + 12 + ], + [ + "BB", + "CAcaACbBb", + 15 + ], + [ + "BB", + "CBabcc", + 9 + ], + [ + "BB", + "CaCBb", + 7 + ], + [ + "BB", + "CaaAAaAAc", + 18 + ], + [ + "BB", + "Cab", + 5 + ], + [ + "BB", + "Cb", + 3 + ], + [ + "BB", + "CbAAC", + 9 + ], + [ + "BB", + "CbcbCcBc", + 13 + ], + [ + "BB", + "Ccccc", + 10 + ], + [ + "BB", + "a", + 4 + ], + [ + "BB", + "aAAbcc", + 11 + ], + [ + "BB", + "aACBABa", + 10 + ], + [ + "BB", + "aACbBCaac", + 15 + ], + [ + "BB", + "aAcCCb", + 11 + ], + [ + "BB", + "aCACcBC", + 12 + ], + [ + "BB", + "aCa", + 6 + ], + [ + "BB", + "aa", + 4 + ], + [ + "BB", + "bAABCCabC", + 15 + ], + [ + "BB", + "bABBCb", + 8 + ], + [ + "BB", + "bBacaAb", + 11 + ], + [ + "BB", + "bc", + 3 + ], + [ + "BB", + "cBBcCBaAa", + 14 + ], + [ + "BB", + "cCaAaBc", + 12 + ], + [ + "BB", + "cCb", + 5 + ], + [ + "BB", + "cCc", + 6 + ], + [ + "BB", + "caBcCA", + 10 + ], + [ + "BB", + "cac", + 6 + ], + [ + "BB", + "cbb", + 4 + ], + [ + "BB", + "ccaC", + 8 + ], + [ + "BBA", + "BB", + 2 + ], + [ + "BBA", + "BCCaaAb", + 10 + ], + [ + "BBA", + "Bb", + 3 + ], + [ + "BBA", + "Bba", + 2 + ], + [ + "BBA", + "CBbBab", + 7 + ], + [ + "BBA", + "aaBbAc", + 7 + ], + [ + "BBA", + "ab", + 5 + ], + [ + "BBA", + "bAAaBCbA", + 11 + ], + [ + "BBA", + "cbcBbC", + 9 + ], + [ + "BBAACb", + "BBaBbb", + 5 + ], + [ + "BBAAb", + "abaBbac", + 10 + ], + [ + "BBAAbbCc", + "a", + 15 + ], + [ + "BBAAcAb", + "cCBCaBCA", + 12 + ], + [ + "BBAAcbba", + "aCACbcCc", + 13 + ], + [ + "BBABA", + "bbccbbb", + 11 + ], + [ + "BBABACCCB", + "c", + 17 + ], + [ + "BBABBAca", + "aCCAAcCA", + 13 + ], + [ + "BBABBaBA", + "CaAaaa", + 11 + ], + [ + "BBABBaaBB", + "CccbccAc", + 16 + ], + [ + "BBABa", + "b", + 9 + ], + [ + "BBABcC", + "aCAA", + 10 + ], + [ + "BBABccb", + "C", + 13 + ], + [ + "BBAC", + "bBCAcBaC", + 9 + ], + [ + "BBACB", + "bAb", + 6 + ], + [ + "BBACC", + "CBccc", + 6 + ], + [ + "BBACC", + "aCBBBB", + 10 + ], + [ + "BBACaBC", + "C", + 12 + ], + [ + "BBACcCbBa", + "CCCcCC", + 12 + ], + [ + "BBAbA", + "AbCaCcB", + 12 + ], + [ + "BBAbCcacC", + "aBaacBcb", + 11 + ], + [ + "BBAba", + "BCbBcB", + 9 + ], + [ + "BBAbcB", + "BBc", + 6 + ], + [ + "BBAbcBBaA", + "bCcAB", + 13 + ], + [ + "BBAbcC", + "BacA", + 7 + ], + [ + "BBAcA", + "bBcaAacbb", + 11 + ], + [ + "BBAcC", + "cC", + 6 + ], + [ + "BBB", + "BaBbB", + 4 + ], + [ + "BBB", + "Bbcbc", + 6 + ], + [ + "BBB", + "CC", + 6 + ], + [ + "BBB", + "CCCAbc", + 11 + ], + [ + "BBB", + "CcbcBc", + 9 + ], + [ + "BBB", + "aaAbb", + 8 + ], + [ + "BBBAAA", + "AA", + 8 + ], + [ + "BBBAAAcB", + "BAcAcCCA", + 11 + ], + [ + "BBBACc", + "AaBCBb", + 10 + ], + [ + "BBBAbAaAB", + "aAcBABA", + 13 + ], + [ + "BBBAccBbC", + "ccbCbAb", + 14 + ], + [ + "BBBB", + "baABcbb", + 9 + ], + [ + "BBBBCCacA", + "bbCCc", + 10 + ], + [ + "BBBBcCA", + "bBAabcB", + 10 + ], + [ + "BBBBccCCC", + "C", + 16 + ], + [ + "BBBCABbb", + "cAAaAB", + 12 + ], + [ + "BBBCab", + "abBACbBc", + 10 + ], + [ + "BBBa", + "CCabbCcaB", + 14 + ], + [ + "BBBa", + "cAa", + 6 + ], + [ + "BBBaCA", + "AAbbaaACB", + 12 + ], + [ + "BBBaaBb", + "cBcaac", + 8 + ], + [ + "BBBab", + "bAa", + 7 + ], + [ + "BBBac", + "aaCcCBcAB", + 15 + ], + [ + "BBBb", + "B", + 6 + ], + [ + "BBBbCCb", + "BbBaCc", + 6 + ], + [ + "BBBba", + "BCaC", + 8 + ], + [ + "BBBbcBcCA", + "BbBB", + 11 + ], + [ + "BBBbccc", + "A", + 14 + ], + [ + "BBBc", + "cCAba", + 9 + ], + [ + "BBBcAB", + "AbCAAbc", + 10 + ], + [ + "BBBcBAa", + "AbBCCa", + 8 + ], + [ + "BBBcBabbB", + "aCC", + 16 + ], + [ + "BBBcbBA", + "AbACCCbCb", + 14 + ], + [ + "BBBcc", + "CCbbabCb", + 12 + ], + [ + "BBBccBaaB", + "BcCB", + 11 + ], + [ + "BBC", + "CaAAAaCbB", + 16 + ], + [ + "BBC", + "Cbcbcc", + 9 + ], + [ + "BBC", + "abBBacA", + 9 + ], + [ + "BBC", + "b", + 5 + ], + [ + "BBC", + "bCBAA", + 7 + ], + [ + "BBC", + "bcCaAbb", + 11 + ], + [ + "BBC", + "cba", + 5 + ], + [ + "BBCA", + "BAcCC", + 6 + ], + [ + "BBCAbbbc", + "BAa", + 12 + ], + [ + "BBCAcb", + "Ca", + 9 + ], + [ + "BBCBb", + "aac", + 9 + ], + [ + "BBCBbc", + "CaaBB", + 9 + ], + [ + "BBCBcaBb", + "ccCcCbBC", + 11 + ], + [ + "BBCBcb", + "CBcCBcacC", + 10 + ], + [ + "BBCC", + "C", + 6 + ], + [ + "BBCCBC", + "aCbbA", + 9 + ], + [ + "BBCCBab", + "cBCaaC", + 8 + ], + [ + "BBCCCbaCB", + "AAA", + 17 + ], + [ + "BBCCaBbB", + "BaBAcCbC", + 11 + ], + [ + "BBCCccBCB", + "bCaaCA", + 13 + ], + [ + "BBCa", + "aB", + 6 + ], + [ + "BBCaa", + "AccACcAAC", + 14 + ], + [ + "BBCabCCBB", + "BcAcCaCC", + 12 + ], + [ + "BBCbB", + "BBb", + 4 + ], + [ + "BBCbB", + "CB", + 6 + ], + [ + "BBCbB", + "caABAb", + 10 + ], + [ + "BBCbc", + "BAAbcaaBc", + 11 + ], + [ + "BBCc", + "BabAb", + 7 + ], + [ + "BBCc", + "cbabab", + 10 + ], + [ + "BBCcbACcB", + "Ab", + 15 + ], + [ + "BBCccCAba", + "aCcCCBAb", + 9 + ], + [ + "BBa", + "AcBcBAb", + 9 + ], + [ + "BBa", + "Ccb", + 6 + ], + [ + "BBa", + "aCCcbCAc", + 14 + ], + [ + "BBa", + "bBaCBa", + 6 + ], + [ + "BBa", + "cbAcBBaA", + 10 + ], + [ + "BBaA", + "bAAaBAAAB", + 12 + ], + [ + "BBaAAccbb", + "CABB", + 14 + ], + [ + "BBaACBaB", + "cbcC", + 13 + ], + [ + "BBaAab", + "BCB", + 9 + ], + [ + "BBaAcAAcb", + "cbCBBCAcb", + 11 + ], + [ + "BBaAcB", + "ACbAcb", + 7 + ], + [ + "BBaBbcB", + "C", + 13 + ], + [ + "BBaCB", + "cC", + 8 + ], + [ + "BBaCCA", + "ABAcc", + 7 + ], + [ + "BBaCccb", + "Cbcbb", + 9 + ], + [ + "BBaa", + "ba", + 5 + ], + [ + "BBaaCCBA", + "AbCbC", + 12 + ], + [ + "BBaaCca", + "aaBBcbAB", + 13 + ], + [ + "BBaaa", + "B", + 8 + ], + [ + "BBaab", + "bbAcbB", + 7 + ], + [ + "BBaabcA", + "BACBAC", + 10 + ], + [ + "BBaacbcaB", + "Abacab", + 10 + ], + [ + "BBaacc", + "C", + 11 + ], + [ + "BBabBa", + "BbBBcAACB", + 13 + ], + [ + "BBabcBab", + "abB", + 10 + ], + [ + "BBabcbb", + "BbbCbA", + 6 + ], + [ + "BBac", + "cBAB", + 5 + ], + [ + "BBacAcaAa", + "Bba", + 13 + ], + [ + "BBacCaBBa", + "aa", + 14 + ], + [ + "BBacCc", + "bcaBA", + 9 + ], + [ + "BBacaABc", + "Cbbaac", + 10 + ], + [ + "BBacb", + "AbbaCBCb", + 9 + ], + [ + "BBacb", + "abCACCBBA", + 14 + ], + [ + "BBb", + "BAcaAaca", + 14 + ], + [ + "BBb", + "BBa", + 2 + ], + [ + "BBb", + "CAcBCAAc", + 14 + ], + [ + "BBb", + "aBACcCCC", + 14 + ], + [ + "BBb", + "acbCc", + 8 + ], + [ + "BBbA", + "ca", + 7 + ], + [ + "BBbAACb", + "ACCbb", + 10 + ], + [ + "BBbABAaAB", + "cAbaCba", + 13 + ], + [ + "BBbABbac", + "cAB", + 12 + ], + [ + "BBbAabbCA", + "A", + 16 + ], + [ + "BBbAbcB", + "AcAAcbABa", + 12 + ], + [ + "BBbAcaAB", + "cBB", + 12 + ], + [ + "BBbAcc", + "bBAb", + 7 + ], + [ + "BBbB", + "ccABABc", + 10 + ], + [ + "BBbBB", + "a", + 10 + ], + [ + "BBbBBbba", + "aACbb", + 12 + ], + [ + "BBbBBcacc", + "AcAa", + 15 + ], + [ + "BBbBCbBc", + "BbABC", + 9 + ], + [ + "BBbBaa", + "bbBbbbaAb", + 9 + ], + [ + "BBbCCCCbc", + "ccaAB", + 15 + ], + [ + "BBbCCcbAc", + "bABcac", + 11 + ], + [ + "BBbCbcb", + "Ab", + 12 + ], + [ + "BBba", + "a", + 6 + ], + [ + "BBbaBc", + "ac", + 8 + ], + [ + "BBbab", + "AcAB", + 8 + ], + [ + "BBbabb", + "CCbAa", + 9 + ], + [ + "BBbabbab", + "bCBBbaA", + 9 + ], + [ + "BBbb", + "BA", + 6 + ], + [ + "BBbbACc", + "A", + 12 + ], + [ + "BBbbCCBB", + "AaCcBAAA", + 15 + ], + [ + "BBbcABBbC", + "AaC", + 14 + ], + [ + "BBbcB", + "ACbA", + 8 + ], + [ + "BBbcCac", + "cbb", + 11 + ], + [ + "BBbccCCBB", + "BbaBcb", + 12 + ], + [ + "BBc", + "ABCbAA", + 9 + ], + [ + "BBc", + "BbbABAACA", + 13 + ], + [ + "BBc", + "BcccAbBaC", + 13 + ], + [ + "BBc", + "aBaCccA", + 10 + ], + [ + "BBc", + "aC", + 5 + ], + [ + "BBc", + "aCacBbCCa", + 14 + ], + [ + "BBcAAA", + "Acbc", + 10 + ], + [ + "BBcAAac", + "acCcAbc", + 9 + ], + [ + "BBcABAA", + "BbBaABaC", + 7 + ], + [ + "BBcB", + "AAacBC", + 8 + ], + [ + "BBcB", + "aAaabB", + 10 + ], + [ + "BBcB", + "acb", + 5 + ], + [ + "BBcB", + "bAcAAbac", + 12 + ], + [ + "BBcBACa", + "CBACA", + 6 + ], + [ + "BBcBCB", + "c", + 10 + ], + [ + "BBcBbCBA", + "cBaaAca", + 12 + ], + [ + "BBcBbcA", + "bBbca", + 6 + ], + [ + "BBcC", + "AcAcAcCc", + 12 + ], + [ + "BBcCBCac", + "ababc", + 12 + ], + [ + "BBcCC", + "ccB", + 7 + ], + [ + "BBcCCabAA", + "BCC", + 12 + ], + [ + "BBcCCcab", + "bCcc", + 10 + ], + [ + "BBcCa", + "BBAA", + 5 + ], + [ + "BBcCcb", + "BCb", + 6 + ], + [ + "BBca", + "b", + 7 + ], + [ + "BBcaA", + "AaCCAbCCC", + 15 + ], + [ + "BBcaA", + "b", + 9 + ], + [ + "BBcaACbB", + "AbCb", + 11 + ], + [ + "BBcaC", + "CCccCCCC", + 12 + ], + [ + "BBcaa", + "aBaa", + 4 + ], + [ + "BBcaa", + "bAAAC", + 8 + ], + [ + "BBcaa", + "ccCCcABA", + 12 + ], + [ + "BBcaaCBB", + "BbCcbC", + 10 + ], + [ + "BBcab", + "aaBc", + 8 + ], + [ + "BBcbA", + "BcBabbA", + 6 + ], + [ + "BBcbaA", + "caBbCcaa", + 9 + ], + [ + "BBcbbaB", + "ccCbbAccB", + 10 + ], + [ + "BBcbbabC", + "BB", + 12 + ], + [ + "BBcc", + "AcbAAB", + 11 + ], + [ + "BBcc", + "CCcBccBbC", + 12 + ], + [ + "BBccAb", + "aCACbAC", + 11 + ], + [ + "BBccB", + "BcB", + 4 + ], + [ + "BBccc", + "BBAbCAcbB", + 11 + ], + [ + "BBcccCAAB", + "ABABBAca", + 14 + ], + [ + "BC", + "A", + 4 + ], + [ + "BC", + "AAcba", + 9 + ], + [ + "BC", + "AC", + 2 + ], + [ + "BC", + "ACBACCA", + 10 + ], + [ + "BC", + "AaA", + 6 + ], + [ + "BC", + "AaCABABAb", + 16 + ], + [ + "BC", + "Ac", + 3 + ], + [ + "BC", + "AcCCcc", + 10 + ], + [ + "BC", + "B", + 2 + ], + [ + "BC", + "BAbC", + 4 + ], + [ + "BC", + "BC", + 0 + ], + [ + "BC", + "BCCCC", + 6 + ], + [ + "BC", + "BbAaaBAbb", + 16 + ], + [ + "BC", + "BbB", + 4 + ], + [ + "BC", + "BcB", + 3 + ], + [ + "BC", + "C", + 2 + ], + [ + "BC", + "CA", + 4 + ], + [ + "BC", + "CaaaCAcaC", + 16 + ], + [ + "BC", + "CbCC", + 5 + ], + [ + "BC", + "CcC", + 4 + ], + [ + "BC", + "CcCCCab", + 12 + ], + [ + "BC", + "CcaAbbbcb", + 16 + ], + [ + "BC", + "CccAb", + 9 + ], + [ + "BC", + "aAA", + 6 + ], + [ + "BC", + "aAbbcbac", + 14 + ], + [ + "BC", + "aCbAaAbBB", + 16 + ], + [ + "BC", + "abbba", + 9 + ], + [ + "BC", + "acAACAC", + 12 + ], + [ + "BC", + "acbCA", + 7 + ], + [ + "BC", + "acccbAAB", + 15 + ], + [ + "BC", + "bAABa", + 8 + ], + [ + "BC", + "bBBAcBbCC", + 14 + ], + [ + "BC", + "bC", + 1 + ], + [ + "BC", + "bCaCaCaa", + 13 + ], + [ + "BC", + "bCbaCa", + 9 + ], + [ + "BC", + "bCbaaaAAC", + 15 + ], + [ + "BC", + "babCAbca", + 13 + ], + [ + "BC", + "babbBBaB", + 14 + ], + [ + "BC", + "bbCA", + 5 + ], + [ + "BC", + "c", + 3 + ], + [ + "BC", + "cAAB", + 8 + ], + [ + "BC", + "cABCBCAB", + 12 + ], + [ + "BC", + "cBaACA", + 8 + ], + [ + "BC", + "cBbaAcB", + 11 + ], + [ + "BC", + "cCCBb", + 8 + ], + [ + "BC", + "cb", + 4 + ], + [ + "BC", + "cbBBB", + 8 + ], + [ + "BC", + "cba", + 5 + ], + [ + "BC", + "cbaB", + 7 + ], + [ + "BC", + "cbaCbBAC", + 12 + ], + [ + "BC", + "cbbCabA", + 11 + ], + [ + "BC", + "ccAACb", + 10 + ], + [ + "BC", + "ccccbb", + 11 + ], + [ + "BCA", + "A", + 4 + ], + [ + "BCA", + "AbcCbCaA", + 11 + ], + [ + "BCA", + "abccbBCC", + 12 + ], + [ + "BCAABABCB", + "abBbBaB", + 11 + ], + [ + "BCAABc", + "bBACcBa", + 9 + ], + [ + "BCAAbBc", + "AaABC", + 8 + ], + [ + "BCAAbbbc", + "A", + 14 + ], + [ + "BCAAcbb", + "CAaCAcBbC", + 9 + ], + [ + "BCAAccAa", + "Aaaa", + 10 + ], + [ + "BCABBA", + "AbBba", + 8 + ], + [ + "BCABBBAa", + "bCAbBA", + 6 + ], + [ + "BCABa", + "b", + 9 + ], + [ + "BCABcB", + "bcAAbCb", + 7 + ], + [ + "BCABcCCc", + "CACcbBAc", + 10 + ], + [ + "BCABccC", + "B", + 12 + ], + [ + "BCABccaB", + "cAaCbBCc", + 13 + ], + [ + "BCAC", + "CAcCB", + 6 + ], + [ + "BCACBaACB", + "aaCaC", + 11 + ], + [ + "BCACBaba", + "BbAaAbc", + 9 + ], + [ + "BCACBb", + "aCbCA", + 8 + ], + [ + "BCACb", + "CAABCa", + 8 + ], + [ + "BCACcABBB", + "C", + 16 + ], + [ + "BCACcCAC", + "aBcAaaB", + 12 + ], + [ + "BCACcb", + "AC", + 8 + ], + [ + "BCAa", + "A", + 6 + ], + [ + "BCAa", + "Cc", + 6 + ], + [ + "BCAacBCB", + "CaCC", + 9 + ], + [ + "BCAbBAbAc", + "aBcbB", + 13 + ], + [ + "BCAbBaCB", + "caCba", + 11 + ], + [ + "BCAbCB", + "ccBcBCC", + 10 + ], + [ + "BCAbaCC", + "aB", + 12 + ], + [ + "BCAbbAcBA", + "A", + 16 + ], + [ + "BCAbbBA", + "BBAAAbac", + 10 + ], + [ + "BCAbc", + "Cb", + 6 + ], + [ + "BCAbcaBCC", + "ccBacBbA", + 13 + ], + [ + "BCAbccbb", + "bAcaCCbAB", + 12 + ], + [ + "BCAc", + "BaBb", + 6 + ], + [ + "BCAcC", + "Ccc", + 5 + ], + [ + "BCAcCabcc", + "CBC", + 14 + ], + [ + "BCAcabbBc", + "bcbC", + 12 + ], + [ + "BCB", + "caa", + 6 + ], + [ + "BCB", + "ccB", + 3 + ], + [ + "BCBA", + "B", + 6 + ], + [ + "BCBA", + "bAB", + 5 + ], + [ + "BCBAACcc", + "aBB", + 14 + ], + [ + "BCBACbAbA", + "ccABAa", + 11 + ], + [ + "BCBACbCb", + "cBA", + 11 + ], + [ + "BCBAaBcA", + "baC", + 12 + ], + [ + "BCBAabB", + "Bcb", + 9 + ], + [ + "BCBAb", + "CccbcCcBb", + 13 + ], + [ + "BCBAcbbC", + "AccaABA", + 13 + ], + [ + "BCBB", + "A", + 8 + ], + [ + "BCBBA", + "ACAC", + 8 + ], + [ + "BCBBACa", + "AbbAA", + 9 + ], + [ + "BCBBbCbC", + "Ccb", + 11 + ], + [ + "BCBBbab", + "cCAccAbb", + 11 + ], + [ + "BCBBcCa", + "BCCCBbA", + 9 + ], + [ + "BCBBcaCBA", + "CbBCCBacA", + 10 + ], + [ + "BCBBccB", + "B", + 12 + ], + [ + "BCBC", + "CbaBa", + 7 + ], + [ + "BCBC", + "cabAcCaCc", + 13 + ], + [ + "BCBCAaB", + "CAcb", + 9 + ], + [ + "BCBCBaAA", + "b", + 15 + ], + [ + "BCBCBbbc", + "CBBA", + 10 + ], + [ + "BCBCbbBC", + "CACcaACaB", + 14 + ], + [ + "BCBCc", + "aBAA", + 8 + ], + [ + "BCBaACb", + "aCB", + 9 + ], + [ + "BCBaAcb", + "AaC", + 11 + ], + [ + "BCBaC", + "BC", + 6 + ], + [ + "BCBaCCabc", + "aAcbC", + 12 + ], + [ + "BCBb", + "AaaBabaAa", + 14 + ], + [ + "BCBbAABB", + "A", + 14 + ], + [ + "BCBbBCaA", + "B", + 14 + ], + [ + "BCBbc", + "AbBba", + 6 + ], + [ + "BCBbcbBC", + "ca", + 14 + ], + [ + "BCBcB", + "bbC", + 7 + ], + [ + "BCBcaACa", + "bbCaabC", + 10 + ], + [ + "BCBcabB", + "aaBaaaAac", + 14 + ], + [ + "BCC", + "AabbC", + 7 + ], + [ + "BCC", + "AbbBBa", + 10 + ], + [ + "BCC", + "CCBcABb", + 11 + ], + [ + "BCCA", + "bcbbc", + 8 + ], + [ + "BCCA", + "cBBbacAc", + 11 + ], + [ + "BCCAAbbC", + "ccCB", + 12 + ], + [ + "BCCABc", + "c", + 10 + ], + [ + "BCCAaba", + "CAbB", + 8 + ], + [ + "BCCB", + "cBca", + 7 + ], + [ + "BCCBCC", + "ACCBaa", + 6 + ], + [ + "BCCBbcAAA", + "aaaCCB", + 16 + ], + [ + "BCCCAbA", + "CabCcbc", + 10 + ], + [ + "BCCCCAB", + "BC", + 10 + ], + [ + "BCCCCBAB", + "cABCcc", + 13 + ], + [ + "BCCCb", + "cCCAc", + 6 + ], + [ + "BCCaAcbB", + "Cabccaa", + 12 + ], + [ + "BCCaB", + "cAaAcAa", + 12 + ], + [ + "BCCaBABCc", + "a", + 16 + ], + [ + "BCCaCBCAA", + "cBbCaccaA", + 9 + ], + [ + "BCCac", + "CbC", + 7 + ], + [ + "BCCbAc", + "AcCAaBcCc", + 12 + ], + [ + "BCCbCbaB", + "caa", + 13 + ], + [ + "BCCbaBBbC", + "baBAB", + 11 + ], + [ + "BCCbb", + "aAAcc", + 10 + ], + [ + "BCCbbb", + "BCaB", + 7 + ], + [ + "BCCbc", + "BBaAbbBCC", + 13 + ], + [ + "BCCbc", + "caBaAbbC", + 11 + ], + [ + "BCCcaAccC", + "bacbC", + 11 + ], + [ + "BCCcbAcBa", + "CA", + 14 + ], + [ + "BCCcbaBc", + "CcCcCAba", + 9 + ], + [ + "BCCccbaC", + "C", + 14 + ], + [ + "BCa", + "CBbbAac", + 10 + ], + [ + "BCa", + "aAbCCb", + 9 + ], + [ + "BCa", + "bbcAB", + 7 + ], + [ + "BCa", + "c", + 5 + ], + [ + "BCaABBaAB", + "acCcBAC", + 13 + ], + [ + "BCaAa", + "AC", + 8 + ], + [ + "BCaAaCac", + "aaAB", + 11 + ], + [ + "BCaAba", + "a", + 10 + ], + [ + "BCaAcAbC", + "B", + 14 + ], + [ + "BCaBAC", + "bCAcCAaB", + 10 + ], + [ + "BCaBcCB", + "BaAA", + 10 + ], + [ + "BCaCAc", + "CBAaBc", + 8 + ], + [ + "BCaCCCcc", + "ccbaBCaC", + 12 + ], + [ + "BCaCbbbc", + "aaAAa", + 14 + ], + [ + "BCaa", + "bc", + 6 + ], + [ + "BCaaAbaB", + "ccCA", + 13 + ], + [ + "BCaaab", + "cAbBBa", + 11 + ], + [ + "BCaaabC", + "Bbbcb", + 10 + ], + [ + "BCaabcbA", + "BabcCAb", + 8 + ], + [ + "BCabaB", + "BaBAbcB", + 7 + ], + [ + "BCabacC", + "a", + 12 + ], + [ + "BCabbbABB", + "BBCcABb", + 10 + ], + [ + "BCabbcCCC", + "ABcCAcbc", + 13 + ], + [ + "BCabcaAAA", + "Baa", + 12 + ], + [ + "BCacBBAAB", + "aBC", + 14 + ], + [ + "BCacBBbB", + "CBCB", + 10 + ], + [ + "BCacCB", + "bB", + 9 + ], + [ + "BCacCba", + "BaBbaAAba", + 10 + ], + [ + "BCb", + "BBa", + 4 + ], + [ + "BCb", + "bBCCa", + 6 + ], + [ + "BCb", + "bCAAAAC", + 11 + ], + [ + "BCbA", + "Bccc", + 5 + ], + [ + "BCbAAC", + "aA", + 9 + ], + [ + "BCbAcbcCB", + "cbBcba", + 11 + ], + [ + "BCbBCacaC", + "AACB", + 16 + ], + [ + "BCbBa", + "cbbBC", + 6 + ], + [ + "BCbBaa", + "aCAAbACc", + 12 + ], + [ + "BCbBca", + "bBaB", + 8 + ], + [ + "BCbCA", + "cB", + 8 + ], + [ + "BCbCccA", + "aaABcCAB", + 11 + ], + [ + "BCbaAABaa", + "AA", + 14 + ], + [ + "BCbaBAb", + "cAAAcbbaA", + 15 + ], + [ + "BCbaCbBAc", + "CBCcabcAb", + 10 + ], + [ + "BCbaaCCaa", + "cCbac", + 11 + ], + [ + "BCbaabbBC", + "abcCBbB", + 11 + ], + [ + "BCbaccCC", + "bbBc", + 11 + ], + [ + "BCbbc", + "AcCbBaACc", + 11 + ], + [ + "BCbbcbbB", + "AbbCca", + 11 + ], + [ + "BCbc", + "cbcCCcBAA", + 13 + ], + [ + "BCbcBbaC", + "babCBCACb", + 9 + ], + [ + "BCbcaACBB", + "aab", + 14 + ], + [ + "BCbccccB", + "Ca", + 14 + ], + [ + "BCc", + "AAaCBcAbc", + 13 + ], + [ + "BCc", + "AcBbBbcba", + 14 + ], + [ + "BCc", + "CAcB", + 6 + ], + [ + "BCc", + "bABBC", + 7 + ], + [ + "BCcA", + "Aac", + 6 + ], + [ + "BCcA", + "bbbCc", + 7 + ], + [ + "BCcACaB", + "CBcbcaBAA", + 11 + ], + [ + "BCcACaCca", + "cca", + 12 + ], + [ + "BCcAcb", + "bbcacbb", + 6 + ], + [ + "BCcBBAccb", + "cBB", + 12 + ], + [ + "BCcBBacB", + "B", + 14 + ], + [ + "BCcBBc", + "ca", + 10 + ], + [ + "BCcBac", + "Cb", + 9 + ], + [ + "BCcBbBAb", + "BBcBBcA", + 7 + ], + [ + "BCcBbBCc", + "A", + 16 + ], + [ + "BCcBc", + "a", + 10 + ], + [ + "BCcCB", + "CABB", + 6 + ], + [ + "BCcCC", + "baB", + 9 + ], + [ + "BCcCcaaB", + "CAaa", + 10 + ], + [ + "BCca", + "aBcCcA", + 5 + ], + [ + "BCcaAc", + "aaAAA", + 9 + ], + [ + "BCcaBbAb", + "A", + 14 + ], + [ + "BCcab", + "CA", + 7 + ], + [ + "BCcacCbcc", + "CBcabaBCC", + 11 + ], + [ + "BCcb", + "CCbBACCcc", + 12 + ], + [ + "BCcb", + "Ca", + 6 + ], + [ + "BCcb", + "bC", + 5 + ], + [ + "BCcbAc", + "B", + 10 + ], + [ + "BCcbAcBAA", + "AacbBc", + 12 + ], + [ + "BCcbBac", + "Aaa", + 12 + ], + [ + "BCcbaC", + "cB", + 9 + ], + [ + "BCcbc", + "BBa", + 7 + ], + [ + "BCccAac", + "ABB", + 12 + ], + [ + "BCccBb", + "Cc", + 8 + ], + [ + "BCccCC", + "BBA", + 10 + ], + [ + "Ba", + "AAACAbbb", + 15 + ], + [ + "Ba", + "AAaBBbBaa", + 14 + ], + [ + "Ba", + "ABBaAabBa", + 14 + ], + [ + "Ba", + "AaccaB", + 10 + ], + [ + "Ba", + "AcABCBc", + 12 + ], + [ + "Ba", + "AcB", + 6 + ], + [ + "Ba", + "AcaAcb", + 10 + ], + [ + "Ba", + "B", + 2 + ], + [ + "Ba", + "BAcBc", + 7 + ], + [ + "Ba", + "Ba", + 0 + ], + [ + "Ba", + "BcAACc", + 9 + ], + [ + "Ba", + "BcCa", + 4 + ], + [ + "Ba", + "BcbBb", + 8 + ], + [ + "Ba", + "C", + 4 + ], + [ + "Ba", + "CABbaBcb", + 12 + ], + [ + "Ba", + "CBBAAB", + 9 + ], + [ + "Ba", + "CC", + 4 + ], + [ + "Ba", + "CCCcABBC", + 14 + ], + [ + "Ba", + "CbCbCa", + 9 + ], + [ + "Ba", + "Cc", + 4 + ], + [ + "Ba", + "a", + 2 + ], + [ + "Ba", + "aA", + 3 + ], + [ + "Ba", + "aB", + 4 + ], + [ + "Ba", + "aCB", + 6 + ], + [ + "Ba", + "aCbCa", + 7 + ], + [ + "Ba", + "acBbb", + 8 + ], + [ + "Ba", + "b", + 3 + ], + [ + "Ba", + "bcBCAbcCB", + 15 + ], + [ + "Ba", + "c", + 4 + ], + [ + "Ba", + "cAaAABbCB", + 16 + ], + [ + "Ba", + "cBBa", + 4 + ], + [ + "Ba", + "cbBaCAb", + 10 + ], + [ + "Ba", + "ccBcACA", + 11 + ], + [ + "Ba", + "ccC", + 6 + ], + [ + "BaA", + "CACaA", + 6 + ], + [ + "BaA", + "CBAbbaca", + 11 + ], + [ + "BaA", + "aaBB", + 6 + ], + [ + "BaA", + "bA", + 3 + ], + [ + "BaA", + "baACAbcA", + 11 + ], + [ + "BaA", + "cbc", + 6 + ], + [ + "BaA", + "ccBAAB", + 7 + ], + [ + "BaAAA", + "BB", + 8 + ], + [ + "BaAAa", + "ca", + 8 + ], + [ + "BaAAaCBaA", + "ACCC", + 14 + ], + [ + "BaAAcCA", + "abCCBA", + 9 + ], + [ + "BaAAcc", + "aaAAbac", + 6 + ], + [ + "BaAAccaCA", + "CcABcBCc", + 12 + ], + [ + "BaAB", + "BBAB", + 2 + ], + [ + "BaAB", + "ac", + 6 + ], + [ + "BaAB", + "bBB", + 5 + ], + [ + "BaABAACBA", + "bAb", + 14 + ], + [ + "BaABCaACb", + "bCbabAaBb", + 13 + ], + [ + "BaABa", + "abC", + 7 + ], + [ + "BaABcbab", + "cAaca", + 10 + ], + [ + "BaACAcCc", + "BC", + 12 + ], + [ + "BaACB", + "bcAa", + 7 + ], + [ + "BaACBAbAC", + "Bac", + 13 + ], + [ + "BaACaCbC", + "AACACCC", + 6 + ], + [ + "BaAa", + "BBCC", + 6 + ], + [ + "BaAa", + "BcbccbCC", + 14 + ], + [ + "BaAa", + "aAbB", + 6 + ], + [ + "BaAaAaAB", + "Ccab", + 13 + ], + [ + "BaAaaAAb", + "ABBCBAcB", + 13 + ], + [ + "BaAacA", + "bACCCb", + 9 + ], + [ + "BaAb", + "AbCBcCba", + 12 + ], + [ + "BaAbCBa", + "CCCcAb", + 13 + ], + [ + "BaAbab", + "aAbaBCc", + 7 + ], + [ + "BaAbcCCB", + "CBcA", + 13 + ], + [ + "BaAcAB", + "BabCCCaC", + 10 + ], + [ + "BaAcAa", + "abaCCCBAA", + 11 + ], + [ + "BaAcAbbC", + "ABaBBAC", + 10 + ], + [ + "BaAcB", + "CBAACbCa", + 9 + ], + [ + "BaAcCbb", + "Abb", + 8 + ], + [ + "BaAcCbca", + "BcCbCAAcB", + 12 + ], + [ + "BaAcbcb", + "c", + 12 + ], + [ + "BaB", + "CACAb", + 8 + ], + [ + "BaB", + "aCACaB", + 8 + ], + [ + "BaB", + "bc", + 5 + ], + [ + "BaB", + "cBBB", + 4 + ], + [ + "BaB", + "cCacaaB", + 10 + ], + [ + "BaBA", + "cABACbbA", + 10 + ], + [ + "BaBAA", + "bbAacbA", + 9 + ], + [ + "BaBAB", + "acAaABBA", + 11 + ], + [ + "BaBABabC", + "C", + 14 + ], + [ + "BaBACA", + "ACAcbbccb", + 15 + ], + [ + "BaBAc", + "BaBABaaC", + 7 + ], + [ + "BaBB", + "aBBCaA", + 8 + ], + [ + "BaBBaABA", + "AaabaABb", + 7 + ], + [ + "BaBBb", + "BC", + 8 + ], + [ + "BaBBc", + "C", + 9 + ], + [ + "BaBBcAaAc", + "C", + 17 + ], + [ + "BaBBcbcb", + "bbabb", + 10 + ], + [ + "BaBCAbab", + "A", + 14 + ], + [ + "BaBCBC", + "BA", + 9 + ], + [ + "BaBCaCac", + "cACc", + 10 + ], + [ + "BaBCc", + "cBcCbAab", + 13 + ], + [ + "BaBa", + "bBBABAabb", + 11 + ], + [ + "BaBa", + "bCAbaAbCb", + 13 + ], + [ + "BaBaBB", + "C", + 12 + ], + [ + "BaBb", + "CC", + 8 + ], + [ + "BaBb", + "bbAbCcbBc", + 13 + ], + [ + "BaBbABA", + "ababcCAc", + 11 + ], + [ + "BaBbBb", + "cCAAb", + 10 + ], + [ + "BaBbC", + "BBACac", + 8 + ], + [ + "BaBbabbCb", + "b", + 16 + ], + [ + "BaBc", + "CB", + 6 + ], + [ + "BaBcACac", + "AbCaBA", + 11 + ], + [ + "BaBcB", + "AaabaaBAa", + 13 + ], + [ + "BaBcBbbAa", + "BCACcab", + 13 + ], + [ + "BaBcCA", + "CbCACA", + 8 + ], + [ + "BaBcCA", + "bCa", + 8 + ], + [ + "BaBcaCCbB", + "BCC", + 12 + ], + [ + "BaBcacC", + "ACB", + 12 + ], + [ + "BaC", + "ABAbc", + 6 + ], + [ + "BaC", + "ABbb", + 6 + ], + [ + "BaC", + "ACa", + 5 + ], + [ + "BaC", + "Aaba", + 6 + ], + [ + "BaC", + "BACccaaBA", + 13 + ], + [ + "BaC", + "BC", + 2 + ], + [ + "BaC", + "Bccb", + 5 + ], + [ + "BaC", + "a", + 4 + ], + [ + "BaC", + "bCbbBcB", + 12 + ], + [ + "BaC", + "baBcbC", + 7 + ], + [ + "BaC", + "cBAabACAa", + 12 + ], + [ + "BaC", + "cBaBAaCc", + 10 + ], + [ + "BaCAA", + "cbbcBBbb", + 14 + ], + [ + "BaCAAbbB", + "BAcB", + 10 + ], + [ + "BaCAB", + "cC", + 8 + ], + [ + "BaCABCB", + "bB", + 11 + ], + [ + "BaCABccaB", + "CAacBCCA", + 12 + ], + [ + "BaCAaAc", + "bCa", + 9 + ], + [ + "BaCB", + "B", + 6 + ], + [ + "BaCBa", + "BbAbaBC", + 9 + ], + [ + "BaCBaCcCC", + "aCCAc", + 11 + ], + [ + "BaCBbCB", + "acB", + 9 + ], + [ + "BaCBc", + "ABCB", + 6 + ], + [ + "BaCBcBC", + "cc", + 11 + ], + [ + "BaCCAcAb", + "Bab", + 10 + ], + [ + "BaCCC", + "CcBCabAAa", + 14 + ], + [ + "BaCCCAcb", + "bbcAaA", + 12 + ], + [ + "BaCCCbb", + "bCCaaCb", + 9 + ], + [ + "BaCCbAA", + "acCBcBB", + 10 + ], + [ + "BaCCc", + "Ca", + 8 + ], + [ + "BaCCcCcc", + "baAbcaCc", + 8 + ], + [ + "BaCa", + "bbAba", + 6 + ], + [ + "BaCaAAaca", + "CCbCb", + 15 + ], + [ + "BaCaCCABa", + "bCaC", + 11 + ], + [ + "BaCb", + "bbaA", + 7 + ], + [ + "BaCb", + "cABC", + 7 + ], + [ + "BaCbAB", + "BAcbaAC", + 6 + ], + [ + "BaCbBA", + "bcAacBaa", + 10 + ], + [ + "BaCbBCaa", + "acBCAAcA", + 11 + ], + [ + "BaCbBcbB", + "bc", + 12 + ], + [ + "BaCbbbBaa", + "CcCAcCCC", + 16 + ], + [ + "BaCc", + "Bc", + 4 + ], + [ + "BaCc", + "CCBcbAAA", + 13 + ], + [ + "BaCcBbAb", + "BcaAaBa", + 11 + ], + [ + "BaCcCcab", + "abBAcBA", + 12 + ], + [ + "BaCcCcbAc", + "BacCAaaaA", + 11 + ], + [ + "BaCcaAabB", + "BBc", + 14 + ], + [ + "BaCcaBAa", + "AaCb", + 11 + ], + [ + "BaCccaaaA", + "cBacBCA", + 12 + ], + [ + "Baa", + "A", + 5 + ], + [ + "Baa", + "AcccabBa", + 12 + ], + [ + "Baa", + "BaaBCABcB", + 12 + ], + [ + "Baa", + "CAAa", + 5 + ], + [ + "Baa", + "CBBcBcC", + 12 + ], + [ + "Baa", + "CCABBacBb", + 14 + ], + [ + "Baa", + "CbABbAcbb", + 15 + ], + [ + "Baa", + "CbCaAbc", + 10 + ], + [ + "Baa", + "bBaBbabAc", + 12 + ], + [ + "Baa", + "bBccbbaAC", + 13 + ], + [ + "Baa", + "bcAbaAACA", + 14 + ], + [ + "BaaA", + "CaBBc", + 8 + ], + [ + "BaaA", + "baBabC", + 7 + ], + [ + "BaaABa", + "bCbcaa", + 9 + ], + [ + "BaaACcAAc", + "bccAaa", + 11 + ], + [ + "BaaAaCc", + "cB", + 13 + ], + [ + "BaaB", + "C", + 8 + ], + [ + "BaaB", + "cAa", + 5 + ], + [ + "BaaBAbAb", + "baccCC", + 13 + ], + [ + "BaaBBcA", + "ccC", + 12 + ], + [ + "BaaC", + "bCb", + 7 + ], + [ + "BaaC", + "cacAcCBcC", + 13 + ], + [ + "BaaCACAaB", + "cAaCabab", + 9 + ], + [ + "BaaCBCBBc", + "ac", + 14 + ], + [ + "BaaCbAC", + "AbCAbca", + 11 + ], + [ + "BaaCcCa", + "cBACb", + 11 + ], + [ + "BaaaBcCab", + "BaBbbbAaC", + 11 + ], + [ + "BaaaC", + "accb", + 8 + ], + [ + "Baaaa", + "B", + 8 + ], + [ + "Baaaa", + "CaA", + 7 + ], + [ + "BaaababB", + "bCAcBCB", + 11 + ], + [ + "BaaacbA", + "aBCba", + 8 + ], + [ + "Baab", + "cCAAcBa", + 11 + ], + [ + "Baab", + "ccaAACcAC", + 15 + ], + [ + "BaabA", + "b", + 8 + ], + [ + "BaabAA", + "A", + 10 + ], + [ + "BaabBAcC", + "aaBCaab", + 10 + ], + [ + "BaabBcc", + "cCCAaCbc", + 12 + ], + [ + "BaabCBCC", + "CcCaB", + 14 + ], + [ + "BaabCa", + "cAaAB", + 9 + ], + [ + "BaabCb", + "CA", + 10 + ], + [ + "Baabacaa", + "bAcbccB", + 10 + ], + [ + "Baabb", + "bBbAbbA", + 7 + ], + [ + "BaabbAaA", + "BBA", + 11 + ], + [ + "BaabcAb", + "aBaC", + 10 + ], + [ + "Baac", + "AaCacBCA", + 10 + ], + [ + "Baac", + "CcBaBAb", + 9 + ], + [ + "Baac", + "a", + 6 + ], + [ + "Baac", + "aaAB", + 5 + ], + [ + "Baac", + "cCcBC", + 9 + ], + [ + "Baac", + "ccCb", + 8 + ], + [ + "BaacCABbA", + "cA", + 14 + ], + [ + "Bab", + "BaCABaCa", + 11 + ], + [ + "Bab", + "C", + 6 + ], + [ + "Bab", + "a", + 4 + ], + [ + "Bab", + "acabAacac", + 14 + ], + [ + "Bab", + "bbCaccBaB", + 13 + ], + [ + "BabA", + "a", + 6 + ], + [ + "BabA", + "cbAc", + 6 + ], + [ + "BabAAccaB", + "abb", + 13 + ], + [ + "BabACA", + "b", + 10 + ], + [ + "BabAaC", + "baCCBcC", + 9 + ], + [ + "BabAb", + "Aa", + 8 + ], + [ + "BabAbACA", + "CBAaCaaa", + 12 + ], + [ + "BabAbCA", + "CccAab", + 12 + ], + [ + "BabBBAaCa", + "AcCAccAB", + 15 + ], + [ + "BabBa", + "ABbACcCcb", + 15 + ], + [ + "BabBcA", + "ACC", + 10 + ], + [ + "BabBcBcA", + "acbCABcB", + 10 + ], + [ + "BabC", + "aABb", + 6 + ], + [ + "BabCaBca", + "AA", + 14 + ], + [ + "Baba", + "BaBCbCBB", + 10 + ], + [ + "BabaBaAA", + "AaB", + 11 + ], + [ + "BabacAbAb", + "cCaA", + 14 + ], + [ + "Babb", + "CcAa", + 8 + ], + [ + "Babb", + "aaACcbc", + 10 + ], + [ + "BabbA", + "CaCb", + 6 + ], + [ + "BabbAaaBA", + "BbCcACC", + 13 + ], + [ + "BabbAccb", + "cacBB", + 11 + ], + [ + "BabbBcBA", + "cAabCbba", + 11 + ], + [ + "BabbCBb", + "aCCcBcB", + 10 + ], + [ + "BabbCCbCA", + "AbAcBa", + 12 + ], + [ + "BabbcBAAa", + "aCacA", + 12 + ], + [ + "BabcBAbcc", + "CabA", + 12 + ], + [ + "BabcCbCA", + "cbC", + 10 + ], + [ + "BabcaC", + "ABB", + 10 + ], + [ + "Babcac", + "bcccAbBC", + 11 + ], + [ + "Bac", + "BBcaBcC", + 8 + ], + [ + "Bac", + "CbcAbAbB", + 14 + ], + [ + "Bac", + "bacBAccb", + 11 + ], + [ + "Bac", + "bbACab", + 9 + ], + [ + "BacAa", + "bBCb", + 8 + ], + [ + "BacAaaCaB", + "ccaaAb", + 10 + ], + [ + "BacAb", + "aBBaBa", + 9 + ], + [ + "BacAbA", + "caAaBbCAb", + 11 + ], + [ + "BacBBa", + "AbBBAAA", + 10 + ], + [ + "BacBCAB", + "cCBaBCc", + 10 + ], + [ + "BacBab", + "CbBABcB", + 10 + ], + [ + "BacCAaBAB", + "ccAb", + 12 + ], + [ + "BacCBBCc", + "B", + 14 + ], + [ + "BacCCaA", + "bcaBA", + 9 + ], + [ + "BacCCcbB", + "cCbaaCBB", + 12 + ], + [ + "BacCa", + "cCBBB", + 10 + ], + [ + "BacCab", + "bA", + 10 + ], + [ + "BacCabcAc", + "CCbBC", + 12 + ], + [ + "BacCccBa", + "ABCBA", + 10 + ], + [ + "BacaB", + "bbac", + 7 + ], + [ + "BacaC", + "AAC", + 6 + ], + [ + "BacaCaBB", + "bB", + 13 + ], + [ + "BacaCbA", + "caBCAa", + 9 + ], + [ + "BacbB", + "CcaACc", + 10 + ], + [ + "BacbC", + "AAabcbbCB", + 10 + ], + [ + "BacbCbBca", + "cCcCC", + 13 + ], + [ + "BacbbCaAa", + "aCACaC", + 11 + ], + [ + "Bacc", + "b", + 7 + ], + [ + "BaccACa", + "B", + 12 + ], + [ + "BaccAaacb", + "A", + 16 + ], + [ + "BaccBcCBB", + "acb", + 13 + ], + [ + "BaccCB", + "BaC", + 6 + ], + [ + "Bacca", + "Ac", + 7 + ], + [ + "BaccaCC", + "CcbA", + 11 + ], + [ + "Baccb", + "aCc", + 5 + ], + [ + "Baccba", + "cbaaBcbc", + 9 + ], + [ + "Bb", + "AAABBB", + 9 + ], + [ + "Bb", + "AAccaCAaa", + 18 + ], + [ + "Bb", + "AaA", + 6 + ], + [ + "Bb", + "AaAAc", + 10 + ], + [ + "Bb", + "B", + 2 + ], + [ + "Bb", + "BACBbABA", + 12 + ], + [ + "Bb", + "BBaBb", + 6 + ], + [ + "Bb", + "BacCBA", + 9 + ], + [ + "Bb", + "BbCBCABAC", + 14 + ], + [ + "Bb", + "Bba", + 2 + ], + [ + "Bb", + "BcBAACA", + 11 + ], + [ + "Bb", + "CC", + 4 + ], + [ + "Bb", + "CcaaBa", + 10 + ], + [ + "Bb", + "a", + 4 + ], + [ + "Bb", + "aAbbb", + 7 + ], + [ + "Bb", + "aBa", + 4 + ], + [ + "Bb", + "aBbcCbAAC", + 14 + ], + [ + "Bb", + "aCCAB", + 9 + ], + [ + "Bb", + "aCccacCB", + 15 + ], + [ + "Bb", + "aacA", + 8 + ], + [ + "Bb", + "abBaBbAa", + 12 + ], + [ + "Bb", + "b", + 2 + ], + [ + "Bb", + "bAcCbc", + 9 + ], + [ + "Bb", + "bBaCBc", + 9 + ], + [ + "Bb", + "bBbBCccc", + 12 + ], + [ + "Bb", + "bCCc", + 7 + ], + [ + "Bb", + "bCaBcC", + 10 + ], + [ + "Bb", + "bCcbbc", + 9 + ], + [ + "Bb", + "baB", + 4 + ], + [ + "Bb", + "bc", + 3 + ], + [ + "Bb", + "bcCaacCC", + 15 + ], + [ + "Bb", + "bcaBccAcb", + 14 + ], + [ + "Bb", + "cab", + 4 + ], + [ + "Bb", + "cbAACCBb", + 12 + ], + [ + "Bb", + "cbabCcbb", + 13 + ], + [ + "Bb", + "cc", + 4 + ], + [ + "BbA", + "BCaAcABab", + 14 + ], + [ + "BbA", + "C", + 6 + ], + [ + "BbA", + "aacCbAc", + 10 + ], + [ + "BbA", + "cbCcbaAAA", + 13 + ], + [ + "BbAAaA", + "AbbBccC", + 11 + ], + [ + "BbAAaA", + "bBc", + 10 + ], + [ + "BbABB", + "BBAACAa", + 9 + ], + [ + "BbABBbAa", + "acaCCbbcB", + 14 + ], + [ + "BbABC", + "c", + 9 + ], + [ + "BbABa", + "accBbBcBb", + 12 + ], + [ + "BbABaacaA", + "b", + 16 + ], + [ + "BbAC", + "CaCCBcAa", + 12 + ], + [ + "BbAC", + "caAcaab", + 11 + ], + [ + "BbACAbcB", + "aaaAB", + 11 + ], + [ + "BbAa", + "ACcA", + 7 + ], + [ + "BbAa", + "cCA", + 6 + ], + [ + "BbAaAaaa", + "cBbbcCB", + 14 + ], + [ + "BbAaCab", + "cA", + 12 + ], + [ + "BbAaa", + "Bbbcacaa", + 7 + ], + [ + "BbAac", + "b", + 8 + ], + [ + "BbAb", + "CccCbabaC", + 13 + ], + [ + "BbAbC", + "CABA", + 7 + ], + [ + "BbAbC", + "ccA", + 8 + ], + [ + "BbAbCab", + "bcabABC", + 10 + ], + [ + "BbAbCcBab", + "CAaAbcbAC", + 12 + ], + [ + "BbAbCcaAC", + "ABbcCa", + 11 + ], + [ + "BbAbbcbb", + "B", + 14 + ], + [ + "BbAbc", + "aABB", + 7 + ], + [ + "BbAcaaaa", + "bBccaac", + 8 + ], + [ + "BbAcbca", + "acCCcbBCB", + 13 + ], + [ + "BbB", + "ACbcc", + 8 + ], + [ + "BbB", + "Acaca", + 10 + ], + [ + "BbB", + "bBcb", + 5 + ], + [ + "BbBA", + "CA", + 6 + ], + [ + "BbBABCcA", + "aaC", + 13 + ], + [ + "BbBAaBa", + "b", + 12 + ], + [ + "BbBAb", + "aaCCcb", + 10 + ], + [ + "BbBAbABAc", + "cBbbBCcB", + 12 + ], + [ + "BbBBCbca", + "cbBcCbC", + 7 + ], + [ + "BbBBa", + "cC", + 10 + ], + [ + "BbBBaBBba", + "bAC", + 15 + ], + [ + "BbBBabAb", + "cbB", + 12 + ], + [ + "BbBC", + "cbbbcaA", + 9 + ], + [ + "BbBCAbb", + "baCCbBbb", + 9 + ], + [ + "BbBCbAb", + "BbCcBbbac", + 9 + ], + [ + "BbBa", + "aCaaaA", + 10 + ], + [ + "BbBa", + "caCcb", + 10 + ], + [ + "BbBaC", + "BA", + 7 + ], + [ + "BbBaCbCcB", + "baAbCbaCc", + 11 + ], + [ + "BbBaaCBbc", + "bcAAAbC", + 11 + ], + [ + "BbBacBCca", + "c", + 16 + ], + [ + "BbBbA", + "BbaaA", + 4 + ], + [ + "BbBbBAb", + "CbcCbAaCc", + 13 + ], + [ + "BbBbBa", + "A", + 11 + ], + [ + "BbBbBbC", + "cBbacbBa", + 10 + ], + [ + "BbBbbbBAb", + "AabCcBa", + 13 + ], + [ + "BbBcBc", + "bcaaC", + 9 + ], + [ + "BbBcaAC", + "cB", + 12 + ], + [ + "BbBcabAa", + "BaBAaACc", + 10 + ], + [ + "BbBcbbC", + "bCBAabbaa", + 11 + ], + [ + "BbC", + "A", + 6 + ], + [ + "BbC", + "Cc", + 5 + ], + [ + "BbC", + "aAAAc", + 9 + ], + [ + "BbC", + "aAAbca", + 9 + ], + [ + "BbC", + "bAaBabC", + 8 + ], + [ + "BbC", + "bBCcCa", + 8 + ], + [ + "BbC", + "baCACBbc", + 11 + ], + [ + "BbC", + "bcBccBcBa", + 14 + ], + [ + "BbCAACcac", + "AcCbB", + 14 + ], + [ + "BbCAACcb", + "b", + 14 + ], + [ + "BbCAaCaC", + "BBc", + 12 + ], + [ + "BbCAbc", + "aaa", + 11 + ], + [ + "BbCAcCaCC", + "CCcB", + 13 + ], + [ + "BbCAcaab", + "BCbACaABc", + 9 + ], + [ + "BbCBCB", + "bAAccbCc", + 11 + ], + [ + "BbCBCbAc", + "CcACA", + 11 + ], + [ + "BbCCAAab", + "ccCCC", + 12 + ], + [ + "BbCCABABa", + "Acaab", + 14 + ], + [ + "BbCCBBb", + "aacCb", + 9 + ], + [ + "BbCCCBCC", + "BCA", + 12 + ], + [ + "BbCCa", + "aaaCAc", + 9 + ], + [ + "BbCCcCcCc", + "AaAAA", + 18 + ], + [ + "BbCCcbc", + "BaaBCc", + 9 + ], + [ + "BbCa", + "aCC", + 6 + ], + [ + "BbCaB", + "A", + 9 + ], + [ + "BbCaBAA", + "AbAABc", + 9 + ], + [ + "BbCaCbACB", + "AAA", + 15 + ], + [ + "BbCaCcC", + "aAAcA", + 11 + ], + [ + "BbCaCcaC", + "A", + 15 + ], + [ + "BbCbC", + "bAACAAcAC", + 13 + ], + [ + "BbCbCCb", + "AbBAA", + 11 + ], + [ + "BbCbaAB", + "C", + 12 + ], + [ + "BbCbaB", + "aAcC", + 11 + ], + [ + "BbCbaBB", + "Cabb", + 8 + ], + [ + "BbCbbBc", + "b", + 12 + ], + [ + "BbCbbb", + "cCcAcBAcA", + 16 + ], + [ + "BbCbcc", + "AAACbaaAa", + 14 + ], + [ + "BbCc", + "A", + 8 + ], + [ + "BbCc", + "aac", + 6 + ], + [ + "BbCcCACB", + "cB", + 12 + ], + [ + "BbCcaBBC", + "CB", + 12 + ], + [ + "BbCccBCB", + "cCcbBb", + 9 + ], + [ + "Bba", + "ABBBcACb", + 12 + ], + [ + "Bba", + "AbAcAbCcA", + 14 + ], + [ + "Bba", + "CaBCAaaaB", + 14 + ], + [ + "Bba", + "CacBBCBA", + 12 + ], + [ + "Bba", + "aBAcbCC", + 10 + ], + [ + "Bba", + "aCCcAaAc", + 14 + ], + [ + "Bba", + "abcA", + 5 + ], + [ + "Bba", + "bcCa", + 5 + ], + [ + "BbaAB", + "BbB", + 4 + ], + [ + "BbaABacc", + "bccbc", + 11 + ], + [ + "BbaAbBCAA", + "caAcCbc", + 12 + ], + [ + "BbaAcAb", + "bacACCbC", + 9 + ], + [ + "BbaB", + "aBbAaCb", + 7 + ], + [ + "BbaB", + "bBbBAbBC", + 9 + ], + [ + "BbaBCB", + "bcA", + 9 + ], + [ + "BbaBcbacc", + "CCBAC", + 14 + ], + [ + "BbaC", + "C", + 6 + ], + [ + "BbaCBC", + "C", + 10 + ], + [ + "BbaCCCb", + "Cca", + 11 + ], + [ + "BbaCa", + "AcCC", + 8 + ], + [ + "BbaCa", + "ccbA", + 9 + ], + [ + "BbaCcBba", + "ACb", + 11 + ], + [ + "BbaCcC", + "cbCCAAbcc", + 11 + ], + [ + "Bbaa", + "abbaAA", + 6 + ], + [ + "BbaaAbAA", + "Acc", + 14 + ], + [ + "BbaaBAb", + "CBbaCbaBB", + 9 + ], + [ + "BbaacA", + "ccAbCabAa", + 12 + ], + [ + "BbaacBcbC", + "cabbCaC", + 12 + ], + [ + "Bbab", + "BCBb", + 4 + ], + [ + "BbabA", + "Cbc", + 8 + ], + [ + "BbabBc", + "AcA", + 11 + ], + [ + "BbabC", + "ab", + 6 + ], + [ + "BbabCcBA", + "AabACBc", + 9 + ], + [ + "Bbaba", + "AAa", + 7 + ], + [ + "BbabbBb", + "b", + 12 + ], + [ + "Bbac", + "bbb", + 5 + ], + [ + "BbacB", + "CBcBB", + 7 + ], + [ + "BbacCc", + "Cb", + 10 + ], + [ + "Bbacaa", + "AaAaac", + 8 + ], + [ + "Bbb", + "ACCCCc", + 12 + ], + [ + "Bbb", + "BCABAa", + 9 + ], + [ + "Bbb", + "BabbbBcCA", + 12 + ], + [ + "Bbb", + "acaCCaa", + 14 + ], + [ + "Bbb", + "bAAcBa", + 10 + ], + [ + "Bbb", + "ccacA", + 10 + ], + [ + "BbbA", + "ACccA", + 8 + ], + [ + "BbbAAB", + "baBAb", + 7 + ], + [ + "BbbABccc", + "AbaABca", + 8 + ], + [ + "BbbAC", + "bcAaBBBca", + 14 + ], + [ + "BbbAaAba", + "a", + 14 + ], + [ + "BbbB", + "Bcc", + 6 + ], + [ + "BbbBCCA", + "cBBb", + 11 + ], + [ + "BbbBCba", + "BA", + 11 + ], + [ + "BbbBcaa", + "b", + 12 + ], + [ + "BbbCAaA", + "cbbBaBaCA", + 9 + ], + [ + "BbbCAb", + "AACC", + 10 + ], + [ + "BbbCAc", + "cbaB", + 9 + ], + [ + "BbbCBcac", + "bAbaBCba", + 10 + ], + [ + "BbbCCB", + "C", + 10 + ], + [ + "BbbCCBAc", + "cbbBcCCAC", + 8 + ], + [ + "Bbba", + "AAaAB", + 9 + ], + [ + "Bbba", + "abAc", + 6 + ], + [ + "BbbaAC", + "aBABcAAaB", + 12 + ], + [ + "BbbaC", + "AbBaaAaA", + 11 + ], + [ + "BbbaabCCA", + "CbcAb", + 13 + ], + [ + "BbbacCbb", + "AaccA", + 11 + ], + [ + "BbbbCAaA", + "cACCaCcAC", + 15 + ], + [ + "BbbbbAc", + "AbbcCb", + 10 + ], + [ + "BbbbcA", + "AaCcBabb", + 14 + ], + [ + "BbbbcbbA", + "bAA", + 12 + ], + [ + "Bbbc", + "CbccaAc", + 10 + ], + [ + "BbbcABb", + "bbbcc", + 7 + ], + [ + "BbbcB", + "bbacbbc", + 8 + ], + [ + "BbbcbAcbB", + "aCaCbaAc", + 13 + ], + [ + "Bbbcbcab", + "caCaAAbc", + 14 + ], + [ + "BbcA", + "CBaA", + 5 + ], + [ + "BbcA", + "CBcAB", + 5 + ], + [ + "BbcABcB", + "AcA", + 10 + ], + [ + "BbcACB", + "BaaCCBBC", + 10 + ], + [ + "BbcACCcB", + "bcbbaaBba", + 14 + ], + [ + "BbcAbCa", + "CbbaCba", + 9 + ], + [ + "BbcAcaB", + "Aca", + 8 + ], + [ + "BbcAccCab", + "abCCaA", + 11 + ], + [ + "BbcB", + "aACCccCaA", + 16 + ], + [ + "BbcBbB", + "baacAca", + 11 + ], + [ + "BbcBbbaC", + "Ab", + 14 + ], + [ + "BbcC", + "AccAcaBaC", + 14 + ], + [ + "BbcCB", + "bBca", + 6 + ], + [ + "BbcCBaB", + "AcCcaCbb", + 11 + ], + [ + "BbcCaab", + "cACaC", + 10 + ], + [ + "BbcaBbCCb", + "acBBcc", + 11 + ], + [ + "BbcaCcaa", + "AbcccaBB", + 9 + ], + [ + "BbcaaACb", + "A", + 14 + ], + [ + "BbcbACAa", + "a", + 14 + ], + [ + "BbcbBCCA", + "AcaCB", + 12 + ], + [ + "BbcbCcCCc", + "a", + 18 + ], + [ + "Bbcbaa", + "a", + 10 + ], + [ + "BbcbbbBab", + "aaBcBcCB", + 14 + ], + [ + "BbcbccB", + "bAACb", + 10 + ], + [ + "Bbcc", + "caAACBBb", + 15 + ], + [ + "BbccBcb", + "BCAbccb", + 8 + ], + [ + "BbccC", + "CaaAC", + 8 + ], + [ + "Bbcca", + "bBcb", + 6 + ], + [ + "BbccccCC", + "aAbcc", + 12 + ], + [ + "Bc", + "A", + 4 + ], + [ + "Bc", + "AB", + 4 + ], + [ + "Bc", + "ABAccAcC", + 12 + ], + [ + "Bc", + "ABBbBBbb", + 14 + ], + [ + "Bc", + "AaA", + 6 + ], + [ + "Bc", + "AaaBaCcCc", + 14 + ], + [ + "Bc", + "AabBBC", + 9 + ], + [ + "Bc", + "Aac", + 4 + ], + [ + "Bc", + "Aba", + 5 + ], + [ + "Bc", + "Abccbabbc", + 15 + ], + [ + "Bc", + "AcAbBC", + 9 + ], + [ + "Bc", + "B", + 2 + ], + [ + "Bc", + "BACaACBC", + 13 + ], + [ + "Bc", + "BBcBCbCB", + 12 + ], + [ + "Bc", + "BcBAAAaA", + 12 + ], + [ + "Bc", + "BcaaABCcc", + 14 + ], + [ + "Bc", + "C", + 3 + ], + [ + "Bc", + "CBCccCbaA", + 14 + ], + [ + "Bc", + "CCaBABaBA", + 16 + ], + [ + "Bc", + "CaBCBbBbc", + 14 + ], + [ + "Bc", + "CbaBCbb", + 11 + ], + [ + "Bc", + "a", + 4 + ], + [ + "Bc", + "aC", + 3 + ], + [ + "Bc", + "abAbBBcc", + 12 + ], + [ + "Bc", + "abaCAABA", + 14 + ], + [ + "Bc", + "abaabAbc", + 13 + ], + [ + "Bc", + "abcbBBB", + 11 + ], + [ + "Bc", + "ac", + 2 + ], + [ + "Bc", + "acCCcb", + 10 + ], + [ + "Bc", + "acCacaaAa", + 16 + ], + [ + "Bc", + "ba", + 3 + ], + [ + "Bc", + "cC", + 3 + ], + [ + "Bc", + "cb", + 4 + ], + [ + "Bc", + "cbACCabca", + 15 + ], + [ + "BcA", + "AC", + 5 + ], + [ + "BcA", + "BAaB", + 5 + ], + [ + "BcA", + "BBc", + 4 + ], + [ + "BcA", + "CBababB", + 11 + ], + [ + "BcA", + "CcB", + 4 + ], + [ + "BcA", + "CcCb", + 6 + ], + [ + "BcA", + "a", + 5 + ], + [ + "BcA", + "bAcab", + 6 + ], + [ + "BcA", + "bBAbb", + 7 + ], + [ + "BcA", + "bbbc", + 7 + ], + [ + "BcA", + "c", + 4 + ], + [ + "BcAABaB", + "bACcAc", + 10 + ], + [ + "BcAAaA", + "BcCAbaBb", + 8 + ], + [ + "BcAB", + "aBCCcACc", + 10 + ], + [ + "BcABAc", + "BaAa", + 7 + ], + [ + "BcABaAaA", + "aCCaCcb", + 13 + ], + [ + "BcABaa", + "CaaBBaa", + 7 + ], + [ + "BcAC", + "BCAbcbCAa", + 11 + ], + [ + "BcACAA", + "aCb", + 9 + ], + [ + "BcACB", + "bCcC", + 6 + ], + [ + "BcACBaBCa", + "BCcBAbACc", + 10 + ], + [ + "BcACC", + "aa", + 9 + ], + [ + "BcAa", + "Cb", + 7 + ], + [ + "BcAaA", + "CBBc", + 9 + ], + [ + "BcAaA", + "abBbBBcB", + 14 + ], + [ + "BcAaACA", + "cbCABAaCb", + 10 + ], + [ + "BcAaCCBB", + "cBbCaBbA", + 11 + ], + [ + "BcAaCbB", + "ACB", + 8 + ], + [ + "BcAaaaC", + "cC", + 10 + ], + [ + "BcAb", + "BB", + 5 + ], + [ + "BcAb", + "CC", + 7 + ], + [ + "BcAbAbCac", + "bab", + 13 + ], + [ + "BcAbB", + "B", + 8 + ], + [ + "BcAbabBA", + "acb", + 12 + ], + [ + "BcAbabBBB", + "BA", + 14 + ], + [ + "BcAbabC", + "BBbaCAB", + 10 + ], + [ + "BcAbbcC", + "AaAcbC", + 8 + ], + [ + "BcAbc", + "BAC", + 5 + ], + [ + "BcAbc", + "BbbcbBabB", + 11 + ], + [ + "BcAbcAAC", + "A", + 14 + ], + [ + "BcAc", + "bC", + 6 + ], + [ + "BcAcAaACb", + "aAbBBB", + 14 + ], + [ + "BcAcB", + "caaAbbccB", + 12 + ], + [ + "BcAcaccb", + "cbAbAb", + 11 + ], + [ + "BcAcba", + "aA", + 10 + ], + [ + "BcAcc", + "BCaCBcAcC", + 9 + ], + [ + "BcB", + "AcCCAC", + 10 + ], + [ + "BcB", + "BBAcaAaB", + 10 + ], + [ + "BcB", + "BabAcacCA", + 14 + ], + [ + "BcB", + "aaBcAb", + 7 + ], + [ + "BcB", + "abcC", + 5 + ], + [ + "BcB", + "bcBBCbab", + 11 + ], + [ + "BcB", + "cAAc", + 8 + ], + [ + "BcB", + "ca", + 4 + ], + [ + "BcB", + "caBaaAAb", + 13 + ], + [ + "BcBA", + "CAabC", + 9 + ], + [ + "BcBA", + "baaC", + 7 + ], + [ + "BcBAAc", + "cBBaaabBB", + 14 + ], + [ + "BcBAB", + "aCaAaBb", + 9 + ], + [ + "BcBABCB", + "aaB", + 11 + ], + [ + "BcBAccAc", + "c", + 14 + ], + [ + "BcBBBA", + "ACc", + 11 + ], + [ + "BcBBC", + "cCABb", + 7 + ], + [ + "BcBBbb", + "bbBAa", + 8 + ], + [ + "BcBBbcAaA", + "CA", + 15 + ], + [ + "BcBC", + "c", + 6 + ], + [ + "BcBCA", + "cAaCC", + 8 + ], + [ + "BcBCAAAcc", + "abc", + 15 + ], + [ + "BcBCBBC", + "BbC", + 9 + ], + [ + "BcBCbAc", + "cBBCaC", + 8 + ], + [ + "BcBCcbccc", + "BcBC", + 10 + ], + [ + "BcBCccAa", + "acb", + 13 + ], + [ + "BcBa", + "aCB", + 5 + ], + [ + "BcBaAcAAc", + "bccBBBcBc", + 11 + ], + [ + "BcBaC", + "acBbcABAa", + 11 + ], + [ + "BcBab", + "CbCACC", + 10 + ], + [ + "BcBb", + "acAABAA", + 10 + ], + [ + "BcBbA", + "Ccba", + 5 + ], + [ + "BcBbAbABB", + "a", + 17 + ], + [ + "BcBbbCAa", + "baACcBCcC", + 14 + ], + [ + "BcBbbCa", + "AcCBAa", + 9 + ], + [ + "BcBbc", + "CcABb", + 6 + ], + [ + "BcBc", + "cBBcc", + 6 + ], + [ + "BcBcAbbc", + "babC", + 11 + ], + [ + "BcBcBc", + "cAAc", + 8 + ], + [ + "BcC", + "ABabcbC", + 8 + ], + [ + "BcC", + "Ac", + 4 + ], + [ + "BcC", + "C", + 4 + ], + [ + "BcC", + "acbcaAaa", + 13 + ], + [ + "BcC", + "bAbB", + 7 + ], + [ + "BcCA", + "ccaBaaAac", + 14 + ], + [ + "BcCAABBB", + "BCabBB", + 6 + ], + [ + "BcCAaAC", + "acCaB", + 8 + ], + [ + "BcCAaBc", + "Bcab", + 7 + ], + [ + "BcCAbB", + "ACaccCB", + 10 + ], + [ + "BcCAbaACc", + "aCAAcCBC", + 12 + ], + [ + "BcCAbcbA", + "bccAbCC", + 7 + ], + [ + "BcCAcA", + "BbB", + 10 + ], + [ + "BcCBABA", + "accb", + 10 + ], + [ + "BcCBAaC", + "b", + 13 + ], + [ + "BcCBbabcc", + "cCb", + 12 + ], + [ + "BcCBc", + "ba", + 9 + ], + [ + "BcCBcaAcc", + "B", + 16 + ], + [ + "BcCBcaCbC", + "CA", + 15 + ], + [ + "BcCCAA", + "caaA", + 7 + ], + [ + "BcCCBaaa", + "AaaCBb", + 12 + ], + [ + "BcCCCAA", + "b", + 13 + ], + [ + "BcCCaaaA", + "BccaAabBa", + 9 + ], + [ + "BcCCbCac", + "AcAa", + 12 + ], + [ + "BcCa", + "AB", + 8 + ], + [ + "BcCa", + "cB", + 6 + ], + [ + "BcCaACC", + "cbACaA", + 9 + ], + [ + "BcCaACCc", + "aaaCb", + 11 + ], + [ + "BcCaBbCAC", + "AbaAc", + 12 + ], + [ + "BcCaBc", + "CCAAB", + 8 + ], + [ + "BcCaCacc", + "CaCbbbc", + 10 + ], + [ + "BcCaaC", + "AcC", + 8 + ], + [ + "BcCb", + "aBacB", + 6 + ], + [ + "BcCbAcCb", + "CAA", + 12 + ], + [ + "BcCbCCCa", + "CBCC", + 9 + ], + [ + "BcCbaBabc", + "abcbB", + 13 + ], + [ + "BcCbbb", + "ccBCb", + 6 + ], + [ + "BcCc", + "bBb", + 7 + ], + [ + "BcCcBBb", + "aCCC", + 10 + ], + [ + "BcCcBcAb", + "aacbBbC", + 13 + ], + [ + "BcCca", + "CCcCaaB", + 8 + ], + [ + "Bca", + "AcbCBab", + 10 + ], + [ + "Bca", + "Bb", + 4 + ], + [ + "Bca", + "Bcc", + 2 + ], + [ + "Bca", + "CAAbA", + 9 + ], + [ + "Bca", + "CcBc", + 6 + ], + [ + "Bca", + "aAAccCBB", + 14 + ], + [ + "Bca", + "aCbbBBcCb", + 14 + ], + [ + "Bca", + "ac", + 4 + ], + [ + "Bca", + "c", + 4 + ], + [ + "BcaAAbACa", + "cAaacBB", + 13 + ], + [ + "BcaAaC", + "B", + 10 + ], + [ + "BcaB", + "A", + 7 + ], + [ + "BcaB", + "ABC", + 7 + ], + [ + "BcaB", + "AcAacA", + 8 + ], + [ + "BcaB", + "CAba", + 7 + ], + [ + "BcaB", + "cc", + 6 + ], + [ + "BcaBACC", + "aabAcbb", + 10 + ], + [ + "BcaBACbA", + "cB", + 12 + ], + [ + "BcaBAc", + "CAcBaCC", + 10 + ], + [ + "BcaBb", + "bABA", + 6 + ], + [ + "BcaBcab", + "aAaA", + 10 + ], + [ + "BcaC", + "abbAb", + 8 + ], + [ + "BcaC", + "ba", + 5 + ], + [ + "BcaC", + "ccaCBCB", + 8 + ], + [ + "BcaCBAbA", + "BA", + 12 + ], + [ + "BcaCC", + "CAbBbB", + 12 + ], + [ + "BcaCCbb", + "bbb", + 9 + ], + [ + "BcaCc", + "AaCABAACA", + 13 + ], + [ + "Bcaa", + "C", + 7 + ], + [ + "Bcaa", + "CbBABaa", + 8 + ], + [ + "Bcaa", + "cbbBcc", + 10 + ], + [ + "BcaaAc", + "AcaCCb", + 8 + ], + [ + "BcaaBBa", + "bBBCAAb", + 12 + ], + [ + "BcaaBcB", + "cCBbbAAcA", + 13 + ], + [ + "Bcaaba", + "ACbcA", + 10 + ], + [ + "Bcaabc", + "Bba", + 8 + ], + [ + "BcaacbcC", + "CBBBbCCA", + 12 + ], + [ + "Bcab", + "AcABabB", + 8 + ], + [ + "Bcab", + "abA", + 6 + ], + [ + "BcabB", + "BcbCc", + 6 + ], + [ + "BcabBB", + "BbAbbbA", + 7 + ], + [ + "BcabCA", + "b", + 10 + ], + [ + "BcabCac", + "BbAAaca", + 9 + ], + [ + "Bcaba", + "aCcbA", + 6 + ], + [ + "BcabaABA", + "CAbB", + 10 + ], + [ + "Bcac", + "CcCAbaaa", + 12 + ], + [ + "Bcac", + "ccc", + 4 + ], + [ + "BcacAC", + "abBc", + 9 + ], + [ + "BcacAaca", + "acbAbc", + 10 + ], + [ + "BcacCB", + "cbabacC", + 9 + ], + [ + "BcacaBC", + "ACCac", + 9 + ], + [ + "BcacabbA", + "Bcb", + 10 + ], + [ + "Bcacc", + "b", + 9 + ], + [ + "Bcb", + "AaAcacaB", + 13 + ], + [ + "Bcb", + "Cc", + 4 + ], + [ + "Bcb", + "aBBC", + 6 + ], + [ + "BcbAACc", + "aBBcA", + 11 + ], + [ + "BcbAAb", + "ab", + 9 + ], + [ + "BcbAC", + "AcC", + 6 + ], + [ + "BcbAaaacc", + "cc", + 14 + ], + [ + "BcbB", + "BbcbBbBC", + 8 + ], + [ + "BcbB", + "aB", + 6 + ], + [ + "BcbBcB", + "bCbb", + 7 + ], + [ + "BcbCA", + "bCc", + 6 + ], + [ + "BcbCAAba", + "CCaa", + 10 + ], + [ + "BcbCBCA", + "BcBa", + 7 + ], + [ + "BcbCC", + "b", + 8 + ], + [ + "BcbCCcc", + "Abb", + 12 + ], + [ + "BcbCabC", + "A", + 13 + ], + [ + "BcbCcB", + "cbaccCa", + 9 + ], + [ + "Bcba", + "ACa", + 5 + ], + [ + "Bcbb", + "baaAa", + 9 + ], + [ + "BcbbA", + "BBb", + 5 + ], + [ + "BcbbC", + "ccaBAABCC", + 12 + ], + [ + "BcbcA", + "bcBcAAcB", + 8 + ], + [ + "BcbcB", + "Ccc", + 6 + ], + [ + "Bcc", + "Ab", + 6 + ], + [ + "Bcc", + "BCAbacBc", + 10 + ], + [ + "Bcc", + "aacb", + 6 + ], + [ + "Bcc", + "baAa", + 7 + ], + [ + "BccABAbcA", + "Aba", + 13 + ], + [ + "BccACA", + "aa", + 10 + ], + [ + "BccAcAbbC", + "caC", + 13 + ], + [ + "BccBA", + "BACCAC", + 7 + ], + [ + "BccBAAbb", + "aac", + 14 + ], + [ + "BccBCbAA", + "BBCCC", + 10 + ], + [ + "BccBaAaa", + "AACAbbbbB", + 16 + ], + [ + "BccBac", + "CCabACa", + 10 + ], + [ + "BccC", + "ac", + 6 + ], + [ + "BccC", + "bCA", + 6 + ], + [ + "BccCa", + "caaBBaaB", + 14 + ], + [ + "BccCab", + "Cbcbac", + 8 + ], + [ + "BccCacBC", + "BBbA", + 13 + ], + [ + "BccCcA", + "BcCBCAB", + 6 + ], + [ + "Bcca", + "AAb", + 8 + ], + [ + "BccaA", + "cCC", + 7 + ], + [ + "BccaA", + "ccc", + 6 + ], + [ + "BccaBbB", + "AA", + 13 + ], + [ + "Bccac", + "CcbaCCa", + 9 + ], + [ + "Bccb", + "AcbC", + 6 + ], + [ + "BccbB", + "bbccA", + 7 + ], + [ + "BccbBCAA", + "AA", + 12 + ], + [ + "BccbBaCbb", + "CbcBBCAA", + 11 + ], + [ + "BccbacAAc", + "CAac", + 12 + ], + [ + "BccbbACa", + "acCAbCAc", + 10 + ], + [ + "BcccAC", + "cccBbBaac", + 12 + ], + [ + "BcccBB", + "bABCc", + 10 + ], + [ + "BcccBBb", + "Cbacbaa", + 11 + ], + [ + "C", + "A", + 2 + ], + [ + "C", + "AAA", + 6 + ], + [ + "C", + "AAAb", + 8 + ], + [ + "C", + "AAB", + 6 + ], + [ + "C", + "AAacba", + 11 + ], + [ + "C", + "AAbb", + 8 + ], + [ + "C", + "ABA", + 6 + ], + [ + "C", + "ABAB", + 8 + ], + [ + "C", + "ABAcBAabA", + 17 + ], + [ + "C", + "ABCB", + 6 + ], + [ + "C", + "ABCbbbBAC", + 16 + ], + [ + "C", + "ABabc", + 9 + ], + [ + "C", + "ACCCcAcAa", + 16 + ], + [ + "C", + "ACc", + 4 + ], + [ + "C", + "ACcbaABc", + 14 + ], + [ + "C", + "AaAAa", + 10 + ], + [ + "C", + "AaAa", + 8 + ], + [ + "C", + "AaAbcaBb", + 15 + ], + [ + "C", + "AabbbC", + 10 + ], + [ + "C", + "AacCCABaB", + 16 + ], + [ + "C", + "AacaaaB", + 13 + ], + [ + "C", + "Ab", + 4 + ], + [ + "C", + "AbBbBACab", + 16 + ], + [ + "C", + "AbBcCcBAb", + 16 + ], + [ + "C", + "AbC", + 4 + ], + [ + "C", + "AbCCaCbc", + 14 + ], + [ + "C", + "Abb", + 6 + ], + [ + "C", + "AbbCcBb", + 12 + ], + [ + "C", + "Acbc", + 7 + ], + [ + "C", + "B", + 2 + ], + [ + "C", + "BAAbAbCa", + 14 + ], + [ + "C", + "BAAc", + 7 + ], + [ + "C", + "BACcBbAcB", + 16 + ], + [ + "C", + "BBBaAcB", + 13 + ], + [ + "C", + "BBacaaA", + 13 + ], + [ + "C", + "BC", + 2 + ], + [ + "C", + "BCCbA", + 8 + ], + [ + "C", + "BCba", + 6 + ], + [ + "C", + "Ba", + 4 + ], + [ + "C", + "BaA", + 6 + ], + [ + "C", + "BaBCCBcb", + 14 + ], + [ + "C", + "BaBcbbCBA", + 16 + ], + [ + "C", + "BaaC", + 6 + ], + [ + "C", + "BabbcCBa", + 14 + ], + [ + "C", + "BacccCB", + 12 + ], + [ + "C", + "BbABc", + 9 + ], + [ + "C", + "BbAaAAC", + 12 + ], + [ + "C", + "BbBBcc", + 11 + ], + [ + "C", + "BbCBCB", + 10 + ], + [ + "C", + "BbcCCbAC", + 14 + ], + [ + "C", + "BcB", + 5 + ], + [ + "C", + "BcBBBbAaa", + 17 + ], + [ + "C", + "BcCAcacAB", + 16 + ], + [ + "C", + "BcCB", + 6 + ], + [ + "C", + "BcacCaB", + 12 + ], + [ + "C", + "BccAaa", + 11 + ], + [ + "C", + "BccaAa", + 11 + ], + [ + "C", + "C", + 0 + ], + [ + "C", + "CA", + 2 + ], + [ + "C", + "CAAbCa", + 10 + ], + [ + "C", + "CABBB", + 8 + ], + [ + "C", + "CAaAa", + 8 + ], + [ + "C", + "CAbAABb", + 12 + ], + [ + "C", + "CBBCaABbB", + 16 + ], + [ + "C", + "CBaacB", + 10 + ], + [ + "C", + "CBc", + 4 + ], + [ + "C", + "CC", + 2 + ], + [ + "C", + "CCA", + 4 + ], + [ + "C", + "CCBbccA", + 12 + ], + [ + "C", + "CCc", + 4 + ], + [ + "C", + "CCcbbCBCc", + 16 + ], + [ + "C", + "CCcc", + 6 + ], + [ + "C", + "CCccCaCa", + 14 + ], + [ + "C", + "Ca", + 2 + ], + [ + "C", + "CaA", + 4 + ], + [ + "C", + "Caab", + 6 + ], + [ + "C", + "Cacaacba", + 14 + ], + [ + "C", + "Cb", + 2 + ], + [ + "C", + "CbaCBcAb", + 14 + ], + [ + "C", + "CbbBCbC", + 12 + ], + [ + "C", + "Cc", + 2 + ], + [ + "C", + "CcAcCA", + 10 + ], + [ + "C", + "CcBBCbBB", + 14 + ], + [ + "C", + "CcCBBbB", + 12 + ], + [ + "C", + "a", + 2 + ], + [ + "C", + "aA", + 4 + ], + [ + "C", + "aAABBb", + 12 + ], + [ + "C", + "aACBaca", + 12 + ], + [ + "C", + "aAaCB", + 8 + ], + [ + "C", + "aAaaBcaB", + 15 + ], + [ + "C", + "aAabb", + 10 + ], + [ + "C", + "aAccAABB", + 15 + ], + [ + "C", + "aBAaa", + 10 + ], + [ + "C", + "aBbab", + 10 + ], + [ + "C", + "aCBccCbBc", + 16 + ], + [ + "C", + "aCCBcbcBc", + 16 + ], + [ + "C", + "aCCC", + 6 + ], + [ + "C", + "aCcB", + 6 + ], + [ + "C", + "aCcccA", + 10 + ], + [ + "C", + "aa", + 4 + ], + [ + "C", + "aaBA", + 8 + ], + [ + "C", + "aaBAbA", + 12 + ], + [ + "C", + "aaBabBB", + 14 + ], + [ + "C", + "aaCAC", + 8 + ], + [ + "C", + "aaCBb", + 8 + ], + [ + "C", + "aaaBcBBc", + 15 + ], + [ + "C", + "aaab", + 8 + ], + [ + "C", + "aab", + 6 + ], + [ + "C", + "aacCCabb", + 14 + ], + [ + "C", + "abBbAcaCc", + 16 + ], + [ + "C", + "abbbAaaC", + 14 + ], + [ + "C", + "abcBBB", + 11 + ], + [ + "C", + "ac", + 3 + ], + [ + "C", + "b", + 2 + ], + [ + "C", + "bAAAccb", + 13 + ], + [ + "C", + "bAAaCBaCa", + 16 + ], + [ + "C", + "bAaABACCb", + 16 + ], + [ + "C", + "bAbAcCAc", + 14 + ], + [ + "C", + "bBABA", + 10 + ], + [ + "C", + "bBB", + 6 + ], + [ + "C", + "bBacA", + 9 + ], + [ + "C", + "bBcbbCabb", + 16 + ], + [ + "C", + "bC", + 2 + ], + [ + "C", + "bCcAcACba", + 16 + ], + [ + "C", + "ba", + 4 + ], + [ + "C", + "baAA", + 8 + ], + [ + "C", + "baAba", + 10 + ], + [ + "C", + "baCABab", + 12 + ], + [ + "C", + "baaA", + 8 + ], + [ + "C", + "baaB", + 8 + ], + [ + "C", + "baaCbCb", + 12 + ], + [ + "C", + "bbABBCA", + 12 + ], + [ + "C", + "bbABcCb", + 12 + ], + [ + "C", + "bbCab", + 8 + ], + [ + "C", + "bbacCaabC", + 16 + ], + [ + "C", + "bbbcaAb", + 13 + ], + [ + "C", + "bc", + 3 + ], + [ + "C", + "bcAca", + 9 + ], + [ + "C", + "bcBAABAAC", + 16 + ], + [ + "C", + "bcBcA", + 9 + ], + [ + "C", + "bcC", + 4 + ], + [ + "C", + "bcCAb", + 8 + ], + [ + "C", + "bcaAb", + 9 + ], + [ + "C", + "bcacBAC", + 12 + ], + [ + "C", + "bcb", + 5 + ], + [ + "C", + "bcbBba", + 11 + ], + [ + "C", + "bcbacaAA", + 15 + ], + [ + "C", + "bccC", + 6 + ], + [ + "C", + "bccccCA", + 12 + ], + [ + "C", + "c", + 1 + ], + [ + "C", + "cAc", + 5 + ], + [ + "C", + "cAcBCbCb", + 14 + ], + [ + "C", + "cAcaABCA", + 14 + ], + [ + "C", + "cBAA", + 7 + ], + [ + "C", + "cBBB", + 7 + ], + [ + "C", + "cBc", + 5 + ], + [ + "C", + "cBccbAA", + 13 + ], + [ + "C", + "cC", + 2 + ], + [ + "C", + "cCBAAB", + 10 + ], + [ + "C", + "cCaACAAC", + 14 + ], + [ + "C", + "cCbA", + 6 + ], + [ + "C", + "cCbbABa", + 12 + ], + [ + "C", + "cCcabCbBb", + 16 + ], + [ + "C", + "ca", + 3 + ], + [ + "C", + "caCACCCaB", + 16 + ], + [ + "C", + "cabCa", + 8 + ], + [ + "C", + "cacACaBB", + 14 + ], + [ + "C", + "cacBaACc", + 14 + ], + [ + "C", + "cbACabc", + 12 + ], + [ + "C", + "cbCCB", + 8 + ], + [ + "C", + "cbcC", + 6 + ], + [ + "C", + "cc", + 3 + ], + [ + "C", + "ccBAbBBC", + 14 + ], + [ + "C", + "ccBaC", + 8 + ], + [ + "C", + "ccaCbbCbb", + 16 + ], + [ + "C", + "ccabC", + 8 + ], + [ + "C", + "ccbBbCCc", + 14 + ], + [ + "CA", + "A", + 2 + ], + [ + "CA", + "AAa", + 4 + ], + [ + "CA", + "Aaacbb", + 11 + ], + [ + "CA", + "AcC", + 5 + ], + [ + "CA", + "BBaB", + 7 + ], + [ + "CA", + "BcAcbBC", + 11 + ], + [ + "CA", + "C", + 2 + ], + [ + "CA", + "CBCAa", + 6 + ], + [ + "CA", + "CacbbBaa", + 13 + ], + [ + "CA", + "CcBcBAB", + 10 + ], + [ + "CA", + "CcbCaBB", + 11 + ], + [ + "CA", + "a", + 3 + ], + [ + "CA", + "aAABAaC", + 12 + ], + [ + "CA", + "aABBa", + 8 + ], + [ + "CA", + "abbCC", + 8 + ], + [ + "CA", + "acbb", + 7 + ], + [ + "CA", + "bAbAAcbA", + 13 + ], + [ + "CA", + "bAcBcACC", + 13 + ], + [ + "CA", + "bCBaBBaB", + 13 + ], + [ + "CA", + "bbbB", + 8 + ], + [ + "CA", + "cAaaaA", + 9 + ], + [ + "CA", + "caCBCcaaA", + 14 + ], + [ + "CAA", + "CCbc", + 6 + ], + [ + "CAA", + "CbCcba", + 9 + ], + [ + "CAA", + "bb", + 6 + ], + [ + "CAA", + "c", + 5 + ], + [ + "CAA", + "cACAaa", + 7 + ], + [ + "CAA", + "cAaabbacA", + 13 + ], + [ + "CAAA", + "bb", + 8 + ], + [ + "CAAAAAcCB", + "bC", + 16 + ], + [ + "CAAAB", + "ba", + 9 + ], + [ + "CAAABaBAc", + "CaaC", + 12 + ], + [ + "CAAACa", + "AAcbAAc", + 9 + ], + [ + "CAAACaAC", + "aABBcbcBa", + 15 + ], + [ + "CAAAaBAa", + "BCBc", + 14 + ], + [ + "CAAAba", + "CacBaB", + 8 + ], + [ + "CAAAbb", + "cCaCb", + 8 + ], + [ + "CAAB", + "CbcCbBBA", + 12 + ], + [ + "CAABAAcBC", + "bccb", + 14 + ], + [ + "CAABAAcbB", + "ABBa", + 13 + ], + [ + "CAABCaaBa", + "aBCACcBca", + 12 + ], + [ + "CAABCcAAa", + "ccCB", + 15 + ], + [ + "CAABbaA", + "AbBaAac", + 10 + ], + [ + "CAAC", + "cacABc", + 7 + ], + [ + "CAACAAC", + "Ab", + 12 + ], + [ + "CAACAbca", + "Bb", + 14 + ], + [ + "CAACCAc", + "CAAB", + 8 + ], + [ + "CAACb", + "CA", + 6 + ], + [ + "CAAa", + "cBCC", + 7 + ], + [ + "CAAaBaAC", + "AaccBBB", + 13 + ], + [ + "CAAaaA", + "CaAaACca", + 7 + ], + [ + "CAAab", + "b", + 8 + ], + [ + "CAAabac", + "Accbbc", + 8 + ], + [ + "CAAbCbCcC", + "B", + 17 + ], + [ + "CAAbb", + "caC", + 8 + ], + [ + "CAAbbAbb", + "BC", + 15 + ], + [ + "CAAcBB", + "aCBCbAAb", + 13 + ], + [ + "CAAccAcC", + "abA", + 13 + ], + [ + "CAB", + "ABabCcc", + 12 + ], + [ + "CAB", + "ABccbC", + 10 + ], + [ + "CAB", + "CABba", + 4 + ], + [ + "CAB", + "CcbB", + 4 + ], + [ + "CAB", + "aababa", + 10 + ], + [ + "CAB", + "bC", + 6 + ], + [ + "CAB", + "cAbbABAac", + 13 + ], + [ + "CABA", + "CaabCCc", + 10 + ], + [ + "CABAAa", + "abC", + 10 + ], + [ + "CABACacA", + "cAbaaABC", + 10 + ], + [ + "CABACcA", + "B", + 12 + ], + [ + "CABAa", + "aac", + 8 + ], + [ + "CABAcBa", + "b", + 13 + ], + [ + "CABBAa", + "bbBcabcC", + 13 + ], + [ + "CABBBAa", + "B", + 12 + ], + [ + "CABBBCaa", + "ACb", + 12 + ], + [ + "CABBBb", + "cacaAACa", + 14 + ], + [ + "CABC", + "cCbacA", + 9 + ], + [ + "CABCAcaC", + "B", + 14 + ], + [ + "CABa", + "B", + 6 + ], + [ + "CABaCc", + "BCaaaAAcb", + 11 + ], + [ + "CABab", + "baB", + 6 + ], + [ + "CABacBA", + "BaACb", + 10 + ], + [ + "CABb", + "bCcBaCba", + 10 + ], + [ + "CABbABcBA", + "aBaAccabA", + 10 + ], + [ + "CABbbC", + "CbACCAcc", + 11 + ], + [ + "CABcAaAb", + "bb", + 13 + ], + [ + "CABcaa", + "C", + 10 + ], + [ + "CABcaa", + "aCaBABAab", + 10 + ], + [ + "CABcabCAa", + "AcbaBcCA", + 11 + ], + [ + "CABcbCAc", + "ABAcc", + 9 + ], + [ + "CABcbbA", + "BBbcA", + 8 + ], + [ + "CABcbcacA", + "cb", + 14 + ], + [ + "CAC", + "aCAAaABb", + 12 + ], + [ + "CAC", + "abCCACaBc", + 12 + ], + [ + "CAC", + "bbcbB", + 9 + ], + [ + "CAC", + "caBAa", + 7 + ], + [ + "CAC", + "ccBaBcb", + 11 + ], + [ + "CACAACccb", + "bc", + 16 + ], + [ + "CACABBA", + "cCACAcCcC", + 10 + ], + [ + "CACACAcBB", + "aBAACCc", + 12 + ], + [ + "CACAabc", + "cbAaBbbbB", + 13 + ], + [ + "CACAcaAC", + "ACcACc", + 8 + ], + [ + "CACAcbC", + "Cc", + 10 + ], + [ + "CACB", + "Cbc", + 5 + ], + [ + "CACBb", + "C", + 8 + ], + [ + "CACC", + "bCa", + 6 + ], + [ + "CACCAAB", + "bACabAC", + 8 + ], + [ + "CACCAb", + "abBBB", + 10 + ], + [ + "CACCCBA", + "cabaac", + 12 + ], + [ + "CACCc", + "ba", + 9 + ], + [ + "CACaAcc", + "acaBbCbb", + 13 + ], + [ + "CACaCBBc", + "a", + 14 + ], + [ + "CACaabcBb", + "aCabba", + 10 + ], + [ + "CACacaABb", + "b", + 16 + ], + [ + "CACb", + "ABbb", + 6 + ], + [ + "CACb", + "AabbaCa", + 11 + ], + [ + "CACbA", + "bCbbC", + 8 + ], + [ + "CACbaCB", + "caacaaC", + 9 + ], + [ + "CACbaaCa", + "bBacacB", + 12 + ], + [ + "CACbbBBc", + "Bc", + 12 + ], + [ + "CACbc", + "Cc", + 6 + ], + [ + "CACbcAcac", + "cCAcA", + 10 + ], + [ + "CACbcba", + "caABaCb", + 10 + ], + [ + "CACcBabCc", + "baAbbaBB", + 13 + ], + [ + "CACcCacC", + "ABcC", + 10 + ], + [ + "CACccCAC", + "B", + 16 + ], + [ + "CAa", + "Ab", + 4 + ], + [ + "CAa", + "CabcCc", + 9 + ], + [ + "CAa", + "aaCAbaBc", + 10 + ], + [ + "CAa", + "bABABaCA", + 12 + ], + [ + "CAa", + "bBAaB", + 6 + ], + [ + "CAa", + "bca", + 4 + ], + [ + "CAa", + "cbC", + 5 + ], + [ + "CAaAAaA", + "abAbbcbaC", + 14 + ], + [ + "CAaAAbaaB", + "CAcBB", + 11 + ], + [ + "CAaAcb", + "bBCBB", + 11 + ], + [ + "CAaBAA", + "AacAcBABB", + 11 + ], + [ + "CAaBB", + "Cc", + 8 + ], + [ + "CAaBBACa", + "CAaacBccC", + 9 + ], + [ + "CAaBC", + "b", + 9 + ], + [ + "CAaBbbaBA", + "BBB", + 13 + ], + [ + "CAaC", + "AaBCacAba", + 14 + ], + [ + "CAaC", + "BcccCbBb", + 13 + ], + [ + "CAaCBbcab", + "CbC", + 13 + ], + [ + "CAaCc", + "AA", + 7 + ], + [ + "CAaCcabB", + "cBABCb", + 11 + ], + [ + "CAaCccCc", + "A", + 14 + ], + [ + "CAaa", + "bcABBaAb", + 10 + ], + [ + "CAaa", + "cbabABAC", + 12 + ], + [ + "CAaaB", + "b", + 9 + ], + [ + "CAaabBcC", + "a", + 14 + ], + [ + "CAaabbCac", + "bAC", + 14 + ], + [ + "CAaabcaB", + "cabCacbaA", + 12 + ], + [ + "CAabABbAa", + "bcbaCa", + 13 + ], + [ + "CAabac", + "baaABbB", + 10 + ], + [ + "CAabb", + "b", + 8 + ], + [ + "CAabb", + "cACACBaBB", + 10 + ], + [ + "CAac", + "aaB", + 5 + ], + [ + "CAac", + "cccBB", + 9 + ], + [ + "CAacAB", + "a", + 10 + ], + [ + "CAaccC", + "cacbc", + 6 + ], + [ + "CAb", + "ACCCAcaA", + 12 + ], + [ + "CAb", + "Aca", + 6 + ], + [ + "CAb", + "BCcBBAA", + 10 + ], + [ + "CAb", + "C", + 4 + ], + [ + "CAb", + "CCAcbAB", + 8 + ], + [ + "CAb", + "CcCbCBC", + 10 + ], + [ + "CAb", + "acAc", + 5 + ], + [ + "CAb", + "bab", + 3 + ], + [ + "CAb", + "cA", + 3 + ], + [ + "CAbAAaAA", + "abc", + 13 + ], + [ + "CAbAc", + "ABBCCB", + 10 + ], + [ + "CAbB", + "acC", + 7 + ], + [ + "CAbBBBB", + "BCcaBbabB", + 10 + ], + [ + "CAbBCCBAC", + "baA", + 14 + ], + [ + "CAbBab", + "cbABcBc", + 10 + ], + [ + "CAbBc", + "bBcAcCA", + 11 + ], + [ + "CAbBc", + "bCAcbba", + 7 + ], + [ + "CAbC", + "AaAc", + 6 + ], + [ + "CAbC", + "cBAC", + 5 + ], + [ + "CAbCBaBb", + "cAbcBa", + 6 + ], + [ + "CAbCCC", + "c", + 11 + ], + [ + "CAbCabbA", + "BbAABACba", + 12 + ], + [ + "CAbCcc", + "babBA", + 9 + ], + [ + "CAba", + "abbB", + 6 + ], + [ + "CAbaAbBBC", + "CbBBAbB", + 9 + ], + [ + "CAbaAcCCC", + "AcbB", + 14 + ], + [ + "CAbaBb", + "A", + 10 + ], + [ + "CAbaa", + "cc", + 9 + ], + [ + "CAbacbba", + "cBCccabA", + 10 + ], + [ + "CAbbAA", + "BAAaCbC", + 12 + ], + [ + "CAbbAAcC", + "CBBACbBCB", + 12 + ], + [ + "CAbbBCcB", + "Abca", + 10 + ], + [ + "CAbbBbC", + "ABcaca", + 11 + ], + [ + "CAbbbAC", + "bcB", + 11 + ], + [ + "CAbbcCABA", + "CbB", + 12 + ], + [ + "CAbc", + "B", + 7 + ], + [ + "CAbcA", + "CAcaccACb", + 10 + ], + [ + "CAbcACCB", + "BB", + 13 + ], + [ + "CAbcBAa", + "bccBACB", + 10 + ], + [ + "CAbcCca", + "cAc", + 9 + ], + [ + "CAbcaBbca", + "ccaacBaaa", + 12 + ], + [ + "CAc", + "AAcacbcC", + 12 + ], + [ + "CAc", + "AbbAbbcc", + 12 + ], + [ + "CAc", + "BAB", + 4 + ], + [ + "CAc", + "ab", + 5 + ], + [ + "CAc", + "b", + 6 + ], + [ + "CAc", + "bbaaac", + 9 + ], + [ + "CAcAaCcaC", + "AAa", + 12 + ], + [ + "CAcAbaaA", + "ccBcBc", + 12 + ], + [ + "CAcAcC", + "BAbCb", + 9 + ], + [ + "CAcAca", + "CCbb", + 9 + ], + [ + "CAcAcbbAc", + "Cca", + 13 + ], + [ + "CAcB", + "cBcaaCcB", + 10 + ], + [ + "CAcBbCccb", + "BBAbAbAB", + 15 + ], + [ + "CAcBbbAaA", + "aACbbCbaa", + 9 + ], + [ + "CAcBbcbCa", + "bcBc", + 12 + ], + [ + "CAcBcBC", + "abCaCb", + 11 + ], + [ + "CAcC", + "CBcB", + 4 + ], + [ + "CAcCACCA", + "cAAa", + 10 + ], + [ + "CAcCaCBCC", + "aac", + 14 + ], + [ + "CAca", + "AaBACbAab", + 13 + ], + [ + "CAcaABcab", + "aCCbC", + 14 + ], + [ + "CAcaaa", + "bbccA", + 9 + ], + [ + "CAcaacBB", + "c", + 14 + ], + [ + "CAcb", + "B", + 7 + ], + [ + "CAcb", + "aBCBAb", + 8 + ], + [ + "CAcbBA", + "bBAaba", + 9 + ], + [ + "CAcbBB", + "CCcCBA", + 6 + ], + [ + "CAcbBacb", + "AbbbB", + 10 + ], + [ + "CAcbCCaaC", + "bccB", + 14 + ], + [ + "CAcbCbc", + "bAcCA", + 8 + ], + [ + "CAcbbabC", + "abAccCaac", + 11 + ], + [ + "CAcbcA", + "BBca", + 8 + ], + [ + "CAcbcB", + "Ab", + 8 + ], + [ + "CAcc", + "b", + 8 + ], + [ + "CAccAaAcc", + "bccbAB", + 12 + ], + [ + "CAccBaAC", + "abbAAaB", + 13 + ], + [ + "CAccBaaCc", + "BCAb", + 15 + ], + [ + "CAccBbA", + "bcBbBcBc", + 13 + ], + [ + "CAccac", + "aAc", + 8 + ], + [ + "CB", + "A", + 4 + ], + [ + "CB", + "ABac", + 6 + ], + [ + "CB", + "AC", + 4 + ], + [ + "CB", + "ACbbcbb", + 11 + ], + [ + "CB", + "ACcCCBAC", + 12 + ], + [ + "CB", + "AaAAcBc", + 11 + ], + [ + "CB", + "AaBCab", + 9 + ], + [ + "CB", + "BCc", + 4 + ], + [ + "CB", + "BaCaB", + 6 + ], + [ + "CB", + "BccbcabCB", + 14 + ], + [ + "CB", + "C", + 2 + ], + [ + "CB", + "CAAAb", + 7 + ], + [ + "CB", + "CCba", + 5 + ], + [ + "CB", + "CaAcA", + 8 + ], + [ + "CB", + "CcCaAccb", + 13 + ], + [ + "CB", + "a", + 4 + ], + [ + "CB", + "aAa", + 6 + ], + [ + "CB", + "aBCBaBbC", + 12 + ], + [ + "CB", + "aCbcAc", + 9 + ], + [ + "CB", + "ac", + 4 + ], + [ + "CB", + "bAa", + 6 + ], + [ + "CB", + "bBAAbcb", + 12 + ], + [ + "CB", + "bCB", + 2 + ], + [ + "CB", + "bCaCCcaCA", + 16 + ], + [ + "CB", + "ba", + 4 + ], + [ + "CB", + "baBCcCc", + 12 + ], + [ + "CB", + "bbcbCB", + 8 + ], + [ + "CB", + "bcaab", + 8 + ], + [ + "CB", + "c", + 3 + ], + [ + "CB", + "cACca", + 8 + ], + [ + "CB", + "cBCbAB", + 8 + ], + [ + "CB", + "cCB", + 2 + ], + [ + "CB", + "cbb", + 4 + ], + [ + "CB", + "cbbBbACCA", + 15 + ], + [ + "CBA", + "ba", + 4 + ], + [ + "CBA", + "cbcBb", + 7 + ], + [ + "CBAA", + "ABBaBcB", + 11 + ], + [ + "CBAA", + "Ba", + 5 + ], + [ + "CBAA", + "bcc", + 7 + ], + [ + "CBAABa", + "BAaBA", + 4 + ], + [ + "CBAAC", + "CBAac", + 2 + ], + [ + "CBAAa", + "cccbAb", + 9 + ], + [ + "CBAAaac", + "bcAAb", + 10 + ], + [ + "CBAAbCc", + "bbaABBB", + 9 + ], + [ + "CBAAbbBC", + "CbBAaAc", + 10 + ], + [ + "CBAAc", + "AAA", + 6 + ], + [ + "CBAAc", + "bCbBbAAaC", + 9 + ], + [ + "CBAB", + "A", + 6 + ], + [ + "CBABAbcBA", + "b", + 16 + ], + [ + "CBABAcBc", + "cAb", + 12 + ], + [ + "CBABCacb", + "aCCbbBABA", + 14 + ], + [ + "CBABaa", + "cBbcCcc", + 11 + ], + [ + "CBABbbCa", + "BbAaA", + 11 + ], + [ + "CBABbcCCC", + "cCAC", + 12 + ], + [ + "CBABc", + "cCc", + 7 + ], + [ + "CBABccBbc", + "caAaaACbb", + 13 + ], + [ + "CBACAA", + "CCc", + 8 + ], + [ + "CBACAcC", + "aCbbc", + 10 + ], + [ + "CBACa", + "BACaBBaB", + 10 + ], + [ + "CBACbcB", + "a", + 13 + ], + [ + "CBACc", + "baCCCA", + 9 + ], + [ + "CBACcAccA", + "aAb", + 15 + ], + [ + "CBAaAC", + "ccbC", + 9 + ], + [ + "CBAaBaC", + "bCccccCb", + 14 + ], + [ + "CBAaCB", + "acabB", + 8 + ], + [ + "CBAaCa", + "Cabb", + 8 + ], + [ + "CBAaCc", + "cBBBBBaC", + 11 + ], + [ + "CBAabb", + "bbCabaBa", + 10 + ], + [ + "CBAacCa", + "Bccbacb", + 11 + ], + [ + "CBAb", + "cCAcbc", + 7 + ], + [ + "CBAbCcaB", + "aC", + 13 + ], + [ + "CBAba", + "CbbabA", + 5 + ], + [ + "CBAbabb", + "BABa", + 7 + ], + [ + "CBAbba", + "ba", + 8 + ], + [ + "CBAcAAbcb", + "BBBBBbABA", + 15 + ], + [ + "CBAcC", + "acbabBA", + 11 + ], + [ + "CBAccB", + "cb", + 9 + ], + [ + "CBAccab", + "cbB", + 11 + ], + [ + "CBB", + "Aa", + 6 + ], + [ + "CBB", + "BbAaC", + 9 + ], + [ + "CBB", + "CB", + 2 + ], + [ + "CBB", + "CCA", + 4 + ], + [ + "CBB", + "CcbCb", + 6 + ], + [ + "CBB", + "baBB", + 4 + ], + [ + "CBB", + "cAcBBccA", + 11 + ], + [ + "CBB", + "cBccaB", + 7 + ], + [ + "CBBABB", + "BbBb", + 6 + ], + [ + "CBBABC", + "AABcaac", + 10 + ], + [ + "CBBAbACb", + "CCBCbAbcB", + 8 + ], + [ + "CBBBA", + "bB", + 7 + ], + [ + "CBBBBaAC", + "ca", + 13 + ], + [ + "CBBBBbbcC", + "BaAbCaB", + 14 + ], + [ + "CBBBb", + "bCbaBBAb", + 7 + ], + [ + "CBBBbCbcA", + "ABCCcABc", + 13 + ], + [ + "CBBC", + "ACaca", + 8 + ], + [ + "CBBC", + "cB", + 5 + ], + [ + "CBBCAAB", + "b", + 13 + ], + [ + "CBBCAb", + "CBA", + 6 + ], + [ + "CBBCCAB", + "AbCa", + 10 + ], + [ + "CBBCCa", + "c", + 11 + ], + [ + "CBBCabAa", + "Cca", + 11 + ], + [ + "CBBaCc", + "CbbA", + 7 + ], + [ + "CBBab", + "acBCcA", + 9 + ], + [ + "CBBabb", + "BBCcBb", + 7 + ], + [ + "CBBacBacB", + "caCCccBc", + 12 + ], + [ + "CBBbAb", + "Bbab", + 5 + ], + [ + "CBBba", + "bcCcb", + 9 + ], + [ + "CBBbaAcbc", + "ABcABaAAc", + 11 + ], + [ + "CBBbbbabc", + "baAaBC", + 12 + ], + [ + "CBBbcaaC", + "bABABABB", + 13 + ], + [ + "CBBc", + "abCABc", + 6 + ], + [ + "CBBcACB", + "abBbC", + 9 + ], + [ + "CBBcBcc", + "A", + 14 + ], + [ + "CBBcbA", + "ccbBAca", + 9 + ], + [ + "CBBcbaBc", + "C", + 14 + ], + [ + "CBBcbaC", + "bBcAAbaaC", + 9 + ], + [ + "CBBccBCCB", + "bacCC", + 11 + ], + [ + "CBBccc", + "aBabc", + 8 + ], + [ + "CBC", + "ACB", + 4 + ], + [ + "CBC", + "CAbCabB", + 9 + ], + [ + "CBC", + "aCaAaAC", + 10 + ], + [ + "CBCAA", + "C", + 8 + ], + [ + "CBCAABa", + "BBcCbBa", + 7 + ], + [ + "CBCAACB", + "bBBcAAB", + 7 + ], + [ + "CBCACAa", + "ABbA", + 10 + ], + [ + "CBCAaCA", + "BbaAbac", + 10 + ], + [ + "CBCAacCc", + "cCb", + 12 + ], + [ + "CBCB", + "aAAaA", + 10 + ], + [ + "CBCBBA", + "acc", + 11 + ], + [ + "CBCBBCAA", + "ABC", + 12 + ], + [ + "CBCBC", + "aBccbCB", + 8 + ], + [ + "CBCBCC", + "BAbc", + 8 + ], + [ + "CBCC", + "CbaaA", + 7 + ], + [ + "CBCCBC", + "aACAA", + 10 + ], + [ + "CBCCBCcCb", + "Ba", + 16 + ], + [ + "CBCCCB", + "cacbAbbaA", + 15 + ], + [ + "CBCCCbaB", + "cacAbc", + 12 + ], + [ + "CBCCacb", + "a", + 12 + ], + [ + "CBCCbcc", + "BbbBcbCBc", + 11 + ], + [ + "CBCCcCbA", + "CBaAa", + 11 + ], + [ + "CBCa", + "aaAbbabA", + 13 + ], + [ + "CBCa", + "b", + 7 + ], + [ + "CBCaA", + "ab", + 8 + ], + [ + "CBCaAaBaa", + "acBbAa", + 12 + ], + [ + "CBCaB", + "caa", + 7 + ], + [ + "CBCacBBBB", + "BcabACCb", + 12 + ], + [ + "CBCb", + "AabACCa", + 11 + ], + [ + "CBCb", + "aABc", + 7 + ], + [ + "CBCbBa", + "abaCaB", + 9 + ], + [ + "CBCbbc", + "CcBCBCcbA", + 9 + ], + [ + "CBCbccaA", + "AbCbbCba", + 9 + ], + [ + "CBCc", + "bbcCBBAcb", + 12 + ], + [ + "CBCcB", + "BAa", + 8 + ], + [ + "CBCcBbb", + "ccaCBbA", + 8 + ], + [ + "CBCcccacB", + "BA", + 15 + ], + [ + "CBa", + "ACaba", + 5 + ], + [ + "CBa", + "CAaB", + 4 + ], + [ + "CBa", + "CBBCccc", + 10 + ], + [ + "CBa", + "CacAccB", + 11 + ], + [ + "CBa", + "c", + 5 + ], + [ + "CBaAAa", + "caCcaBAA", + 9 + ], + [ + "CBaAC", + "CBb", + 6 + ], + [ + "CBaAbaaAA", + "CbbcC", + 13 + ], + [ + "CBaB", + "aabBAbbC", + 12 + ], + [ + "CBaBBAa", + "AaAcA", + 10 + ], + [ + "CBaBCABC", + "AAA", + 13 + ], + [ + "CBaBaCcB", + "cBaAbBAAa", + 12 + ], + [ + "CBaBaa", + "AAB", + 9 + ], + [ + "CBaCAbC", + "BbCaCC", + 7 + ], + [ + "CBaCBba", + "bcABB", + 10 + ], + [ + "CBaCCAB", + "cBcBAcA", + 10 + ], + [ + "CBaCabccB", + "Bba", + 14 + ], + [ + "CBaaA", + "AacbaC", + 10 + ], + [ + "CBaaAcac", + "aacbcbACc", + 13 + ], + [ + "CBaaB", + "CbaAcB", + 4 + ], + [ + "CBaaBc", + "Ba", + 8 + ], + [ + "CBaaCaCa", + "c", + 15 + ], + [ + "CBaaaBB", + "B", + 12 + ], + [ + "CBaab", + "BaabBcba", + 10 + ], + [ + "CBaab", + "bBB", + 7 + ], + [ + "CBaabAB", + "CBCC", + 10 + ], + [ + "CBaacACCC", + "a", + 16 + ], + [ + "CBab", + "CB", + 4 + ], + [ + "CBab", + "aBcc", + 6 + ], + [ + "CBabACCaA", + "A", + 16 + ], + [ + "CBabCbbaC", + "AcbAc", + 12 + ], + [ + "CBacA", + "acBCCaCB", + 10 + ], + [ + "CBacaCBCB", + "aB", + 14 + ], + [ + "CBacbacB", + "CaBa", + 9 + ], + [ + "CBb", + "Cbb", + 1 + ], + [ + "CBb", + "a", + 6 + ], + [ + "CBb", + "bBbcc", + 6 + ], + [ + "CBb", + "bCaaaA", + 10 + ], + [ + "CBbA", + "bAaCbBC", + 10 + ], + [ + "CBbAAa", + "aabB", + 10 + ], + [ + "CBbAB", + "A", + 8 + ], + [ + "CBbAB", + "bbb", + 6 + ], + [ + "CBbACca", + "CbCAcB", + 7 + ], + [ + "CBbACcbC", + "bCcbBAb", + 12 + ], + [ + "CBbBB", + "C", + 8 + ], + [ + "CBbBB", + "CBCBcCBa", + 8 + ], + [ + "CBbBBCaBB", + "AbAca", + 13 + ], + [ + "CBbCA", + "aca", + 8 + ], + [ + "CBbCCB", + "BAbC", + 8 + ], + [ + "CBbCCCa", + "Ca", + 10 + ], + [ + "CBbCCb", + "CACbBBb", + 8 + ], + [ + "CBbCbbBc", + "ABaA", + 14 + ], + [ + "CBbaAcAAc", + "ccAACB", + 12 + ], + [ + "CBbaAca", + "bB", + 12 + ], + [ + "CBbaB", + "B", + 8 + ], + [ + "CBbaBBCaC", + "baaCcA", + 12 + ], + [ + "CBbaC", + "c", + 9 + ], + [ + "CBbaCa", + "cbbcAa", + 6 + ], + [ + "CBbaCbB", + "AcCcACbA", + 10 + ], + [ + "CBbabC", + "abB", + 8 + ], + [ + "CBbb", + "C", + 6 + ], + [ + "CBbb", + "a", + 8 + ], + [ + "CBbbbaB", + "B", + 12 + ], + [ + "CBbc", + "BaAABc", + 9 + ], + [ + "CBbcBAAB", + "cAbCAA", + 8 + ], + [ + "CBbcbb", + "cBCaC", + 8 + ], + [ + "CBc", + "Bcab", + 6 + ], + [ + "CBc", + "CBAAacc", + 8 + ], + [ + "CBc", + "abaCa", + 8 + ], + [ + "CBc", + "cAabccaaA", + 14 + ], + [ + "CBcA", + "CCA", + 3 + ], + [ + "CBcAAaccC", + "abBcAbaBC", + 10 + ], + [ + "CBcAaCCCb", + "aB", + 15 + ], + [ + "CBcAbAA", + "BabCcAAc", + 11 + ], + [ + "CBcAcB", + "CbbAAA", + 7 + ], + [ + "CBcB", + "a", + 8 + ], + [ + "CBcB", + "bcCBCCcbb", + 11 + ], + [ + "CBcBA", + "cBbCb", + 7 + ], + [ + "CBcBBABcc", + "bbBcabA", + 13 + ], + [ + "CBcBC", + "cAbCAAbcA", + 13 + ], + [ + "CBcBaaACB", + "cCc", + 14 + ], + [ + "CBcBb", + "c", + 8 + ], + [ + "CBcC", + "bCBBbBa", + 10 + ], + [ + "CBcC", + "c", + 6 + ], + [ + "CBcCC", + "aB", + 8 + ], + [ + "CBca", + "cC", + 6 + ], + [ + "CBcaAa", + "A", + 10 + ], + [ + "CBcaAba", + "AaAAaaCaA", + 13 + ], + [ + "CBcaBCc", + "AcBcb", + 9 + ], + [ + "CBcabbbba", + "bCCBc", + 15 + ], + [ + "CBcacCbCC", + "bCAaab", + 13 + ], + [ + "CBcb", + "Acb", + 4 + ], + [ + "CBcb", + "CbcABCA", + 8 + ], + [ + "CBcb", + "cBAac", + 7 + ], + [ + "CBcbCbc", + "cBBaBc", + 7 + ], + [ + "CBcbCcaBc", + "BAACAaC", + 11 + ], + [ + "CBcba", + "abCA", + 7 + ], + [ + "CBcbab", + "ABBabBC", + 9 + ], + [ + "CBcbbaC", + "ABcCa", + 8 + ], + [ + "CBcbcAAA", + "aaAbb", + 14 + ], + [ + "CBcbcbC", + "c", + 12 + ], + [ + "CBccaC", + "CAAa", + 8 + ], + [ + "CBccaCaaa", + "ccbBabcBB", + 15 + ], + [ + "CBccac", + "aB", + 10 + ], + [ + "CBccbaAbb", + "bBaacaC", + 14 + ], + [ + "CBccbbc", + "ACaAaCB", + 13 + ], + [ + "CC", + "AA", + 4 + ], + [ + "CC", + "AAaacAc", + 12 + ], + [ + "CC", + "ACACbCABB", + 14 + ], + [ + "CC", + "ACBaCAb", + 10 + ], + [ + "CC", + "ACaaB", + 8 + ], + [ + "CC", + "ACcCAaabb", + 14 + ], + [ + "CC", + "AacCBBc", + 11 + ], + [ + "CC", + "BaAACABCC", + 14 + ], + [ + "CC", + "BaBcaCbb", + 13 + ], + [ + "CC", + "Bc", + 3 + ], + [ + "CC", + "CBC", + 2 + ], + [ + "CC", + "CCAAAbab", + 12 + ], + [ + "CC", + "CCc", + 2 + ], + [ + "CC", + "Cb", + 2 + ], + [ + "CC", + "CbbBb", + 8 + ], + [ + "CC", + "CcAc", + 5 + ], + [ + "CC", + "a", + 4 + ], + [ + "CC", + "aACcC", + 6 + ], + [ + "CC", + "aBC", + 4 + ], + [ + "CC", + "aBbc", + 7 + ], + [ + "CC", + "aa", + 4 + ], + [ + "CC", + "bA", + 4 + ], + [ + "CC", + "c", + 3 + ], + [ + "CC", + "cBBB", + 7 + ], + [ + "CC", + "cBcCbBcab", + 15 + ], + [ + "CC", + "cCabBBC", + 10 + ], + [ + "CC", + "cCbBB", + 7 + ], + [ + "CCA", + "A", + 4 + ], + [ + "CCA", + "BaaabCA", + 10 + ], + [ + "CCA", + "CBaCacAB", + 10 + ], + [ + "CCA", + "aba", + 5 + ], + [ + "CCA", + "bb", + 6 + ], + [ + "CCAA", + "ABBCA", + 8 + ], + [ + "CCAA", + "BcCbBA", + 7 + ], + [ + "CCAA", + "babAabAA", + 12 + ], + [ + "CCAA", + "cAcC", + 7 + ], + [ + "CCAABbAAc", + "CA", + 14 + ], + [ + "CCAABc", + "BacACaaB", + 11 + ], + [ + "CCAACAbaa", + "BA", + 16 + ], + [ + "CCAAaB", + "CaCCcCa", + 10 + ], + [ + "CCAAbabbc", + "cc", + 15 + ], + [ + "CCAAcCCcA", + "ABcACaBc", + 13 + ], + [ + "CCAAcb", + "C", + 10 + ], + [ + "CCAB", + "bbc", + 8 + ], + [ + "CCABAAB", + "BCABbccB", + 8 + ], + [ + "CCABBCcbB", + "CabB", + 11 + ], + [ + "CCABCCAaa", + "CBCc", + 11 + ], + [ + "CCABb", + "Bcc", + 9 + ], + [ + "CCABcCBAa", + "bbBa", + 13 + ], + [ + "CCABcaC", + "caCc", + 10 + ], + [ + "CCABcbBaC", + "cAAaAAaC", + 11 + ], + [ + "CCAC", + "aBAccBA", + 11 + ], + [ + "CCACb", + "BABcBc", + 10 + ], + [ + "CCACbACc", + "Aabbbac", + 11 + ], + [ + "CCACcaCB", + "ABbCACbB", + 11 + ], + [ + "CCAa", + "aACCbaaac", + 11 + ], + [ + "CCAb", + "ACCBba", + 6 + ], + [ + "CCAbBcABA", + "cAaCBc", + 12 + ], + [ + "CCAbBcb", + "baCB", + 10 + ], + [ + "CCAbCAb", + "BaCCaa", + 10 + ], + [ + "CCAbaC", + "BBbcaa", + 10 + ], + [ + "CCAbaCBa", + "bcccA", + 12 + ], + [ + "CCAcAB", + "ccAAbCa", + 9 + ], + [ + "CCAcACA", + "acBA", + 9 + ], + [ + "CCAcBAa", + "aCacBaBAA", + 8 + ], + [ + "CCAcC", + "BbcBbC", + 9 + ], + [ + "CCAcCB", + "cBcaBBBA", + 11 + ], + [ + "CCAcaC", + "CcaB", + 6 + ], + [ + "CCAcaaca", + "cC", + 13 + ], + [ + "CCAcacccc", + "cAcabcB", + 9 + ], + [ + "CCAcbCb", + "ACCa", + 9 + ], + [ + "CCAcbbCBc", + "CcAbBCa", + 8 + ], + [ + "CCB", + "ACbaAACcB", + 12 + ], + [ + "CCB", + "Cc", + 3 + ], + [ + "CCB", + "abaAB", + 8 + ], + [ + "CCB", + "baCac", + 8 + ], + [ + "CCBA", + "Aaaaa", + 9 + ], + [ + "CCBAA", + "a", + 9 + ], + [ + "CCBABcc", + "BCABb", + 8 + ], + [ + "CCBAC", + "bCCcaCAc", + 9 + ], + [ + "CCBAbAC", + "bCA", + 10 + ], + [ + "CCBAc", + "BC", + 7 + ], + [ + "CCBAcaacb", + "aCCAA", + 13 + ], + [ + "CCBBCBccb", + "BBAa", + 14 + ], + [ + "CCBBbB", + "CbcAACa", + 11 + ], + [ + "CCBBccC", + "c", + 12 + ], + [ + "CCBC", + "CBCaABac", + 9 + ], + [ + "CCBCAaAA", + "CaC", + 12 + ], + [ + "CCBCBCcc", + "bcCccbAbb", + 13 + ], + [ + "CCBCb", + "c", + 9 + ], + [ + "CCBaAaB", + "bACAC", + 11 + ], + [ + "CCBaAaB", + "cbcCBca", + 11 + ], + [ + "CCBaAcba", + "BaaaCAA", + 11 + ], + [ + "CCBaBbbBC", + "a", + 16 + ], + [ + "CCBaCcAa", + "ccBbACCb", + 10 + ], + [ + "CCBbAAAB", + "CCcaBabc", + 11 + ], + [ + "CCBbACc", + "bbbaacBb", + 12 + ], + [ + "CCBbB", + "cBBBACC", + 10 + ], + [ + "CCBbBC", + "bAcCCab", + 12 + ], + [ + "CCBbabCB", + "bCcaB", + 10 + ], + [ + "CCBbca", + "bc", + 8 + ], + [ + "CCBcB", + "ACbbCa", + 8 + ], + [ + "CCBca", + "BacCaaAA", + 12 + ], + [ + "CCBcbbC", + "cB", + 11 + ], + [ + "CCC", + "CcaB", + 5 + ], + [ + "CCC", + "baaaC", + 8 + ], + [ + "CCCAABC", + "CC", + 10 + ], + [ + "CCCAbbBCb", + "cBBaaBab", + 12 + ], + [ + "CCCAc", + "BaccCaB", + 9 + ], + [ + "CCCBAbcc", + "accAb", + 10 + ], + [ + "CCCBBcab", + "ACCA", + 11 + ], + [ + "CCCBCaAcC", + "aaCcAbAa", + 14 + ], + [ + "CCCBbbbC", + "aCb", + 12 + ], + [ + "CCCBcBBaa", + "aBACAbAA", + 14 + ], + [ + "CCCBccaB", + "ccBcc", + 8 + ], + [ + "CCCC", + "bBcAaAbaC", + 15 + ], + [ + "CCCCA", + "BBACa", + 7 + ], + [ + "CCCCA", + "bAcAbbC", + 13 + ], + [ + "CCCCA", + "cccAC", + 7 + ], + [ + "CCCCAbaa", + "CaBaccBB", + 14 + ], + [ + "CCCCBC", + "ABbBc", + 9 + ], + [ + "CCCCCa", + "BCACA", + 7 + ], + [ + "CCCCaBCA", + "Bacbcab", + 13 + ], + [ + "CCCCbaC", + "b", + 12 + ], + [ + "CCCCbbba", + "BAbcAbcBb", + 14 + ], + [ + "CCCCcA", + "bbCCbbc", + 10 + ], + [ + "CCCCcBAC", + "acACBcC", + 9 + ], + [ + "CCCa", + "bAb", + 8 + ], + [ + "CCCaABacb", + "bacBBcAA", + 14 + ], + [ + "CCCaCcA", + "bAcAC", + 10 + ], + [ + "CCCaCcbBA", + "bAbc", + 15 + ], + [ + "CCCaaCaBA", + "AbAbBCb", + 15 + ], + [ + "CCCabaaab", + "cAbca", + 12 + ], + [ + "CCCacacb", + "CAcCBcA", + 10 + ], + [ + "CCCb", + "C", + 6 + ], + [ + "CCCbBBB", + "A", + 14 + ], + [ + "CCCba", + "BbAcaC", + 10 + ], + [ + "CCCbbAA", + "a", + 13 + ], + [ + "CCCbbaA", + "cCB", + 10 + ], + [ + "CCCbbcc", + "BCA", + 12 + ], + [ + "CCCc", + "CBbBBaBBa", + 16 + ], + [ + "CCCcBBba", + "CcBbcAa", + 9 + ], + [ + "CCCcBaA", + "CCCBAAAcA", + 9 + ], + [ + "CCa", + "Ca", + 2 + ], + [ + "CCa", + "CbabbAAcA", + 14 + ], + [ + "CCa", + "bbbCBA", + 9 + ], + [ + "CCa", + "cCAcbAbCC", + 14 + ], + [ + "CCaA", + "c", + 7 + ], + [ + "CCaAA", + "BBACcB", + 11 + ], + [ + "CCaAAb", + "cBa", + 9 + ], + [ + "CCaABbBaA", + "CcACab", + 11 + ], + [ + "CCaAcb", + "ABbBCaCa", + 13 + ], + [ + "CCaBAcbAC", + "CBbc", + 11 + ], + [ + "CCaBBA", + "bAbBB", + 8 + ], + [ + "CCaCAAbA", + "AA", + 12 + ], + [ + "CCaCACBcb", + "caC", + 13 + ], + [ + "CCaCAaA", + "BCAaCaa", + 7 + ], + [ + "CCaCB", + "CAAA", + 7 + ], + [ + "CCaCBcAC", + "bcCa", + 12 + ], + [ + "CCaCCC", + "CACcBa", + 8 + ], + [ + "CCaCaCc", + "BcCba", + 11 + ], + [ + "CCaCaCcBA", + "a", + 16 + ], + [ + "CCaCabaAa", + "CcccAacAC", + 11 + ], + [ + "CCaCb", + "bAcaCbCAb", + 11 + ], + [ + "CCaCbaaaC", + "CaCBCBBc", + 10 + ], + [ + "CCaaBbbA", + "BAAc", + 14 + ], + [ + "CCaaCbCab", + "aBbB", + 13 + ], + [ + "CCaaaaccC", + "AABcca", + 12 + ], + [ + "CCaacBa", + "a", + 12 + ], + [ + "CCabA", + "acbCAAbb", + 10 + ], + [ + "CCabA", + "bbaacCA", + 10 + ], + [ + "CCabCAac", + "ABABA", + 12 + ], + [ + "CCabcCbc", + "BCbc", + 9 + ], + [ + "CCac", + "a", + 6 + ], + [ + "CCacAac", + "bbBbaC", + 11 + ], + [ + "CCacAbCcb", + "bCbBa", + 14 + ], + [ + "CCacBca", + "C", + 12 + ], + [ + "CCacCb", + "cBAC", + 8 + ], + [ + "CCacab", + "Aa", + 9 + ], + [ + "CCacbb", + "CAb", + 7 + ], + [ + "CCacbcB", + "Cca", + 9 + ], + [ + "CCb", + "BAbBAaB", + 12 + ], + [ + "CCb", + "CAAAbca", + 10 + ], + [ + "CCb", + "bAc", + 6 + ], + [ + "CCb", + "bcabBaB", + 11 + ], + [ + "CCb", + "cbCcaAaac", + 15 + ], + [ + "CCbA", + "AABAC", + 7 + ], + [ + "CCbA", + "BbBCabCaa", + 13 + ], + [ + "CCbAAa", + "BcABbC", + 11 + ], + [ + "CCbABCC", + "a", + 13 + ], + [ + "CCbACCBa", + "BbbaBbC", + 12 + ], + [ + "CCbAaaA", + "aB", + 12 + ], + [ + "CCbAbC", + "cAacBCa", + 10 + ], + [ + "CCbAbbBB", + "abcaA", + 13 + ], + [ + "CCbB", + "Aa", + 8 + ], + [ + "CCbB", + "cbaBCBBA", + 10 + ], + [ + "CCbBBB", + "CACCacCBc", + 12 + ], + [ + "CCbBCC", + "BbCAbcAA", + 12 + ], + [ + "CCbBbaBa", + "BcbccCAc", + 13 + ], + [ + "CCbCBCbB", + "cCbbB", + 7 + ], + [ + "CCbCbA", + "ABACcA", + 8 + ], + [ + "CCbCbcacC", + "bBC", + 13 + ], + [ + "CCba", + "aBaAcAaCC", + 15 + ], + [ + "CCba", + "caCAcc", + 9 + ], + [ + "CCba", + "cbaCcba", + 7 + ], + [ + "CCbaB", + "Ba", + 7 + ], + [ + "CCbaBB", + "CCaccA", + 8 + ], + [ + "CCbaCC", + "aBbcaa", + 10 + ], + [ + "CCbaaBcAB", + "a", + 16 + ], + [ + "CCbaba", + "ACbaBAAb", + 8 + ], + [ + "CCbb", + "aBbacACBa", + 14 + ], + [ + "CCbbACcA", + "CBabaAA", + 9 + ], + [ + "CCbbAabCa", + "cAABb", + 13 + ], + [ + "CCbbBabb", + "aBBcc", + 13 + ], + [ + "CCbbaBC", + "cBC", + 9 + ], + [ + "CCbbca", + "bAC", + 9 + ], + [ + "CCbbcaCBA", + "CCaAaBBAB", + 10 + ], + [ + "CCbcAac", + "a", + 12 + ], + [ + "CCbcBC", + "aabbAc", + 9 + ], + [ + "CCbcBca", + "B", + 12 + ], + [ + "CCbcCAc", + "Bb", + 12 + ], + [ + "CCbcb", + "cBaC", + 8 + ], + [ + "CCbcc", + "aBaccBcB", + 11 + ], + [ + "CCc", + "A", + 6 + ], + [ + "CCc", + "Ac", + 4 + ], + [ + "CCc", + "Bc", + 4 + ], + [ + "CCc", + "Cacbb", + 6 + ], + [ + "CCc", + "bCcbAbbAB", + 14 + ], + [ + "CCcAC", + "CABCA", + 7 + ], + [ + "CCcACBCC", + "B", + 14 + ], + [ + "CCcACc", + "cACCCA", + 8 + ], + [ + "CCcAaaba", + "CCCBcABca", + 9 + ], + [ + "CCcAc", + "abaaaCCc", + 13 + ], + [ + "CCcB", + "aCAaBc", + 8 + ], + [ + "CCcBAB", + "CacAbbcbB", + 11 + ], + [ + "CCcBAcC", + "bB", + 12 + ], + [ + "CCcBbAb", + "aBbC", + 10 + ], + [ + "CCcBcaB", + "cBbb", + 9 + ], + [ + "CCcCaaAC", + "CAAbccC", + 12 + ], + [ + "CCcCb", + "B", + 9 + ], + [ + "CCcCbacba", + "AAa", + 15 + ], + [ + "CCcCcABB", + "cAcBabacb", + 13 + ], + [ + "CCcCcccAB", + "BAaACCCAB", + 11 + ], + [ + "CCca", + "Acc", + 5 + ], + [ + "CCca", + "a", + 6 + ], + [ + "CCcaBCabB", + "cAabc", + 11 + ], + [ + "CCcaC", + "bbCACAA", + 10 + ], + [ + "CCcaCca", + "CabacBb", + 9 + ], + [ + "CCcaaBaB", + "ccaAabb", + 9 + ], + [ + "CCcaaabbc", + "BCaCC", + 13 + ], + [ + "CCcabA", + "bCCAAcaBC", + 9 + ], + [ + "CCcacbB", + "Babb", + 9 + ], + [ + "CCcb", + "C", + 6 + ], + [ + "CCcbB", + "abB", + 6 + ], + [ + "CCcbBBC", + "aAAaCCCc", + 14 + ], + [ + "CCcbC", + "ACcBBBBc", + 10 + ], + [ + "CCcbC", + "a", + 10 + ], + [ + "CCcbCACab", + "Accba", + 11 + ], + [ + "CCcbcaaaA", + "ccaaAbaaB", + 12 + ], + [ + "CCccAAB", + "CCaaa", + 8 + ], + [ + "CCccB", + "AACBAbC", + 11 + ], + [ + "CCcca", + "baAaCccaa", + 10 + ], + [ + "CCccaAA", + "b", + 14 + ], + [ + "CCccbb", + "babC", + 10 + ], + [ + "Ca", + "ABCCb", + 8 + ], + [ + "Ca", + "AaCcCBaab", + 14 + ], + [ + "Ca", + "Aac", + 4 + ], + [ + "Ca", + "BAAABca", + 11 + ], + [ + "Ca", + "BACaCCb", + 10 + ], + [ + "Ca", + "BAc", + 5 + ], + [ + "Ca", + "BBAA", + 7 + ], + [ + "Ca", + "BCB", + 4 + ], + [ + "Ca", + "BCBbBaAC", + 12 + ], + [ + "Ca", + "BaCCAcBAa", + 14 + ], + [ + "Ca", + "BabB", + 6 + ], + [ + "Ca", + "BababbBb", + 14 + ], + [ + "Ca", + "BbCB", + 6 + ], + [ + "Ca", + "Bbb", + 6 + ], + [ + "Ca", + "CAbc", + 5 + ], + [ + "Ca", + "CBAbac", + 8 + ], + [ + "Ca", + "CBB", + 4 + ], + [ + "Ca", + "CBaA", + 4 + ], + [ + "Ca", + "CCAB", + 5 + ], + [ + "Ca", + "CCaCBaaA", + 12 + ], + [ + "Ca", + "CCbaAA", + 8 + ], + [ + "Ca", + "CCbb", + 6 + ], + [ + "Ca", + "Cb", + 2 + ], + [ + "Ca", + "aACCba", + 8 + ], + [ + "Ca", + "aB", + 4 + ], + [ + "Ca", + "aBBA", + 7 + ], + [ + "Ca", + "aac", + 4 + ], + [ + "Ca", + "acBacBcAA", + 15 + ], + [ + "Ca", + "b", + 4 + ], + [ + "Ca", + "bAbBbbAa", + 14 + ], + [ + "Ca", + "bBCA", + 5 + ], + [ + "Ca", + "bbBAAAa", + 12 + ], + [ + "Ca", + "c", + 3 + ], + [ + "Ca", + "cCBCBbAAa", + 14 + ], + [ + "Ca", + "caBabaBc", + 13 + ], + [ + "Ca", + "ccCaCCC", + 10 + ], + [ + "CaA", + "BcBcAb", + 9 + ], + [ + "CaA", + "CCBBA", + 6 + ], + [ + "CaA", + "aBa", + 5 + ], + [ + "CaA", + "aBcbcb", + 11 + ], + [ + "CaA", + "bBcCBcbcc", + 16 + ], + [ + "CaA", + "bCABC", + 7 + ], + [ + "CaA", + "bCcBaAaA", + 10 + ], + [ + "CaA", + "c", + 5 + ], + [ + "CaAA", + "bacCbbA", + 10 + ], + [ + "CaAABbc", + "c", + 12 + ], + [ + "CaAAC", + "c", + 9 + ], + [ + "CaAACB", + "bB", + 10 + ], + [ + "CaAAa", + "AcCbA", + 9 + ], + [ + "CaAAbcCa", + "a", + 14 + ], + [ + "CaAB", + "BCaCaCB", + 7 + ], + [ + "CaAB", + "Ba", + 6 + ], + [ + "CaAB", + "bCb", + 7 + ], + [ + "CaABBc", + "b", + 11 + ], + [ + "CaABCBA", + "bBb", + 11 + ], + [ + "CaABa", + "B", + 8 + ], + [ + "CaABbACB", + "BA", + 12 + ], + [ + "CaABbC", + "AAacAc", + 9 + ], + [ + "CaAC", + "BBCbCcaaC", + 11 + ], + [ + "CaAC", + "aCAABab", + 9 + ], + [ + "CaAC", + "acCBbcbA", + 13 + ], + [ + "CaACB", + "B", + 8 + ], + [ + "CaACbacC", + "BAbabAAB", + 12 + ], + [ + "CaACbc", + "CBA", + 8 + ], + [ + "CaACcBACc", + "aAcc", + 10 + ], + [ + "CaAa", + "Bababa", + 7 + ], + [ + "CaAa", + "ccAC", + 5 + ], + [ + "CaAaAC", + "ACa", + 9 + ], + [ + "CaAaACCc", + "BaabbBCbC", + 12 + ], + [ + "CaAaBbB", + "cbAb", + 9 + ], + [ + "CaAaa", + "cAAACBa", + 7 + ], + [ + "CaAab", + "BBbcbaa", + 12 + ], + [ + "CaAabcABB", + "ACcBacC", + 14 + ], + [ + "CaAbC", + "bC", + 6 + ], + [ + "CaAbbBa", + "c", + 13 + ], + [ + "CaAc", + "Ac", + 4 + ], + [ + "CaAc", + "abB", + 6 + ], + [ + "CaAcCAb", + "cABCaAA", + 9 + ], + [ + "CaAcCa", + "bBabcCCC", + 10 + ], + [ + "CaAca", + "bcAcBAABA", + 13 + ], + [ + "CaAcb", + "aBbACB", + 8 + ], + [ + "CaAcc", + "ACc", + 5 + ], + [ + "CaB", + "B", + 4 + ], + [ + "CaB", + "C", + 4 + ], + [ + "CaB", + "acbCAcC", + 11 + ], + [ + "CaB", + "bbAaAaBB", + 12 + ], + [ + "CaB", + "cabbb", + 6 + ], + [ + "CaBA", + "cbcAB", + 7 + ], + [ + "CaBAAB", + "BBAC", + 8 + ], + [ + "CaBAAB", + "a", + 10 + ], + [ + "CaBAAaBB", + "AACB", + 10 + ], + [ + "CaBAbCb", + "baA", + 10 + ], + [ + "CaBAcBCcb", + "A", + 16 + ], + [ + "CaBAcc", + "AACCaA", + 11 + ], + [ + "CaBAcc", + "bAcBbA", + 11 + ], + [ + "CaBB", + "aCCBbcaba", + 13 + ], + [ + "CaBB", + "babABcb", + 9 + ], + [ + "CaBBBa", + "Cc", + 10 + ], + [ + "CaBBBbBB", + "CAAaC", + 13 + ], + [ + "CaBBCccC", + "bAac", + 13 + ], + [ + "CaBBaA", + "a", + 10 + ], + [ + "CaBBabBcc", + "b", + 16 + ], + [ + "CaBBcC", + "BA", + 10 + ], + [ + "CaBBccac", + "accCBA", + 11 + ], + [ + "CaBC", + "CaaacC", + 6 + ], + [ + "CaBCACabA", + "CbcABbc", + 10 + ], + [ + "CaBCBbC", + "ccbBbaC", + 8 + ], + [ + "CaBCCcaCc", + "CaC", + 12 + ], + [ + "CaBaaBcA", + "C", + 14 + ], + [ + "CaBacBc", + "c", + 12 + ], + [ + "CaBba", + "bbcCaAa", + 10 + ], + [ + "CaBbac", + "A", + 11 + ], + [ + "CaBcAc", + "CAAab", + 8 + ], + [ + "CaBcBAbB", + "BABCCca", + 12 + ], + [ + "CaBcaA", + "bcC", + 9 + ], + [ + "CaBccaAc", + "bBa", + 12 + ], + [ + "CaC", + "aC", + 2 + ], + [ + "CaCA", + "bcBA", + 6 + ], + [ + "CaCAAbCcB", + "aBCc", + 11 + ], + [ + "CaCABCcbA", + "bB", + 16 + ], + [ + "CaCACCaA", + "BCBBaaBCA", + 13 + ], + [ + "CaCAbBA", + "bCBb", + 10 + ], + [ + "CaCB", + "AB", + 5 + ], + [ + "CaCB", + "bAb", + 6 + ], + [ + "CaCBa", + "ACabCC", + 8 + ], + [ + "CaCBa", + "abBaa", + 6 + ], + [ + "CaCBaaC", + "bcACBa", + 8 + ], + [ + "CaCBbBBb", + "bab", + 12 + ], + [ + "CaCBc", + "cAC", + 6 + ], + [ + "CaCCbB", + "babA", + 8 + ], + [ + "CaCCbc", + "BAbcCcBB", + 11 + ], + [ + "CaCa", + "Aab", + 6 + ], + [ + "CaCa", + "CCccAab", + 9 + ], + [ + "CaCa", + "CcbcAAC", + 10 + ], + [ + "CaCa", + "abCCa", + 6 + ], + [ + "CaCaAB", + "aBb", + 9 + ], + [ + "CaCb", + "bCab", + 4 + ], + [ + "CaCbBcCCC", + "CaBBab", + 11 + ], + [ + "CaCbbCAcb", + "aABcAc", + 10 + ], + [ + "CaCbbaB", + "ABB", + 10 + ], + [ + "CaCc", + "A", + 7 + ], + [ + "CaCc", + "ccBbc", + 7 + ], + [ + "CaCcAAb", + "baCBCB", + 9 + ], + [ + "CaCcB", + "AA", + 9 + ], + [ + "CaCcBa", + "cAc", + 8 + ], + [ + "CaCcaa", + "CABabbB", + 11 + ], + [ + "CaCcbBA", + "CAbccCaa", + 9 + ], + [ + "Caa", + "Acc", + 6 + ], + [ + "Caa", + "BAcBcBCbb", + 16 + ], + [ + "Caa", + "abbAb", + 9 + ], + [ + "CaaABcBab", + "acACccaCa", + 12 + ], + [ + "CaaAaAa", + "bbaabAc", + 9 + ], + [ + "CaaAaAaba", + "aaCBc", + 13 + ], + [ + "CaaAbCcAb", + "bc", + 14 + ], + [ + "CaaBAab", + "CcC", + 12 + ], + [ + "CaaBcB", + "Caaa", + 6 + ], + [ + "CaaC", + "AAbBaC", + 7 + ], + [ + "CaaCA", + "A", + 8 + ], + [ + "CaaCbCB", + "c", + 13 + ], + [ + "Caaa", + "BBaBcA", + 9 + ], + [ + "CaaaCabaC", + "CAa", + 13 + ], + [ + "Caaab", + "a", + 8 + ], + [ + "CaaabAbc", + "ACc", + 12 + ], + [ + "CaaabCbB", + "ABcB", + 11 + ], + [ + "Caaabcc", + "babAaAC", + 10 + ], + [ + "Caaac", + "AAbbccabb", + 15 + ], + [ + "Caab", + "BacAc", + 7 + ], + [ + "CaacAaCb", + "AbccbacB", + 10 + ], + [ + "CaacCb", + "acbcCABBB", + 13 + ], + [ + "Caacaccbc", + "bCcbB", + 13 + ], + [ + "CaacccACC", + "baBCAaCA", + 12 + ], + [ + "Cab", + "AbBCbcBbc", + 14 + ], + [ + "Cab", + "AcC", + 6 + ], + [ + "Cab", + "BCA", + 5 + ], + [ + "Cab", + "BaB", + 3 + ], + [ + "Cab", + "c", + 5 + ], + [ + "CabA", + "b", + 6 + ], + [ + "CabAabA", + "cbCaBBb", + 10 + ], + [ + "CabAbcCB", + "acbCa", + 10 + ], + [ + "CabB", + "AACcCBcab", + 14 + ], + [ + "CabB", + "BabB", + 2 + ], + [ + "CabB", + "Ccba", + 4 + ], + [ + "CabBCa", + "AaCaab", + 10 + ], + [ + "CabBacbC", + "C", + 14 + ], + [ + "CabBbab", + "Bc", + 12 + ], + [ + "CabBbc", + "BbBCbcBca", + 12 + ], + [ + "CabBcC", + "AaAcCAC", + 9 + ], + [ + "CabCB", + "AaCBCab", + 8 + ], + [ + "CabCaAABb", + "BbaB", + 12 + ], + [ + "CabCb", + "BcCbacbb", + 10 + ], + [ + "Caba", + "AA", + 6 + ], + [ + "Caba", + "CBbBBbCC", + 12 + ], + [ + "CabaAAcbc", + "aA", + 14 + ], + [ + "CabaAaab", + "Bccb", + 13 + ], + [ + "CabaBCc", + "acBcBb", + 11 + ], + [ + "CabaCAC", + "bAaa", + 10 + ], + [ + "CababAAcB", + "a", + 16 + ], + [ + "Cabb", + "aBbABaA", + 11 + ], + [ + "CabbAAABB", + "ccA", + 15 + ], + [ + "CabbACc", + "ABCCca", + 10 + ], + [ + "CabbB", + "BabCaAC", + 10 + ], + [ + "CabbB", + "CbBbAccbc", + 12 + ], + [ + "CabbCaa", + "bbaBbcacC", + 10 + ], + [ + "CabbaAba", + "CABaCBAbA", + 9 + ], + [ + "CabbbAACB", + "Babac", + 12 + ], + [ + "CabcB", + "aBAbaA", + 9 + ], + [ + "CabcBaC", + "Cac", + 8 + ], + [ + "CabcaABb", + "cBBc", + 12 + ], + [ + "CabcaBcB", + "bAABcbCA", + 12 + ], + [ + "Cabcb", + "Ba", + 8 + ], + [ + "Cabccb", + "BBBA", + 11 + ], + [ + "Cac", + "A", + 5 + ], + [ + "Cac", + "BAcb", + 5 + ], + [ + "Cac", + "BCcCCac", + 8 + ], + [ + "Cac", + "Bc", + 4 + ], + [ + "Cac", + "CBaacCcCb", + 12 + ], + [ + "Cac", + "cC", + 4 + ], + [ + "Cac", + "cbACbC", + 9 + ], + [ + "CacA", + "BaBabBAA", + 12 + ], + [ + "CacA", + "CacbC", + 4 + ], + [ + "CacAb", + "acCAb", + 4 + ], + [ + "CacAcAa", + "ACbcC", + 10 + ], + [ + "CacAcBc", + "caABbAAAA", + 13 + ], + [ + "CacBBBC", + "B", + 12 + ], + [ + "CacBCa", + "CcbcBA", + 7 + ], + [ + "CacBaCbca", + "aaBaaaBc", + 11 + ], + [ + "CacBbbB", + "CaCbbaBbb", + 7 + ], + [ + "CacBc", + "bBbAC", + 9 + ], + [ + "CacC", + "AcC", + 3 + ], + [ + "CacCbaAcc", + "bCaAaCB", + 13 + ], + [ + "CacaAc", + "aAbC", + 8 + ], + [ + "Cacab", + "AaBCAa", + 8 + ], + [ + "Cacab", + "c", + 8 + ], + [ + "CacacC", + "AABCCAA", + 12 + ], + [ + "Cacb", + "ac", + 4 + ], + [ + "CacbB", + "BCbcBaa", + 9 + ], + [ + "CacbC", + "bBbCaBBB", + 11 + ], + [ + "CacbC", + "cCAAcCa", + 9 + ], + [ + "CacbCAccB", + "AAaBBcbBc", + 14 + ], + [ + "Cacc", + "AACc", + 4 + ], + [ + "CaccAC", + "BabBAaC", + 8 + ], + [ + "CaccBca", + "aCbb", + 10 + ], + [ + "CaccaAbbC", + "BBbC", + 13 + ], + [ + "CaccaCCa", + "cc", + 12 + ], + [ + "Cb", + "A", + 4 + ], + [ + "Cb", + "AACB", + 5 + ], + [ + "Cb", + "AACCbcBc", + 12 + ], + [ + "Cb", + "ABcAcaCcC", + 16 + ], + [ + "Cb", + "ACCBAaCaA", + 15 + ], + [ + "Cb", + "AaBCBCc", + 11 + ], + [ + "Cb", + "Aaa", + 6 + ], + [ + "Cb", + "AacabCcB", + 13 + ], + [ + "Cb", + "Ac", + 4 + ], + [ + "Cb", + "BABB", + 7 + ], + [ + "Cb", + "BBcAabC", + 11 + ], + [ + "Cb", + "Ba", + 4 + ], + [ + "Cb", + "BaaB", + 7 + ], + [ + "Cb", + "BbCaabCCC", + 14 + ], + [ + "Cb", + "CA", + 2 + ], + [ + "Cb", + "CCcBaAB", + 11 + ], + [ + "Cb", + "Cbb", + 2 + ], + [ + "Cb", + "CcBccBacC", + 15 + ], + [ + "Cb", + "aBcaCb", + 8 + ], + [ + "Cb", + "aCBCcAACA", + 15 + ], + [ + "Cb", + "aaAbc", + 8 + ], + [ + "Cb", + "aaCBBaB", + 11 + ], + [ + "Cb", + "aaaB", + 7 + ], + [ + "Cb", + "abBaC", + 8 + ], + [ + "Cb", + "abaaaCABA", + 15 + ], + [ + "Cb", + "acBbAAca", + 13 + ], + [ + "Cb", + "acbbabbCC", + 15 + ], + [ + "Cb", + "bcCCBA", + 9 + ], + [ + "Cb", + "c", + 3 + ], + [ + "Cb", + "cAbcbCA", + 11 + ], + [ + "Cb", + "cAcCbBA", + 10 + ], + [ + "Cb", + "cB", + 2 + ], + [ + "Cb", + "cBcB", + 6 + ], + [ + "Cb", + "caacb", + 7 + ], + [ + "Cb", + "cbCBBbacA", + 14 + ], + [ + "Cb", + "cbcbccA", + 11 + ], + [ + "CbA", + "AAC", + 6 + ], + [ + "CbA", + "BBCBaCC", + 10 + ], + [ + "CbA", + "BBcBB", + 8 + ], + [ + "CbA", + "baaA", + 6 + ], + [ + "CbA", + "c", + 5 + ], + [ + "CbA", + "caBAbA", + 7 + ], + [ + "CbAABaAC", + "A", + 14 + ], + [ + "CbAACACcC", + "ba", + 15 + ], + [ + "CbAACa", + "AaCaAccab", + 11 + ], + [ + "CbAAaAB", + "BCaBCcaaC", + 12 + ], + [ + "CbAAb", + "bacAabaAB", + 11 + ], + [ + "CbAAcAb", + "caC", + 11 + ], + [ + "CbAAcC", + "ABbcaCa", + 10 + ], + [ + "CbABACcaB", + "Babb", + 14 + ], + [ + "CbABC", + "b", + 8 + ], + [ + "CbABaBaAC", + "acaC", + 12 + ], + [ + "CbAC", + "A", + 6 + ], + [ + "CbAC", + "c", + 7 + ], + [ + "CbACaB", + "aBcA", + 9 + ], + [ + "CbACb", + "bcABcCC", + 10 + ], + [ + "CbACc", + "BbabcaBCA", + 13 + ], + [ + "CbACcABba", + "baABAcBc", + 12 + ], + [ + "CbAaAaaa", + "ACbBACbbc", + 13 + ], + [ + "CbAaabbC", + "A", + 14 + ], + [ + "CbAacB", + "c", + 10 + ], + [ + "CbAacBB", + "CacbBBbBA", + 12 + ], + [ + "CbAb", + "baBac", + 8 + ], + [ + "CbAbBaC", + "aCAaACb", + 11 + ], + [ + "CbAba", + "CbaA", + 4 + ], + [ + "CbAbb", + "Bc", + 9 + ], + [ + "CbAc", + "a", + 7 + ], + [ + "CbAcAC", + "bcbCB", + 8 + ], + [ + "CbAcB", + "aac", + 7 + ], + [ + "CbAcBCcaa", + "ccABcacAc", + 11 + ], + [ + "CbAcCb", + "AC", + 8 + ], + [ + "CbAcaAAA", + "accCbbACC", + 15 + ], + [ + "CbAcc", + "AABbcBBc", + 11 + ], + [ + "CbB", + "ABcCCBb", + 10 + ], + [ + "CbB", + "B", + 4 + ], + [ + "CbB", + "BB", + 3 + ], + [ + "CbB", + "BBb", + 4 + ], + [ + "CbB", + "ababacC", + 11 + ], + [ + "CbB", + "bAaaABc", + 12 + ], + [ + "CbB", + "cCb", + 4 + ], + [ + "CbBABACb", + "BCcBAA", + 10 + ], + [ + "CbBABBaA", + "BacaabAc", + 13 + ], + [ + "CbBAb", + "cB", + 7 + ], + [ + "CbBB", + "CcBCBCcAC", + 12 + ], + [ + "CbBBB", + "BCCcb", + 9 + ], + [ + "CbBBaC", + "ab", + 10 + ], + [ + "CbBBcCCBb", + "B", + 16 + ], + [ + "CbBCCbbB", + "cC", + 13 + ], + [ + "CbBa", + "a", + 6 + ], + [ + "CbBa", + "aAb", + 7 + ], + [ + "CbBabA", + "CcCB", + 9 + ], + [ + "CbBb", + "aBccCBA", + 11 + ], + [ + "CbBbC", + "CCCbbC", + 5 + ], + [ + "CbBbbba", + "BaCCABCcC", + 15 + ], + [ + "CbBbc", + "C", + 8 + ], + [ + "CbBc", + "aAaAAbBA", + 12 + ], + [ + "CbBcB", + "b", + 8 + ], + [ + "CbC", + "AcacbAB", + 11 + ], + [ + "CbC", + "BCAc", + 5 + ], + [ + "CbC", + "Bc", + 4 + ], + [ + "CbC", + "CbB", + 2 + ], + [ + "CbC", + "b", + 4 + ], + [ + "CbCAAbb", + "bAAaC", + 8 + ], + [ + "CbCABcaC", + "aCcAACB", + 12 + ], + [ + "CbCACA", + "baAac", + 8 + ], + [ + "CbCAa", + "cCbabBb", + 10 + ], + [ + "CbCB", + "BAcbcAba", + 11 + ], + [ + "CbCBBb", + "cAbACcA", + 11 + ], + [ + "CbCBC", + "cacCccCb", + 11 + ], + [ + "CbCBCAB", + "bBBcC", + 9 + ], + [ + "CbCBcCAaa", + "aAAaAa", + 14 + ], + [ + "CbCCCc", + "BcBCaba", + 10 + ], + [ + "CbCCbAccA", + "cCbBB", + 13 + ], + [ + "CbCCcCCaB", + "bcaaab", + 11 + ], + [ + "CbCa", + "cBBaCacA", + 10 + ], + [ + "CbCaAaB", + "acaa", + 9 + ], + [ + "CbCaB", + "ACbaa", + 6 + ], + [ + "CbCaB", + "AcbAa", + 7 + ], + [ + "CbCaBCb", + "bcA", + 10 + ], + [ + "CbCaa", + "ABCBCcAb", + 10 + ], + [ + "CbCaa", + "ba", + 6 + ], + [ + "CbCaaaC", + "bAcBcBaAa", + 12 + ], + [ + "CbCb", + "Bbc", + 5 + ], + [ + "CbCbaBa", + "AbacBbabc", + 10 + ], + [ + "CbCbcC", + "Ca", + 10 + ], + [ + "CbCc", + "ACC", + 5 + ], + [ + "CbCc", + "Cabbb", + 6 + ], + [ + "CbCcAaBaA", + "cbC", + 13 + ], + [ + "CbCcAcAc", + "AcBaCaAC", + 11 + ], + [ + "CbCcBAb", + "c", + 12 + ], + [ + "CbCcBC", + "CacBbCAc", + 10 + ], + [ + "CbCcBac", + "AAbAC", + 11 + ], + [ + "Cba", + "ABBBbaab", + 12 + ], + [ + "Cba", + "AcbAbB", + 8 + ], + [ + "Cba", + "C", + 4 + ], + [ + "Cba", + "CCCbbbC", + 10 + ], + [ + "Cba", + "CCaaAa", + 8 + ], + [ + "Cba", + "CcBCCAAAA", + 14 + ], + [ + "Cba", + "aABbaC", + 8 + ], + [ + "CbaA", + "CAbCBaB", + 8 + ], + [ + "CbaA", + "CBb", + 5 + ], + [ + "CbaABAC", + "aBCBCBc", + 12 + ], + [ + "CbaAaBCaB", + "AcCcB", + 12 + ], + [ + "CbaAcBb", + "C", + 12 + ], + [ + "CbaAcc", + "acaCaaccA", + 10 + ], + [ + "CbaBA", + "ab", + 7 + ], + [ + "CbaBBcBaB", + "bB", + 14 + ], + [ + "CbaBa", + "CBC", + 6 + ], + [ + "CbaBb", + "CCCbcB", + 8 + ], + [ + "CbaBba", + "BC", + 10 + ], + [ + "CbaBcAcbC", + "cc", + 14 + ], + [ + "CbaC", + "B", + 7 + ], + [ + "CbaCA", + "BAbaAacA", + 9 + ], + [ + "CbaCABBb", + "cAB", + 11 + ], + [ + "CbaCABcbA", + "BbaCbC", + 10 + ], + [ + "CbaCAaCc", + "a", + 14 + ], + [ + "CbaCAcCba", + "BCcaB", + 12 + ], + [ + "CbaCbCA", + "ccBBAcBc", + 12 + ], + [ + "CbaaA", + "BA", + 7 + ], + [ + "CbaaBA", + "AcccAAaac", + 14 + ], + [ + "CbabAACaC", + "cABaCcbB", + 13 + ], + [ + "CbabACa", + "A", + 12 + ], + [ + "CbabAac", + "aaaB", + 9 + ], + [ + "CbabAcC", + "cbb", + 9 + ], + [ + "CbabBBBcc", + "c", + 16 + ], + [ + "CbabBCCab", + "bB", + 14 + ], + [ + "CbabC", + "CacBB", + 7 + ], + [ + "CbabbBbb", + "BaAABCC", + 11 + ], + [ + "Cbabc", + "aACbC", + 7 + ], + [ + "CbabcB", + "CBcbAcb", + 6 + ], + [ + "CbacAab", + "c", + 12 + ], + [ + "CbacCAB", + "cCC", + 10 + ], + [ + "CbacCCb", + "caaC", + 9 + ], + [ + "CbacCaCAB", + "Cbbba", + 12 + ], + [ + "Cbacaca", + "BACAc", + 8 + ], + [ + "Cbb", + "Ababcb", + 8 + ], + [ + "Cbb", + "Bb", + 3 + ], + [ + "Cbb", + "CACbbBCb", + 10 + ], + [ + "Cbb", + "CBaABccaA", + 14 + ], + [ + "Cbb", + "abcb", + 4 + ], + [ + "Cbb", + "bACBBCCaa", + 14 + ], + [ + "Cbb", + "caCCACBaB", + 14 + ], + [ + "Cbb", + "cbAcABac", + 12 + ], + [ + "CbbA", + "cAACAA", + 9 + ], + [ + "CbbAACbC", + "ccaABC", + 9 + ], + [ + "CbbABCCA", + "CBCABCA", + 5 + ], + [ + "CbbAb", + "B", + 9 + ], + [ + "CbbAbbac", + "bCBbca", + 10 + ], + [ + "CbbAcaCbC", + "CAbBbcAc", + 11 + ], + [ + "CbbB", + "A", + 8 + ], + [ + "CbbB", + "C", + 6 + ], + [ + "CbbBA", + "BBCabAB", + 10 + ], + [ + "CbbBAbcA", + "CcCCAaBb", + 12 + ], + [ + "CbbBAcCba", + "BBbbCaBA", + 11 + ], + [ + "CbbBaBcB", + "cCabACB", + 10 + ], + [ + "CbbCCacBc", + "cbbAcacab", + 8 + ], + [ + "CbbaAacC", + "bcbcbbaBa", + 13 + ], + [ + "CbbaCBAb", + "aBCBBcC", + 13 + ], + [ + "CbbbBb", + "BA", + 10 + ], + [ + "CbbbaA", + "aacCccBbB", + 15 + ], + [ + "CbbbaACba", + "aCBba", + 12 + ], + [ + "Cbbbab", + "AABAbbc", + 11 + ], + [ + "Cbbc", + "bcaAb", + 9 + ], + [ + "Cbbc", + "cba", + 5 + ], + [ + "CbbcBcAc", + "BbC", + 12 + ], + [ + "CbbcCBacB", + "AaC", + 15 + ], + [ + "Cbbcbb", + "bBaBCBA", + 10 + ], + [ + "CbbcbcabC", + "AcBcBcba", + 10 + ], + [ + "Cbbcc", + "B", + 9 + ], + [ + "Cbbcc", + "CcbA", + 6 + ], + [ + "CbbccBAaB", + "cCA", + 13 + ], + [ + "Cbbccc", + "BcA", + 9 + ], + [ + "Cbc", + "A", + 6 + ], + [ + "Cbc", + "BAa", + 6 + ], + [ + "Cbc", + "BaabcacC", + 12 + ], + [ + "Cbc", + "CBbAc", + 4 + ], + [ + "Cbc", + "aCCbab", + 8 + ], + [ + "Cbc", + "baB", + 6 + ], + [ + "Cbc", + "bcB", + 4 + ], + [ + "Cbc", + "caACbaC", + 9 + ], + [ + "Cbc", + "cbabc", + 5 + ], + [ + "CbcAB", + "AabCABAb", + 9 + ], + [ + "CbcAaaBa", + "aACA", + 12 + ], + [ + "CbcB", + "CBcC", + 3 + ], + [ + "CbcBCbB", + "CAAacC", + 11 + ], + [ + "CbcBb", + "aaCACAAcC", + 15 + ], + [ + "CbcBcb", + "AabcC", + 9 + ], + [ + "CbcBcbC", + "aCa", + 13 + ], + [ + "CbcCAAc", + "aaBBbbc", + 12 + ], + [ + "CbcCAB", + "bbaCCcc", + 9 + ], + [ + "CbcCB", + "c", + 8 + ], + [ + "CbcCBB", + "aB", + 10 + ], + [ + "CbcCCa", + "BB", + 11 + ], + [ + "CbcCcA", + "aCaacCaAa", + 10 + ], + [ + "Cbca", + "cABAAaaA", + 12 + ], + [ + "CbcaC", + "bCcabCa", + 8 + ], + [ + "Cbcb", + "AA", + 8 + ], + [ + "Cbcb", + "AaCc", + 7 + ], + [ + "Cbcb", + "CbBAa", + 6 + ], + [ + "CbcbA", + "bccbACcC", + 10 + ], + [ + "CbcbB", + "aC", + 9 + ], + [ + "CbcbBCBC", + "cBB", + 10 + ], + [ + "CbcbC", + "a", + 10 + ], + [ + "Cbcbb", + "cccAB", + 6 + ], + [ + "Cbcbcbc", + "CbaccAcCC", + 9 + ], + [ + "Cbcc", + "BbAcAabc", + 10 + ], + [ + "Cbcc", + "bbBBA", + 8 + ], + [ + "CbccCAcBA", + "acCA", + 12 + ], + [ + "CbccaACc", + "AC", + 12 + ], + [ + "Cc", + "AAbABC", + 11 + ], + [ + "Cc", + "AAcbCAca", + 12 + ], + [ + "Cc", + "ABaC", + 7 + ], + [ + "Cc", + "AC", + 3 + ], + [ + "Cc", + "ACCcCa", + 8 + ], + [ + "Cc", + "AaCCC", + 7 + ], + [ + "Cc", + "BBCBCbbb", + 13 + ], + [ + "Cc", + "BbAbcC", + 10 + ], + [ + "Cc", + "Bc", + 2 + ], + [ + "Cc", + "Bccba", + 7 + ], + [ + "Cc", + "CABACbAcB", + 14 + ], + [ + "Cc", + "CAaCbCA", + 11 + ], + [ + "Cc", + "CCCCAaC", + 11 + ], + [ + "Cc", + "CCbCaAb", + 11 + ], + [ + "Cc", + "Ccb", + 2 + ], + [ + "Cc", + "a", + 4 + ], + [ + "Cc", + "aAABCBacb", + 14 + ], + [ + "Cc", + "aAAccbC", + 11 + ], + [ + "Cc", + "aBBCbc", + 8 + ], + [ + "Cc", + "aCAcaCca", + 12 + ], + [ + "Cc", + "bB", + 4 + ], + [ + "Cc", + "baABAABbC", + 17 + ], + [ + "Cc", + "bbacA", + 8 + ], + [ + "Cc", + "bc", + 2 + ], + [ + "Cc", + "c", + 2 + ], + [ + "Cc", + "cAaAAbc", + 11 + ], + [ + "Cc", + "cBbbBbcA", + 13 + ], + [ + "Cc", + "ca", + 3 + ], + [ + "Cc", + "ccAC", + 5 + ], + [ + "Cc", + "ccCb", + 5 + ], + [ + "CcA", + "BbcbaAB", + 10 + ], + [ + "CcA", + "aCA", + 3 + ], + [ + "CcA", + "bAAbca", + 9 + ], + [ + "CcA", + "bBccA", + 5 + ], + [ + "CcA", + "bCCBb", + 7 + ], + [ + "CcA", + "cBAbaC", + 9 + ], + [ + "CcAA", + "bcACbc", + 8 + ], + [ + "CcAAAAC", + "abCa", + 12 + ], + [ + "CcAAAB", + "bBAbaC", + 9 + ], + [ + "CcAAAcCb", + "cAcBcBBC", + 11 + ], + [ + "CcAABaC", + "BbC", + 10 + ], + [ + "CcAABbc", + "BCaAbab", + 9 + ], + [ + "CcAACB", + "b", + 11 + ], + [ + "CcAAaC", + "CAbaab", + 7 + ], + [ + "CcAB", + "BbcCBc", + 8 + ], + [ + "CcAB", + "accAaa", + 7 + ], + [ + "CcABACACb", + "abBBbCAA", + 12 + ], + [ + "CcABBccA", + "cc", + 12 + ], + [ + "CcABb", + "a", + 9 + ], + [ + "CcABbabBA", + "cCA", + 14 + ], + [ + "CcABcAaa", + "Ba", + 12 + ], + [ + "CcACAbc", + "AbBB", + 11 + ], + [ + "CcACbaCA", + "abbBaBbBa", + 16 + ], + [ + "CcACcCAa", + "AaB", + 13 + ], + [ + "CcAa", + "aAbBCCc", + 13 + ], + [ + "CcAa", + "ccBC", + 5 + ], + [ + "CcAaA", + "cbcC", + 8 + ], + [ + "CcAaB", + "babCBbCAC", + 15 + ], + [ + "CcAaCABA", + "cbaBaCCCb", + 12 + ], + [ + "CcAaCcbcB", + "aBbaCAAC", + 13 + ], + [ + "CcAac", + "Cabaac", + 5 + ], + [ + "CcAacBAaC", + "bCACB", + 12 + ], + [ + "CcAb", + "b", + 6 + ], + [ + "CcAbBcBaa", + "CBc", + 12 + ], + [ + "CcAbaaA", + "cCa", + 10 + ], + [ + "CcAbcaB", + "bcA", + 9 + ], + [ + "CcAc", + "AAaaABABA", + 16 + ], + [ + "CcAcAB", + "aabAabCcb", + 14 + ], + [ + "CcAcCcC", + "cACbbac", + 10 + ], + [ + "CcAcCcc", + "A", + 12 + ], + [ + "CcB", + "AbcBBCAac", + 14 + ], + [ + "CcB", + "Ac", + 4 + ], + [ + "CcB", + "Ccb", + 1 + ], + [ + "CcB", + "ba", + 6 + ], + [ + "CcB", + "cab", + 4 + ], + [ + "CcB", + "cbc", + 5 + ], + [ + "CcBABc", + "cAAC", + 7 + ], + [ + "CcBACb", + "ac", + 10 + ], + [ + "CcBAaAa", + "aCCcB", + 12 + ], + [ + "CcBB", + "bab", + 7 + ], + [ + "CcBBA", + "B", + 8 + ], + [ + "CcBBAb", + "aBCAAc", + 10 + ], + [ + "CcBBCAAC", + "A", + 14 + ], + [ + "CcBBbac", + "Bb", + 10 + ], + [ + "CcBCBaa", + "bbbbB", + 11 + ], + [ + "CcBCCaAbC", + "aAaCaCA", + 14 + ], + [ + "CcBCCbABb", + "BbBCbccaC", + 14 + ], + [ + "CcBCaacCB", + "CAbCcbA", + 11 + ], + [ + "CcBCc", + "C", + 8 + ], + [ + "CcBCc", + "cBcba", + 7 + ], + [ + "CcBCccbb", + "abaAAAa", + 15 + ], + [ + "CcBaBb", + "BAabCcBcB", + 12 + ], + [ + "CcBaa", + "abBac", + 6 + ], + [ + "CcBabaabc", + "bCB", + 15 + ], + [ + "CcBacBC", + "aa", + 12 + ], + [ + "CcBb", + "AAB", + 6 + ], + [ + "CcBb", + "BaAcbA", + 9 + ], + [ + "CcBbC", + "A", + 10 + ], + [ + "CcBbaCbb", + "abcBCCBab", + 11 + ], + [ + "CcBbbaB", + "baCb", + 11 + ], + [ + "CcBc", + "aBabCbbcB", + 13 + ], + [ + "CcBc", + "cabcACa", + 10 + ], + [ + "CcBcABAC", + "AAC", + 10 + ], + [ + "CcBcBCBaa", + "CBA", + 13 + ], + [ + "CcC", + "CBAacacBa", + 13 + ], + [ + "CcC", + "CBbcac", + 7 + ], + [ + "CcCAA", + "cB", + 8 + ], + [ + "CcCABC", + "ACA", + 8 + ], + [ + "CcCACCb", + "CbAbCccC", + 11 + ], + [ + "CcCAaCAaB", + "ABbAAaBb", + 12 + ], + [ + "CcCAbB", + "abb", + 8 + ], + [ + "CcCAbCBc", + "cbaCBaB", + 11 + ], + [ + "CcCB", + "ABbCBAc", + 10 + ], + [ + "CcCB", + "bcaabaccc", + 14 + ], + [ + "CcCB", + "cbBaC", + 8 + ], + [ + "CcCBA", + "CbAbcb", + 9 + ], + [ + "CcCBCcCA", + "BbCcaa", + 10 + ], + [ + "CcCBb", + "aBBC", + 8 + ], + [ + "CcCBc", + "BCBb", + 6 + ], + [ + "CcCC", + "b", + 8 + ], + [ + "CcCCAB", + "CCbB", + 6 + ], + [ + "CcCCBACCa", + "ABAABAAAb", + 14 + ], + [ + "CcCCacbB", + "aACA", + 13 + ], + [ + "CcCaA", + "baa", + 7 + ], + [ + "CcCaaaaA", + "bCbbACC", + 13 + ], + [ + "CcCacAB", + "BCCBacCcb", + 10 + ], + [ + "CcCacbC", + "ACBabB", + 9 + ], + [ + "CcCbB", + "BABCbb", + 7 + ], + [ + "CcCbCA", + "aBcA", + 8 + ], + [ + "CcCbCbBbc", + "ccabB", + 11 + ], + [ + "CcCba", + "BCb", + 6 + ], + [ + "CcCbaAbB", + "aBbcaCB", + 11 + ], + [ + "CcCbcaC", + "CcBbaCbCb", + 9 + ], + [ + "CcCc", + "cBAACa", + 9 + ], + [ + "CcCcAB", + "aCBbA", + 9 + ], + [ + "CcCcAc", + "baBacaB", + 11 + ], + [ + "CcCcBB", + "bcbBbcAbB", + 11 + ], + [ + "CcCcCCAb", + "BCBBaABab", + 13 + ], + [ + "CcCca", + "bCaBAB", + 10 + ], + [ + "CcCcaB", + "aAacCCb", + 10 + ], + [ + "CcCcbCb", + "BBBCBaCC", + 12 + ], + [ + "CcCcbCb", + "cC", + 10 + ], + [ + "CcCccCbbc", + "cabBaCB", + 14 + ], + [ + "Cca", + "AbbaBABCA", + 16 + ], + [ + "Cca", + "AcacBcc", + 10 + ], + [ + "Cca", + "a", + 4 + ], + [ + "Cca", + "b", + 6 + ], + [ + "Cca", + "bAbaCBB", + 12 + ], + [ + "CcaAB", + "B", + 8 + ], + [ + "CcaABA", + "a", + 10 + ], + [ + "CcaAC", + "BaCbAbaAa", + 12 + ], + [ + "CcaACB", + "BcBbAAA", + 10 + ], + [ + "CcaACCA", + "bCacCB", + 8 + ], + [ + "CcaAa", + "cCC", + 8 + ], + [ + "CcaAbCCC", + "AbCBCb", + 10 + ], + [ + "CcaAcb", + "bcBAa", + 8 + ], + [ + "CcaAcc", + "babAAAcbB", + 13 + ], + [ + "CcaB", + "BbaCbBbCB", + 14 + ], + [ + "CcaBA", + "ACCCABCBC", + 12 + ], + [ + "CcaBaBC", + "bacAbAab", + 11 + ], + [ + "CcaBaa", + "A", + 11 + ], + [ + "CcaBcBAB", + "Ac", + 13 + ], + [ + "CcaBcC", + "A", + 11 + ], + [ + "CcaCAaabB", + "CABCacccB", + 11 + ], + [ + "CcaCCAAA", + "BCAaCCcc", + 10 + ], + [ + "CcaCa", + "BAa", + 7 + ], + [ + "CcaCabAcb", + "BaaaBAC", + 10 + ], + [ + "CcaCacbba", + "bccAB", + 13 + ], + [ + "CcaCbbA", + "CcB", + 9 + ], + [ + "CcaCcBc", + "cbaCa", + 9 + ], + [ + "Ccaa", + "AabAcaC", + 10 + ], + [ + "CcaaA", + "aaCAA", + 7 + ], + [ + "CcaaAAbc", + "aABCaCacc", + 13 + ], + [ + "CcaaBC", + "abBCaC", + 10 + ], + [ + "Ccaaa", + "cc", + 7 + ], + [ + "CcaaaAB", + "bc", + 12 + ], + [ + "Ccaaaa", + "ABbbbb", + 12 + ], + [ + "Ccaaabccc", + "cA", + 15 + ], + [ + "CcaabbCCA", + "Bbc", + 14 + ], + [ + "Ccab", + "AC", + 7 + ], + [ + "Ccab", + "Cba", + 4 + ], + [ + "Ccab", + "abBAaBB", + 11 + ], + [ + "Ccab", + "abbBCaBb", + 11 + ], + [ + "CcabACcb", + "cCAaC", + 10 + ], + [ + "Ccac", + "cbCCA", + 8 + ], + [ + "CcacB", + "cCBbaCB", + 7 + ], + [ + "CcacBabBC", + "CaBBaB", + 8 + ], + [ + "CcacC", + "AbbacBa", + 10 + ], + [ + "CcacCABB", + "ba", + 14 + ], + [ + "CcacCaA", + "CaAC", + 8 + ], + [ + "CcacaBB", + "B", + 12 + ], + [ + "CcacbC", + "cCb", + 7 + ], + [ + "Ccb", + "AcbABaCC", + 12 + ], + [ + "Ccb", + "CAA", + 4 + ], + [ + "Ccb", + "CbAcC", + 6 + ], + [ + "Ccb", + "c", + 4 + ], + [ + "CcbA", + "BccAc", + 6 + ], + [ + "CcbA", + "aabCBb", + 10 + ], + [ + "CcbA", + "bABb", + 7 + ], + [ + "CcbAACA", + "bcc", + 11 + ], + [ + "CcbAACB", + "acBBAcac", + 10 + ], + [ + "CcbAAaA", + "ccBCaCa", + 8 + ], + [ + "CcbACBA", + "A", + 12 + ], + [ + "CcbACC", + "bbaC", + 7 + ], + [ + "CcbAaBcAA", + "bAcac", + 11 + ], + [ + "CcbAc", + "AcbbBBBa", + 12 + ], + [ + "CcbBAbCcC", + "AAb", + 14 + ], + [ + "CcbBaCC", + "ABcA", + 11 + ], + [ + "CcbBac", + "BCAAA", + 10 + ], + [ + "CcbBbBBA", + "AbB", + 12 + ], + [ + "CcbBbbbbb", + "bb", + 14 + ], + [ + "CcbBc", + "cBAcacB", + 11 + ], + [ + "CcbBcAC", + "BbCcAbcbB", + 11 + ], + [ + "CcbBcaA", + "bbcca", + 8 + ], + [ + "CcbBcc", + "bbcc", + 5 + ], + [ + "CcbCA", + "a", + 9 + ], + [ + "CcbCA", + "aabAA", + 6 + ], + [ + "CcbCbB", + "CACab", + 7 + ], + [ + "CcbCbCA", + "BcaCaCAb", + 8 + ], + [ + "CcbCc", + "AC", + 8 + ], + [ + "CcbCc", + "cBbbc", + 5 + ], + [ + "CcbCcC", + "c", + 10 + ], + [ + "CcbaccBCb", + "bbCBA", + 13 + ], + [ + "CcbbABb", + "CbCbacB", + 8 + ], + [ + "CcbbaAbbB", + "bc", + 16 + ], + [ + "Ccbbb", + "CBAcACB", + 9 + ], + [ + "CcbbbC", + "cBacabCb", + 11 + ], + [ + "CcbbbCbc", + "BbCAaBaB", + 15 + ], + [ + "CcbbcABBa", + "BcAcCcBCB", + 13 + ], + [ + "Ccbc", + "ABAaaBAb", + 15 + ], + [ + "Ccc", + "B", + 6 + ], + [ + "Ccc", + "BABa", + 8 + ], + [ + "Ccc", + "BcaBAc", + 8 + ], + [ + "Ccc", + "CBB", + 4 + ], + [ + "Ccc", + "CC", + 3 + ], + [ + "Ccc", + "aaaaCC", + 10 + ], + [ + "Ccc", + "ac", + 4 + ], + [ + "Ccc", + "bb", + 6 + ], + [ + "CccABA", + "AAbBcA", + 10 + ], + [ + "CccACCB", + "AcbaC", + 9 + ], + [ + "CccAaB", + "baCaBcCa", + 12 + ], + [ + "CccAb", + "aABBc", + 10 + ], + [ + "CccAcbbca", + "c", + 16 + ], + [ + "CccBBBcB", + "bBbbCa", + 11 + ], + [ + "CccBBbC", + "Bba", + 10 + ], + [ + "CccBa", + "B", + 8 + ], + [ + "CccBa", + "caBA", + 5 + ], + [ + "CccBaaAB", + "C", + 14 + ], + [ + "CccBbBCb", + "a", + 16 + ], + [ + "CccBbca", + "BacAbCBbC", + 12 + ], + [ + "CccCAa", + "CcBcBCCc", + 8 + ], + [ + "CccCCa", + "Ba", + 10 + ], + [ + "CccCa", + "aA", + 9 + ], + [ + "CccCb", + "baA", + 10 + ], + [ + "Ccca", + "A", + 7 + ], + [ + "CccaABbBb", + "aAcB", + 12 + ], + [ + "CccaBBCb", + "BBbaaCCC", + 12 + ], + [ + "Cccaa", + "BbbC", + 10 + ], + [ + "CccacCBb", + "ccBa", + 10 + ], + [ + "CccbABBbb", + "B", + 16 + ], + [ + "Cccbbc", + "CCcBcBa", + 7 + ], + [ + "Cccc", + "BaCbb", + 9 + ], + [ + "CcccAB", + "aCBAB", + 7 + ], + [ + "CcccAbbA", + "BCBcBc", + 12 + ], + [ + "CcccBacc", + "BCAcB", + 11 + ], + [ + "CcccBcCB", + "cCAb", + 12 + ], + [ + "a", + "AA", + 3 + ], + [ + "a", + "AABaCbca", + 14 + ], + [ + "a", + "AACaBBAC", + 14 + ], + [ + "a", + "ABCaa", + 8 + ], + [ + "a", + "ABaaBCCb", + 14 + ], + [ + "a", + "ABab", + 6 + ], + [ + "a", + "ABcB", + 7 + ], + [ + "a", + "ACbcCBCB", + 15 + ], + [ + "a", + "ACbcaA", + 10 + ], + [ + "a", + "AaAA", + 6 + ], + [ + "a", + "AaABacaAA", + 16 + ], + [ + "a", + "AaAbBca", + 12 + ], + [ + "a", + "AabBCaab", + 14 + ], + [ + "a", + "AabCbCb", + 12 + ], + [ + "a", + "AaccB", + 8 + ], + [ + "a", + "Ab", + 3 + ], + [ + "a", + "AbBBaB", + 10 + ], + [ + "a", + "AbCb", + 7 + ], + [ + "a", + "AbCc", + 7 + ], + [ + "a", + "AbbbBacAa", + 16 + ], + [ + "a", + "AbcCaCbac", + 16 + ], + [ + "a", + "AcABaA", + 10 + ], + [ + "a", + "AcACCA", + 11 + ], + [ + "a", + "AcCbc", + 9 + ], + [ + "a", + "AcaB", + 6 + ], + [ + "a", + "B", + 2 + ], + [ + "a", + "BA", + 3 + ], + [ + "a", + "BABAAcBBc", + 17 + ], + [ + "a", + "BABaBa", + 10 + ], + [ + "a", + "BAC", + 5 + ], + [ + "a", + "BAaAbAc", + 12 + ], + [ + "a", + "BAabccCBc", + 16 + ], + [ + "a", + "BAbaaa", + 10 + ], + [ + "a", + "BAbb", + 7 + ], + [ + "a", + "BBAcbBB", + 13 + ], + [ + "a", + "BBBaCab", + 12 + ], + [ + "a", + "BBC", + 6 + ], + [ + "a", + "BBaCbBAC", + 14 + ], + [ + "a", + "BBcca", + 8 + ], + [ + "a", + "BCA", + 5 + ], + [ + "a", + "BCBab", + 8 + ], + [ + "a", + "BaAAAcAbc", + 16 + ], + [ + "a", + "BaAcABBC", + 14 + ], + [ + "a", + "BaBcaC", + 10 + ], + [ + "a", + "BaC", + 4 + ], + [ + "a", + "BaabBAabb", + 16 + ], + [ + "a", + "BbCaCa", + 10 + ], + [ + "a", + "BbcACCA", + 13 + ], + [ + "a", + "Bbcba", + 8 + ], + [ + "a", + "BcAb", + 7 + ], + [ + "a", + "BcBABC", + 11 + ], + [ + "a", + "BcBaAacC", + 14 + ], + [ + "a", + "Bca", + 4 + ], + [ + "a", + "Bcabba", + 10 + ], + [ + "a", + "BccCbbAb", + 15 + ], + [ + "a", + "C", + 2 + ], + [ + "a", + "CA", + 3 + ], + [ + "a", + "CABAcBbc", + 15 + ], + [ + "a", + "CABCB", + 9 + ], + [ + "a", + "CAbC", + 7 + ], + [ + "a", + "CAba", + 6 + ], + [ + "a", + "CAcCAa", + 10 + ], + [ + "a", + "CAcc", + 7 + ], + [ + "a", + "CBA", + 5 + ], + [ + "a", + "CBABB", + 9 + ], + [ + "a", + "CBAa", + 6 + ], + [ + "a", + "CBBCcB", + 12 + ], + [ + "a", + "CBBcA", + 9 + ], + [ + "a", + "CBaCAC", + 10 + ], + [ + "a", + "CBbaCCbC", + 14 + ], + [ + "a", + "CCBc", + 8 + ], + [ + "a", + "CCcaC", + 8 + ], + [ + "a", + "Ca", + 2 + ], + [ + "a", + "CaAaC", + 8 + ], + [ + "a", + "CaBacbb", + 12 + ], + [ + "a", + "CaabacC", + 12 + ], + [ + "a", + "CbBc", + 8 + ], + [ + "a", + "CbbcAB", + 11 + ], + [ + "a", + "CcBBaaCc", + 14 + ], + [ + "a", + "CcBbabBAc", + 16 + ], + [ + "a", + "CcaACcB", + 12 + ], + [ + "a", + "Ccca", + 6 + ], + [ + "a", + "a", + 0 + ], + [ + "a", + "aA", + 2 + ], + [ + "a", + "aAACAbCC", + 14 + ], + [ + "a", + "aACABcAb", + 14 + ], + [ + "a", + "aAbaB", + 8 + ], + [ + "a", + "aBB", + 4 + ], + [ + "a", + "aBBCb", + 8 + ], + [ + "a", + "aBaCbb", + 10 + ], + [ + "a", + "aBb", + 4 + ], + [ + "a", + "aC", + 2 + ], + [ + "a", + "aCCCCcab", + 14 + ], + [ + "a", + "aCaBBABa", + 14 + ], + [ + "a", + "aCaC", + 6 + ], + [ + "a", + "aCb", + 4 + ], + [ + "a", + "aCcBaAa", + 12 + ], + [ + "a", + "aa", + 2 + ], + [ + "a", + "aaBbC", + 8 + ], + [ + "a", + "aaCA", + 6 + ], + [ + "a", + "ab", + 2 + ], + [ + "a", + "abACBBAcC", + 16 + ], + [ + "a", + "abBbBba", + 12 + ], + [ + "a", + "abCaAaCbC", + 16 + ], + [ + "a", + "aba", + 4 + ], + [ + "a", + "abcac", + 8 + ], + [ + "a", + "acAcBAbaB", + 16 + ], + [ + "a", + "acBcAA", + 10 + ], + [ + "a", + "accCCCc", + 12 + ], + [ + "a", + "b", + 2 + ], + [ + "a", + "bA", + 3 + ], + [ + "a", + "bAAaB", + 8 + ], + [ + "a", + "bABBa", + 8 + ], + [ + "a", + "bACca", + 8 + ], + [ + "a", + "bAbAccb", + 13 + ], + [ + "a", + "bAccbcaAB", + 16 + ], + [ + "a", + "bBAca", + 8 + ], + [ + "a", + "bBBC", + 8 + ], + [ + "a", + "bBBCaccC", + 14 + ], + [ + "a", + "bBCb", + 8 + ], + [ + "a", + "bBbC", + 8 + ], + [ + "a", + "bBbCABa", + 12 + ], + [ + "a", + "bC", + 4 + ], + [ + "a", + "bCBacAcC", + 14 + ], + [ + "a", + "bCC", + 6 + ], + [ + "a", + "bCCbaccA", + 14 + ], + [ + "a", + "bCbA", + 7 + ], + [ + "a", + "bCbBab", + 10 + ], + [ + "a", + "bCcaCbc", + 12 + ], + [ + "a", + "bCcaaCbB", + 14 + ], + [ + "a", + "ba", + 2 + ], + [ + "a", + "baa", + 4 + ], + [ + "a", + "baacacaba", + 16 + ], + [ + "a", + "babcA", + 8 + ], + [ + "a", + "babcCAA", + 12 + ], + [ + "a", + "bb", + 4 + ], + [ + "a", + "bbAc", + 7 + ], + [ + "a", + "bbB", + 6 + ], + [ + "a", + "bbBA", + 7 + ], + [ + "a", + "bbBBcaBc", + 14 + ], + [ + "a", + "bbBbAaAAc", + 16 + ], + [ + "a", + "bbCCaACb", + 14 + ], + [ + "a", + "bbCaca", + 10 + ], + [ + "a", + "bbaC", + 6 + ], + [ + "a", + "bbbAcAB", + 13 + ], + [ + "a", + "bbbBAABC", + 15 + ], + [ + "a", + "bbcCBcB", + 14 + ], + [ + "a", + "bcAca", + 8 + ], + [ + "a", + "bcAcacCB", + 14 + ], + [ + "a", + "bcaAcBAbC", + 16 + ], + [ + "a", + "bcb", + 6 + ], + [ + "a", + "bcc", + 6 + ], + [ + "a", + "c", + 2 + ], + [ + "a", + "cAC", + 5 + ], + [ + "a", + "cACBACca", + 14 + ], + [ + "a", + "cACaCa", + 10 + ], + [ + "a", + "cAa", + 4 + ], + [ + "a", + "cBAaa", + 8 + ], + [ + "a", + "cBbBbab", + 12 + ], + [ + "a", + "cC", + 4 + ], + [ + "a", + "cCBA", + 7 + ], + [ + "a", + "cCCa", + 6 + ], + [ + "a", + "cCCbaa", + 10 + ], + [ + "a", + "cCaCcb", + 10 + ], + [ + "a", + "cCacac", + 10 + ], + [ + "a", + "cCcBbCBAa", + 16 + ], + [ + "a", + "cabbcaBBc", + 16 + ], + [ + "a", + "cb", + 4 + ], + [ + "a", + "cbAcAab", + 12 + ], + [ + "a", + "cbAcCB", + 11 + ], + [ + "a", + "cbBaC", + 8 + ], + [ + "a", + "cbCcbC", + 12 + ], + [ + "a", + "cbaCbbBb", + 14 + ], + [ + "a", + "cbbAaCA", + 12 + ], + [ + "a", + "ccACCaBAa", + 16 + ], + [ + "a", + "ccACbaC", + 12 + ], + [ + "a", + "ccAbAC", + 11 + ], + [ + "a", + "ccAbbaBA", + 14 + ], + [ + "a", + "ccCBaCcB", + 14 + ], + [ + "a", + "ccac", + 6 + ], + [ + "aA", + "AAc", + 3 + ], + [ + "aA", + "ABcACbCcb", + 15 + ], + [ + "aA", + "ACcbCBA", + 11 + ], + [ + "aA", + "AacACCa", + 10 + ], + [ + "aA", + "B", + 4 + ], + [ + "aA", + "BabBBcCbC", + 16 + ], + [ + "aA", + "Babb", + 6 + ], + [ + "aA", + "BcAcB", + 8 + ], + [ + "aA", + "CAacB", + 8 + ], + [ + "aA", + "CBABBaAB", + 12 + ], + [ + "aA", + "CaBbbbA", + 10 + ], + [ + "aA", + "CcAabB", + 10 + ], + [ + "aA", + "CcaAb", + 6 + ], + [ + "aA", + "a", + 2 + ], + [ + "aA", + "aCAcAb", + 8 + ], + [ + "aA", + "aCBb", + 6 + ], + [ + "aA", + "aCacaAA", + 10 + ], + [ + "aA", + "aaACBacA", + 12 + ], + [ + "aA", + "aaC", + 3 + ], + [ + "aA", + "abbAaBC", + 10 + ], + [ + "aA", + "abbBBbc", + 12 + ], + [ + "aA", + "bAc", + 4 + ], + [ + "aA", + "bB", + 4 + ], + [ + "aA", + "bBCBC", + 10 + ], + [ + "aA", + "baCBAbCa", + 12 + ], + [ + "aA", + "cABBaA", + 8 + ], + [ + "aA", + "cBc", + 6 + ], + [ + "aA", + "cabbabbA", + 12 + ], + [ + "aA", + "ccbcB", + 10 + ], + [ + "aAA", + "aACbBCCb", + 12 + ], + [ + "aAA", + "ac", + 4 + ], + [ + "aAA", + "bAc", + 4 + ], + [ + "aAAA", + "AaAC", + 4 + ], + [ + "aAAAA", + "c", + 10 + ], + [ + "aAAAAC", + "CbaaCCBBa", + 14 + ], + [ + "aAAAAaAA", + "cbAabBb", + 13 + ], + [ + "aAAABb", + "BABBaB", + 9 + ], + [ + "aAAAC", + "bC", + 8 + ], + [ + "aAAAaAb", + "AAC", + 10 + ], + [ + "aAAAabBca", + "AB", + 14 + ], + [ + "aAAAcCbCB", + "CcBbABBB", + 15 + ], + [ + "aAAB", + "A", + 6 + ], + [ + "aAAB", + "bcabAb", + 7 + ], + [ + "aAAB", + "c", + 8 + ], + [ + "aAAB", + "cCAB", + 4 + ], + [ + "aAABCcCcC", + "CbBBCAB", + 14 + ], + [ + "aAABbBACb", + "aACb", + 10 + ], + [ + "aAACC", + "ccbbaBA", + 13 + ], + [ + "aAACaCa", + "ACbabAc", + 11 + ], + [ + "aAAaAAaa", + "ACBcbbcaA", + 14 + ], + [ + "aAAaaaB", + "BABbaaAbA", + 10 + ], + [ + "aAAabaAbA", + "bcCBbaBB", + 13 + ], + [ + "aAAacaaC", + "a", + 14 + ], + [ + "aAAb", + "aCAA", + 4 + ], + [ + "aAAbBbaA", + "BaaaAbb", + 11 + ], + [ + "aAAbCbCcC", + "cBC", + 14 + ], + [ + "aAAbbA", + "bcbbaBa", + 10 + ], + [ + "aAAc", + "AccBbB", + 10 + ], + [ + "aAAcA", + "BBBABa", + 9 + ], + [ + "aAAcA", + "bb", + 10 + ], + [ + "aAAcAA", + "aCAA", + 5 + ], + [ + "aAAcAa", + "CbCAa", + 7 + ], + [ + "aAAcAaa", + "bAAACb", + 8 + ], + [ + "aAAcB", + "AbcACA", + 8 + ], + [ + "aAAcBBba", + "BcaAcACb", + 11 + ], + [ + "aAAcCCbCB", + "C", + 16 + ], + [ + "aAAcCaCc", + "caACAaAa", + 10 + ], + [ + "aAAcCb", + "bbAbA", + 10 + ], + [ + "aAAcabaab", + "cAACAbb", + 8 + ], + [ + "aAAcbACab", + "bCcAaBbCC", + 15 + ], + [ + "aAAcc", + "aaAC", + 4 + ], + [ + "aAB", + "AAaAcCaa", + 12 + ], + [ + "aAB", + "AbCBB", + 7 + ], + [ + "aAB", + "AcaAB", + 4 + ], + [ + "aAB", + "CAbaA", + 7 + ], + [ + "aAB", + "CCBAcbaCC", + 15 + ], + [ + "aAB", + "aBcbbB", + 8 + ], + [ + "aAB", + "aCCcaBCC", + 11 + ], + [ + "aAB", + "bcbccABCA", + 14 + ], + [ + "aABA", + "cCac", + 8 + ], + [ + "aABAB", + "AaaBBcCA", + 11 + ], + [ + "aABACAa", + "CcBBbA", + 10 + ], + [ + "aABAaA", + "C", + 12 + ], + [ + "aABAb", + "BbBCC", + 8 + ], + [ + "aABAb", + "ca", + 9 + ], + [ + "aABAbab", + "a", + 12 + ], + [ + "aABAc", + "AabAabaB", + 10 + ], + [ + "aABAcCbbA", + "aB", + 14 + ], + [ + "aABB", + "CCAaaccbA", + 14 + ], + [ + "aABB", + "aCac", + 6 + ], + [ + "aABB", + "cb", + 7 + ], + [ + "aABBBBCc", + "cBAcbb", + 13 + ], + [ + "aABBBBa", + "CaBaCCBB", + 11 + ], + [ + "aABBCc", + "cc", + 9 + ], + [ + "aABBacCA", + "BBbacBc", + 9 + ], + [ + "aABBbA", + "aCBbC", + 6 + ], + [ + "aABBbaB", + "CbbBCbacb", + 10 + ], + [ + "aABCAa", + "bBbC", + 9 + ], + [ + "aABCB", + "cAB", + 6 + ], + [ + "aABCCcbB", + "CBbaCcbc", + 9 + ], + [ + "aABCaca", + "Bb", + 12 + ], + [ + "aABCbCba", + "CabccBaB", + 11 + ], + [ + "aABaBA", + "BC", + 10 + ], + [ + "aABaBbAAA", + "B", + 16 + ], + [ + "aABb", + "BC", + 6 + ], + [ + "aABbAa", + "bCbBcC", + 10 + ], + [ + "aABbBbCca", + "ABCCBAbAc", + 12 + ], + [ + "aABbaA", + "bcBC", + 10 + ], + [ + "aABbac", + "AbBbABAAb", + 12 + ], + [ + "aABbccca", + "BcaA", + 11 + ], + [ + "aABcACB", + "A", + 12 + ], + [ + "aABccB", + "A", + 10 + ], + [ + "aAC", + "BA", + 4 + ], + [ + "aAC", + "CAbbaab", + 11 + ], + [ + "aAC", + "CbC", + 4 + ], + [ + "aAC", + "b", + 6 + ], + [ + "aAC", + "bACCbC", + 8 + ], + [ + "aAC", + "bcaBacc", + 10 + ], + [ + "aACABAcCa", + "ccCB", + 13 + ], + [ + "aACBA", + "Aa", + 7 + ], + [ + "aACBACA", + "BaCBa", + 8 + ], + [ + "aACBBcBC", + "A", + 14 + ], + [ + "aACCA", + "Cb", + 8 + ], + [ + "aACCAAb", + "Bca", + 12 + ], + [ + "aACCBc", + "AcbAaCB", + 9 + ], + [ + "aACCa", + "ACccABA", + 10 + ], + [ + "aACCbAA", + "AAaa", + 9 + ], + [ + "aACCc", + "BBcc", + 7 + ], + [ + "aACCc", + "cbaBAC", + 10 + ], + [ + "aACaaA", + "bcACaCbA", + 8 + ], + [ + "aACacACa", + "aaBccaB", + 10 + ], + [ + "aACacCb", + "C", + 12 + ], + [ + "aACaccCAC", + "cC", + 14 + ], + [ + "aACbAC", + "AB", + 9 + ], + [ + "aACbBBcb", + "b", + 14 + ], + [ + "aACbbbaBC", + "aBBACCB", + 13 + ], + [ + "aACcaBb", + "ABabA", + 9 + ], + [ + "aACcbCa", + "b", + 12 + ], + [ + "aAa", + "BbBb", + 8 + ], + [ + "aAa", + "CBCBAabb", + 12 + ], + [ + "aAa", + "Cc", + 6 + ], + [ + "aAa", + "bCaaCBcAa", + 12 + ], + [ + "aAa", + "bc", + 6 + ], + [ + "aAaA", + "b", + 8 + ], + [ + "aAaAcc", + "B", + 12 + ], + [ + "aAaAccAb", + "BCAbabcAA", + 11 + ], + [ + "aAaBBCb", + "Cbbc", + 11 + ], + [ + "aAaBaBBBc", + "cAcCCCA", + 16 + ], + [ + "aAaBaBa", + "bBbBc", + 10 + ], + [ + "aAaCB", + "cCc", + 8 + ], + [ + "aAaCCCc", + "bbcBabaAA", + 16 + ], + [ + "aAaCaaAc", + "BCaA", + 10 + ], + [ + "aAaa", + "bb", + 8 + ], + [ + "aAaaAcCA", + "cBbcccB", + 13 + ], + [ + "aAaaBbbb", + "bCca", + 14 + ], + [ + "aAaaC", + "aCAC", + 5 + ], + [ + "aAaaCBa", + "aaCBCc", + 8 + ], + [ + "aAaaaA", + "aBACbaBAC", + 10 + ], + [ + "aAaabBcb", + "AcCAcBA", + 12 + ], + [ + "aAab", + "C", + 8 + ], + [ + "aAab", + "bCaB", + 5 + ], + [ + "aAab", + "bbaacCCB", + 12 + ], + [ + "aAabC", + "ccbBa", + 9 + ], + [ + "aAabCAc", + "aBCb", + 9 + ], + [ + "aAabCCBaa", + "bBC", + 14 + ], + [ + "aAabbbbB", + "CBAACBCCb", + 13 + ], + [ + "aAabbcCaA", + "A", + 16 + ], + [ + "aAac", + "cCBbCAaBB", + 14 + ], + [ + "aAacB", + "cAcbcBa", + 8 + ], + [ + "aAacBcCBa", + "cba", + 13 + ], + [ + "aAb", + "A", + 4 + ], + [ + "aAb", + "a", + 4 + ], + [ + "aAb", + "b", + 4 + ], + [ + "aAbA", + "bccaaaCAB", + 13 + ], + [ + "aAbABAA", + "bCBAcabBb", + 14 + ], + [ + "aAbAC", + "cACcCa", + 8 + ], + [ + "aAbACaBAA", + "CB", + 14 + ], + [ + "aAbAb", + "cBBabc", + 8 + ], + [ + "aAbAcCb", + "c", + 12 + ], + [ + "aAbBB", + "AC", + 8 + ], + [ + "aAbBaBcAA", + "C", + 17 + ], + [ + "aAbCAcbCC", + "BAAaA", + 14 + ], + [ + "aAbCBA", + "aCBaCABc", + 9 + ], + [ + "aAbCCACCb", + "AbcacbAb", + 11 + ], + [ + "aAbCCC", + "Aca", + 9 + ], + [ + "aAbCa", + "bABc", + 6 + ], + [ + "aAbCbCa", + "AabbBCbA", + 8 + ], + [ + "aAbCbbaba", + "CcBbccaB", + 14 + ], + [ + "aAbCc", + "cCa", + 8 + ], + [ + "aAba", + "BbcbcCBcC", + 16 + ], + [ + "aAbaBA", + "aBAAAA", + 7 + ], + [ + "aAbaBBba", + "Cb", + 14 + ], + [ + "aAbaCAA", + "cAB", + 11 + ], + [ + "aAbaaBA", + "bcCbaccBA", + 10 + ], + [ + "aAbb", + "bBBaACCBC", + 13 + ], + [ + "aAbbABabC", + "cbACbB", + 12 + ], + [ + "aAbbBab", + "caaA", + 11 + ], + [ + "aAbbCAA", + "BBbBcbA", + 8 + ], + [ + "aAbbCbCCa", + "AAB", + 14 + ], + [ + "aAbbCbcb", + "CBc", + 11 + ], + [ + "aAbbaB", + "a", + 10 + ], + [ + "aAbbacAb", + "BccBb", + 11 + ], + [ + "aAbbcbA", + "A", + 12 + ], + [ + "aAbcCaA", + "CCc", + 11 + ], + [ + "aAbcaABCA", + "BCAcbCab", + 13 + ], + [ + "aAbcaAa", + "CCababB", + 11 + ], + [ + "aAbcbbB", + "baba", + 10 + ], + [ + "aAc", + "aBbBCbac", + 11 + ], + [ + "aAc", + "abbAbaC", + 9 + ], + [ + "aAcA", + "A", + 6 + ], + [ + "aAcA", + "ACABBCB", + 10 + ], + [ + "aAcAAb", + "ACC", + 9 + ], + [ + "aAcAAbB", + "cAbbBaCBb", + 13 + ], + [ + "aAcAabacA", + "aCbBcA", + 9 + ], + [ + "aAcAacaba", + "caACb", + 11 + ], + [ + "aAcAbaCac", + "bBc", + 14 + ], + [ + "aAcAcAbaB", + "bcCCa", + 13 + ], + [ + "aAcB", + "aaaCC", + 6 + ], + [ + "aAcB", + "caC", + 6 + ], + [ + "aAcBACBB", + "a", + 14 + ], + [ + "aAcBC", + "aCca", + 6 + ], + [ + "aAcBbABB", + "AA", + 12 + ], + [ + "aAcBbAaBC", + "BbCbaCcaC", + 14 + ], + [ + "aAcCAaC", + "b", + 14 + ], + [ + "aAcCCcAc", + "baabCBAaA", + 12 + ], + [ + "aAcCccBB", + "AaAa", + 14 + ], + [ + "aAcaAaB", + "AcC", + 10 + ], + [ + "aAcaBaA", + "ACcaaA", + 5 + ], + [ + "aAcaaac", + "BaABaBaBA", + 10 + ], + [ + "aAcb", + "AABbA", + 5 + ], + [ + "aAcb", + "aABC", + 4 + ], + [ + "aAcbA", + "cbcAAC", + 8 + ], + [ + "aAcbBb", + "CbcccCBcC", + 14 + ], + [ + "aAcbBcca", + "A", + 14 + ], + [ + "aAcba", + "acBcBBBAc", + 12 + ], + [ + "aAcba", + "b", + 8 + ], + [ + "aAcc", + "CACC", + 4 + ], + [ + "aAccac", + "ACABCBBAC", + 12 + ], + [ + "aAccbCaba", + "BCBbBc", + 14 + ], + [ + "aB", + "A", + 3 + ], + [ + "aB", + "AA", + 3 + ], + [ + "aB", + "AB", + 1 + ], + [ + "aB", + "ABAb", + 5 + ], + [ + "aB", + "ACcb", + 6 + ], + [ + "aB", + "Aabaaa", + 9 + ], + [ + "aB", + "AccabABB", + 12 + ], + [ + "aB", + "B", + 2 + ], + [ + "aB", + "BBcbB", + 8 + ], + [ + "aB", + "BCc", + 6 + ], + [ + "aB", + "Bca", + 6 + ], + [ + "aB", + "C", + 4 + ], + [ + "aB", + "CAAaa", + 8 + ], + [ + "aB", + "CbccACC", + 13 + ], + [ + "aB", + "Cc", + 4 + ], + [ + "aB", + "CcAcCC", + 11 + ], + [ + "aB", + "aCbBBc", + 8 + ], + [ + "aB", + "aaAbCb", + 9 + ], + [ + "aB", + "aaaAb", + 7 + ], + [ + "aB", + "aabCACc", + 11 + ], + [ + "aB", + "abAC", + 5 + ], + [ + "aB", + "bB", + 2 + ], + [ + "aB", + "bBB", + 4 + ], + [ + "aB", + "bb", + 3 + ], + [ + "aB", + "bbbca", + 9 + ], + [ + "aB", + "bbc", + 5 + ], + [ + "aB", + "bc", + 4 + ], + [ + "aB", + "bcb", + 5 + ], + [ + "aB", + "cB", + 2 + ], + [ + "aB", + "cCC", + 6 + ], + [ + "aB", + "cc", + 4 + ], + [ + "aBA", + "BAaAaaCBc", + 14 + ], + [ + "aBAAA", + "CBCAbbcAA", + 10 + ], + [ + "aBAAB", + "BacBABcAc", + 10 + ], + [ + "aBAAaB", + "caAc", + 9 + ], + [ + "aBAAacaa", + "acabBBcBC", + 13 + ], + [ + "aBAAbCcB", + "BCB", + 10 + ], + [ + "aBAAcbCc", + "cBCBBcAAB", + 14 + ], + [ + "aBAB", + "aaCc", + 6 + ], + [ + "aBAB", + "bbaAbA", + 8 + ], + [ + "aBABa", + "bacAa", + 6 + ], + [ + "aBABaABc", + "AAaCCC", + 10 + ], + [ + "aBABcaABA", + "CBcbB", + 12 + ], + [ + "aBACC", + "a", + 8 + ], + [ + "aBACa", + "c", + 9 + ], + [ + "aBACba", + "C", + 10 + ], + [ + "aBACcAA", + "B", + 12 + ], + [ + "aBAa", + "aaB", + 5 + ], + [ + "aBAaC", + "cCACbbBb", + 14 + ], + [ + "aBAabaAB", + "cacCbb", + 13 + ], + [ + "aBAabba", + "AaBABbCBc", + 9 + ], + [ + "aBAacCbbb", + "aBaCaAc", + 10 + ], + [ + "aBAacc", + "aAaAAcABC", + 10 + ], + [ + "aBAb", + "aBBbAac", + 8 + ], + [ + "aBAbAC", + "aABAbCAc", + 5 + ], + [ + "aBAbaaab", + "CCbbAaba", + 11 + ], + [ + "aBAbbB", + "aCacbAB", + 7 + ], + [ + "aBAbbcbb", + "bcBAb", + 11 + ], + [ + "aBAcABbb", + "Ca", + 14 + ], + [ + "aBAcBa", + "CbBcCaa", + 9 + ], + [ + "aBAcBbC", + "aaAaBac", + 7 + ], + [ + "aBAca", + "AACACCcAa", + 11 + ], + [ + "aBAcabBA", + "BBACb", + 9 + ], + [ + "aBAcba", + "bBaaCcB", + 10 + ], + [ + "aBAccCaba", + "CAcabBBbC", + 14 + ], + [ + "aBAcca", + "ABAABbCa", + 8 + ], + [ + "aBB", + "AaAbaAaBb", + 13 + ], + [ + "aBB", + "AaCaB", + 6 + ], + [ + "aBB", + "Acbc", + 6 + ], + [ + "aBB", + "aC", + 4 + ], + [ + "aBB", + "aa", + 4 + ], + [ + "aBB", + "bbCAAaBBB", + 12 + ], + [ + "aBBA", + "BAAc", + 6 + ], + [ + "aBBABAaAA", + "AbCAcbAA", + 10 + ], + [ + "aBBABaaaa", + "bCC", + 17 + ], + [ + "aBBABca", + "bA", + 11 + ], + [ + "aBBACC", + "caaCB", + 9 + ], + [ + "aBBAc", + "aCAA", + 6 + ], + [ + "aBBB", + "AcBcBa", + 7 + ], + [ + "aBBBA", + "CcbaAB", + 9 + ], + [ + "aBBBABAB", + "BBA", + 10 + ], + [ + "aBBBACB", + "Aa", + 12 + ], + [ + "aBBBAaA", + "bB", + 11 + ], + [ + "aBBBB", + "AaBb", + 6 + ], + [ + "aBBBCCBbb", + "BCBca", + 12 + ], + [ + "aBBBaBaAa", + "cCCB", + 16 + ], + [ + "aBBC", + "AbabA", + 7 + ], + [ + "aBBC", + "CcbcBBCb", + 10 + ], + [ + "aBBC", + "cCbbBa", + 9 + ], + [ + "aBBCBac", + "AbacacaCC", + 12 + ], + [ + "aBBCBbab", + "bb", + 12 + ], + [ + "aBBCaCCAB", + "ccBAcA", + 12 + ], + [ + "aBBCbAb", + "cACACb", + 10 + ], + [ + "aBBCbB", + "aCabbA", + 8 + ], + [ + "aBBCcabCb", + "CCacAacc", + 14 + ], + [ + "aBBaB", + "BCaccaa", + 10 + ], + [ + "aBBaB", + "bACCbA", + 11 + ], + [ + "aBBaCCab", + "CBcAbcC", + 12 + ], + [ + "aBBaa", + "a", + 8 + ], + [ + "aBBabAb", + "bACbcAa", + 11 + ], + [ + "aBBac", + "BbAa", + 6 + ], + [ + "aBBacABac", + "CbCbc", + 13 + ], + [ + "aBBb", + "BCAaBa", + 9 + ], + [ + "aBBbA", + "aaa", + 7 + ], + [ + "aBBbB", + "bbBaba", + 7 + ], + [ + "aBBbba", + "bCCcacbB", + 14 + ], + [ + "aBBc", + "BccbaCCac", + 14 + ], + [ + "aBBcABB", + "BBcAbCBAB", + 8 + ], + [ + "aBBcBAab", + "CcCBBA", + 12 + ], + [ + "aBBcBBA", + "cC", + 12 + ], + [ + "aBBcBcb", + "BBBca", + 6 + ], + [ + "aBBcaB", + "AbBAa", + 6 + ], + [ + "aBBccB", + "abcac", + 7 + ], + [ + "aBBcca", + "bABb", + 10 + ], + [ + "aBBcccbCB", + "bCcab", + 12 + ], + [ + "aBC", + "AABCBaAAC", + 13 + ], + [ + "aBC", + "CcaCAB", + 10 + ], + [ + "aBC", + "aACCc", + 6 + ], + [ + "aBC", + "aaaCcCcB", + 12 + ], + [ + "aBC", + "bABBAa", + 9 + ], + [ + "aBC", + "bC", + 3 + ], + [ + "aBC", + "baCCa", + 6 + ], + [ + "aBC", + "bc", + 4 + ], + [ + "aBCA", + "AbbcbAb", + 9 + ], + [ + "aBCA", + "CBaAa", + 6 + ], + [ + "aBCAa", + "BbCCAbAAb", + 12 + ], + [ + "aBCAcBAa", + "BC", + 12 + ], + [ + "aBCAcBCAC", + "AABaCbBcA", + 12 + ], + [ + "aBCAccBCB", + "b", + 17 + ], + [ + "aBCBA", + "b", + 9 + ], + [ + "aBCBBCa", + "aCCAaa", + 8 + ], + [ + "aBCBC", + "bCccB", + 8 + ], + [ + "aBCBC", + "bbB", + 7 + ], + [ + "aBCBabb", + "a", + 12 + ], + [ + "aBCCcc", + "caAAc", + 10 + ], + [ + "aBCaB", + "cB", + 7 + ], + [ + "aBCaCa", + "aCA", + 7 + ], + [ + "aBCaCbC", + "BcaaBaABA", + 13 + ], + [ + "aBCab", + "cAacAcCaA", + 12 + ], + [ + "aBCababA", + "ccbABc", + 11 + ], + [ + "aBCac", + "CbCBbaAB", + 11 + ], + [ + "aBCb", + "acabaC", + 8 + ], + [ + "aBCbb", + "C", + 8 + ], + [ + "aBCbbB", + "bCcBb", + 7 + ], + [ + "aBCbcCBc", + "cBaCAc", + 10 + ], + [ + "aBCc", + "bABcbCa", + 9 + ], + [ + "aBCcAc", + "cCAaaAc", + 10 + ], + [ + "aBCcAcAC", + "BCcBaca", + 8 + ], + [ + "aBCcCBaa", + "b", + 15 + ], + [ + "aBCcCBbcC", + "ba", + 16 + ], + [ + "aBCcaBAaC", + "bBbcaCC", + 10 + ], + [ + "aBa", + "cBCbb", + 8 + ], + [ + "aBa", + "caB", + 4 + ], + [ + "aBaA", + "caBAC", + 5 + ], + [ + "aBaAaAcC", + "CAbBbbabc", + 13 + ], + [ + "aBaAcCBBA", + "AcAcc", + 12 + ], + [ + "aBaAcCC", + "Bc", + 10 + ], + [ + "aBaB", + "CBBbc", + 7 + ], + [ + "aBaB", + "bBAaA", + 6 + ], + [ + "aBaBC", + "CBbcaaBA", + 10 + ], + [ + "aBaBbA", + "CCabBBAaC", + 11 + ], + [ + "aBaBbCcc", + "aB", + 12 + ], + [ + "aBaBc", + "CcCAb", + 10 + ], + [ + "aBaC", + "A", + 7 + ], + [ + "aBaC", + "aa", + 4 + ], + [ + "aBaC", + "accAbAC", + 8 + ], + [ + "aBaCCAb", + "cbBC", + 11 + ], + [ + "aBaCaA", + "Aa", + 9 + ], + [ + "aBaCaC", + "CbaCccAc", + 9 + ], + [ + "aBaCaabAc", + "bCcACb", + 13 + ], + [ + "aBaCba", + "baB", + 8 + ], + [ + "aBaa", + "CBaBAABbB", + 12 + ], + [ + "aBaa", + "Cb", + 7 + ], + [ + "aBaaA", + "aAAaABCB", + 9 + ], + [ + "aBaabC", + "bCBbAabAC", + 9 + ], + [ + "aBaabCba", + "bCCaB", + 12 + ], + [ + "aBaabaB", + "AA", + 12 + ], + [ + "aBaacBbca", + "cCc", + 14 + ], + [ + "aBabACAA", + "acABb", + 12 + ], + [ + "aBabBcb", + "bAbA", + 10 + ], + [ + "aBabbaBbc", + "cbaccAb", + 12 + ], + [ + "aBac", + "c", + 6 + ], + [ + "aBacAAcA", + "aaCB", + 11 + ], + [ + "aBacBB", + "cb", + 9 + ], + [ + "aBacC", + "ACCCcAb", + 11 + ], + [ + "aBacCB", + "abccAac", + 9 + ], + [ + "aBaccCAa", + "aBcacaaCb", + 10 + ], + [ + "aBb", + "BCBAabbac", + 13 + ], + [ + "aBb", + "Ccb", + 4 + ], + [ + "aBb", + "aaac", + 6 + ], + [ + "aBb", + "bbcbbbAaC", + 15 + ], + [ + "aBb", + "cbCaBb", + 6 + ], + [ + "aBb", + "cbaB", + 6 + ], + [ + "aBbA", + "bbaaAbab", + 11 + ], + [ + "aBbACC", + "cCcccBCC", + 12 + ], + [ + "aBbAcCBCA", + "cCBab", + 12 + ], + [ + "aBbB", + "BacA", + 8 + ], + [ + "aBbB", + "aCcCb", + 7 + ], + [ + "aBbBA", + "aAcC", + 8 + ], + [ + "aBbBB", + "aBCc", + 6 + ], + [ + "aBbBCbBAa", + "Ac", + 16 + ], + [ + "aBbBa", + "bAa", + 6 + ], + [ + "aBbBbAbC", + "Abc", + 11 + ], + [ + "aBbBbaBA", + "AbbBCccC", + 10 + ], + [ + "aBbBbcBcb", + "abaBAAAAB", + 12 + ], + [ + "aBbC", + "bCbBAcBc", + 12 + ], + [ + "aBbCABBA", + "Caa", + 12 + ], + [ + "aBbCACaCb", + "A", + 16 + ], + [ + "aBbCBCaBC", + "aCcc", + 12 + ], + [ + "aBbCaa", + "cbAC", + 9 + ], + [ + "aBbCbbbBA", + "aA", + 14 + ], + [ + "aBbCbcCAb", + "BcAC", + 12 + ], + [ + "aBbCc", + "AcACCCba", + 12 + ], + [ + "aBba", + "BcAcCBB", + 12 + ], + [ + "aBbaAACBA", + "BacacbaC", + 12 + ], + [ + "aBbaAB", + "bbCc", + 9 + ], + [ + "aBbaBcC", + "c", + 12 + ], + [ + "aBbaCcbC", + "bbbbcab", + 10 + ], + [ + "aBbaaCcaA", + "bBABBAbCc", + 13 + ], + [ + "aBbabCc", + "CaCB", + 10 + ], + [ + "aBbb", + "Bb", + 4 + ], + [ + "aBbb", + "cccAbB", + 9 + ], + [ + "aBbbA", + "AAC", + 9 + ], + [ + "aBbbAB", + "C", + 12 + ], + [ + "aBbbCab", + "AaCBacaba", + 10 + ], + [ + "aBbbbBA", + "BcbbBBA", + 5 + ], + [ + "aBbcCBaCc", + "C", + 16 + ], + [ + "aBbcCba", + "BbAcaBac", + 9 + ], + [ + "aBc", + "A", + 5 + ], + [ + "aBc", + "Bac", + 4 + ], + [ + "aBc", + "bCCc", + 6 + ], + [ + "aBc", + "baC", + 5 + ], + [ + "aBcAA", + "ABc", + 5 + ], + [ + "aBcAABabC", + "cab", + 12 + ], + [ + "aBcAaAccc", + "BacacaA", + 12 + ], + [ + "aBcAabAA", + "aBcB", + 9 + ], + [ + "aBcAc", + "bCCbbCc", + 11 + ], + [ + "aBcB", + "c", + 6 + ], + [ + "aBcBBBA", + "BbBBaAAab", + 12 + ], + [ + "aBcBBCAbb", + "AAA", + 15 + ], + [ + "aBcBaa", + "bbB", + 9 + ], + [ + "aBcBbbCb", + "cAcBCca", + 11 + ], + [ + "aBcC", + "CCAAA", + 10 + ], + [ + "aBcCABCc", + "BCbbcC", + 9 + ], + [ + "aBcCBbAc", + "cAbABaAa", + 12 + ], + [ + "aBcCCbA", + "cbA", + 8 + ], + [ + "aBcCcbaAC", + "aBBbBaB", + 11 + ], + [ + "aBca", + "BacbCCcB", + 11 + ], + [ + "aBca", + "CBAcCcCA", + 11 + ], + [ + "aBcaCC", + "c", + 10 + ], + [ + "aBcaaBB", + "BAbBaACbc", + 12 + ], + [ + "aBcbABAAa", + "bCCc", + 16 + ], + [ + "aBcbCAb", + "abA", + 8 + ], + [ + "aBcba", + "cAbCC", + 9 + ], + [ + "aBccABa", + "baBbCAAB", + 9 + ], + [ + "aBccAbac", + "CAaaCbb", + 14 + ], + [ + "aBccacCBa", + "ccbc", + 12 + ], + [ + "aBcccC", + "CccbACAcc", + 12 + ], + [ + "aC", + "AABacca", + 11 + ], + [ + "aC", + "ABABACC", + 11 + ], + [ + "aC", + "AcaBa", + 8 + ], + [ + "aC", + "AcaCBcaCB", + 14 + ], + [ + "aC", + "BBbbBcA", + 13 + ], + [ + "aC", + "BCABB", + 8 + ], + [ + "aC", + "Ba", + 4 + ], + [ + "aC", + "BaACBc", + 8 + ], + [ + "aC", + "BacccCb", + 10 + ], + [ + "aC", + "BcACaaCa", + 12 + ], + [ + "aC", + "BcB", + 5 + ], + [ + "aC", + "CAAcA", + 8 + ], + [ + "aC", + "CBbB", + 8 + ], + [ + "aC", + "CCA", + 4 + ], + [ + "aC", + "CaAA", + 6 + ], + [ + "aC", + "a", + 2 + ], + [ + "aC", + "aACaBBaB", + 12 + ], + [ + "aC", + "abCBcb", + 8 + ], + [ + "aC", + "abcabC", + 8 + ], + [ + "aC", + "bA", + 4 + ], + [ + "aC", + "bABAabc", + 11 + ], + [ + "aC", + "bBAAbCAcB", + 15 + ], + [ + "aC", + "bBaaaA", + 10 + ], + [ + "aC", + "bCBA", + 6 + ], + [ + "aC", + "baaaCC", + 8 + ], + [ + "aC", + "babcC", + 6 + ], + [ + "aC", + "bbc", + 5 + ], + [ + "aC", + "caCbAcaCc", + 14 + ], + [ + "aC", + "cbCaCaB", + 10 + ], + [ + "aCA", + "BCcBBaBc", + 13 + ], + [ + "aCA", + "CAACacC", + 10 + ], + [ + "aCA", + "bAaaC", + 8 + ], + [ + "aCA", + "babAaaBaC", + 14 + ], + [ + "aCA", + "baccbBBC", + 13 + ], + [ + "aCA", + "cBCc", + 6 + ], + [ + "aCAAA", + "aB", + 8 + ], + [ + "aCAAAaAba", + "BAbB", + 14 + ], + [ + "aCAABBBBb", + "BBABB", + 11 + ], + [ + "aCAACBcA", + "aA", + 12 + ], + [ + "aCAAaaa", + "AcbacaACb", + 12 + ], + [ + "aCAAbCBbA", + "cBc", + 15 + ], + [ + "aCAAcbC", + "CAaAcCcCc", + 10 + ], + [ + "aCAB", + "AbabAb", + 7 + ], + [ + "aCAB", + "BA", + 6 + ], + [ + "aCAB", + "CBAC", + 6 + ], + [ + "aCABAcBac", + "CCBCcbCC", + 10 + ], + [ + "aCABBC", + "ABb", + 7 + ], + [ + "aCABaAaC", + "BCaaABcC", + 9 + ], + [ + "aCABaBca", + "c", + 14 + ], + [ + "aCABaa", + "C", + 10 + ], + [ + "aCACACAC", + "baAbACC", + 8 + ], + [ + "aCACBBC", + "aCACBaAac", + 7 + ], + [ + "aCACBaaA", + "acCcCB", + 10 + ], + [ + "aCACCca", + "aBA", + 10 + ], + [ + "aCACa", + "baBbABB", + 10 + ], + [ + "aCACcbaa", + "CbAbBCBb", + 14 + ], + [ + "aCAa", + "bBCCcc", + 10 + ], + [ + "aCAaCba", + "AabAc", + 9 + ], + [ + "aCAab", + "CCCcCCAA", + 13 + ], + [ + "aCAacCAAa", + "abAcBacA", + 10 + ], + [ + "aCAb", + "Aaac", + 6 + ], + [ + "aCAbCC", + "bca", + 9 + ], + [ + "aCAbCC", + "cCABABac", + 10 + ], + [ + "aCAbCaCca", + "cACA", + 12 + ], + [ + "aCAbaBab", + "BABbA", + 11 + ], + [ + "aCAbca", + "B", + 11 + ], + [ + "aCAcBbbC", + "acaAbACAB", + 12 + ], + [ + "aCAcaCB", + "CbC", + 10 + ], + [ + "aCAcacc", + "aA", + 10 + ], + [ + "aCAcbA", + "aa", + 9 + ], + [ + "aCAcbabB", + "Aabbca", + 12 + ], + [ + "aCAccCa", + "AaBca", + 8 + ], + [ + "aCB", + "A", + 5 + ], + [ + "aCB", + "aAaCbBcA", + 10 + ], + [ + "aCB", + "aBABAaccA", + 14 + ], + [ + "aCB", + "aBcAb", + 6 + ], + [ + "aCB", + "abCbBA", + 6 + ], + [ + "aCB", + "b", + 5 + ], + [ + "aCB", + "cCcaBbc", + 10 + ], + [ + "aCBA", + "BCcCccBA", + 10 + ], + [ + "aCBA", + "cCCbcA", + 7 + ], + [ + "aCBAabBa", + "aA", + 12 + ], + [ + "aCBAb", + "B", + 8 + ], + [ + "aCBAbb", + "c", + 11 + ], + [ + "aCBB", + "bAb", + 7 + ], + [ + "aCBBAAB", + "bcbCAAAbc", + 11 + ], + [ + "aCBBBAab", + "CBcBbBcb", + 9 + ], + [ + "aCBBCcC", + "aBaCAa", + 8 + ], + [ + "aCBBabca", + "bCaaC", + 11 + ], + [ + "aCBBbb", + "cb", + 9 + ], + [ + "aCBBcBBC", + "c", + 14 + ], + [ + "aCBCCa", + "AaaABcA", + 10 + ], + [ + "aCBCc", + "bacBbaaaA", + 13 + ], + [ + "aCBa", + "cAA", + 6 + ], + [ + "aCBaABA", + "ba", + 11 + ], + [ + "aCBaAC", + "a", + 10 + ], + [ + "aCBaacBc", + "cacacCBB", + 10 + ], + [ + "aCBb", + "Ba", + 6 + ], + [ + "aCBb", + "CaAc", + 8 + ], + [ + "aCBb", + "CaBb", + 4 + ], + [ + "aCBbABbA", + "CbBcaA", + 10 + ], + [ + "aCBbACC", + "cAaBBC", + 10 + ], + [ + "aCBbBAcBB", + "aabBbCcaB", + 9 + ], + [ + "aCBbBc", + "aBabCbbaC", + 10 + ], + [ + "aCBbcccA", + "cCA", + 11 + ], + [ + "aCBc", + "ABAAaC", + 10 + ], + [ + "aCBc", + "BCcc", + 4 + ], + [ + "aCBcB", + "bBCCAC", + 10 + ], + [ + "aCBcaC", + "A", + 11 + ], + [ + "aCC", + "AB", + 5 + ], + [ + "aCC", + "Ac", + 4 + ], + [ + "aCC", + "abABbCBc", + 11 + ], + [ + "aCC", + "cBAcBCcb", + 12 + ], + [ + "aCC", + "cCCcBaa", + 10 + ], + [ + "aCCAA", + "C", + 8 + ], + [ + "aCCAB", + "bbCB", + 6 + ], + [ + "aCCABabA", + "aCa", + 10 + ], + [ + "aCCAcCbA", + "cBbCc", + 13 + ], + [ + "aCCAccAA", + "CB", + 14 + ], + [ + "aCCBBB", + "cccacc", + 10 + ], + [ + "aCCBCcA", + "aAc", + 10 + ], + [ + "aCCBCcCC", + "acBCbA", + 9 + ], + [ + "aCCC", + "ACbCcBa", + 8 + ], + [ + "aCCCBAC", + "AB", + 11 + ], + [ + "aCCCBCBa", + "a", + 14 + ], + [ + "aCCCBaAAa", + "CcBb", + 13 + ], + [ + "aCCCCB", + "cCABAcbA", + 12 + ], + [ + "aCCCCbCc", + "B", + 15 + ], + [ + "aCCCCcCb", + "AAB", + 14 + ], + [ + "aCCCab", + "BAaBcaC", + 10 + ], + [ + "aCCCc", + "aBabBBaa", + 14 + ], + [ + "aCCaA", + "aAAbb", + 8 + ], + [ + "aCCaA", + "cAA", + 6 + ], + [ + "aCCaBCcb", + "BAcc", + 12 + ], + [ + "aCCaC", + "CcacABac", + 10 + ], + [ + "aCCbBBbCB", + "baAcABCC", + 13 + ], + [ + "aCCbbAC", + "BAcbcB", + 11 + ], + [ + "aCCbbbaaB", + "abbC", + 12 + ], + [ + "aCCc", + "bAa", + 8 + ], + [ + "aCCc", + "cbaB", + 8 + ], + [ + "aCCcABBCB", + "bbC", + 14 + ], + [ + "aCCcBBABA", + "bABaacb", + 15 + ], + [ + "aCCcBCCBc", + "a", + 16 + ], + [ + "aCCcbaa", + "B", + 13 + ], + [ + "aCCccBBa", + "baAB", + 14 + ], + [ + "aCa", + "B", + 6 + ], + [ + "aCa", + "BCcC", + 6 + ], + [ + "aCa", + "bbbbCBA", + 11 + ], + [ + "aCaAAACa", + "AcCAA", + 10 + ], + [ + "aCaAAb", + "BCBccaB", + 10 + ], + [ + "aCaABBc", + "aCAbcbB", + 8 + ], + [ + "aCaABbAb", + "aBc", + 12 + ], + [ + "aCaAb", + "aCc", + 6 + ], + [ + "aCaAcCBb", + "baCcbb", + 9 + ], + [ + "aCaAcaC", + "BBCcCcbc", + 11 + ], + [ + "aCaB", + "BacaaAcBA", + 11 + ], + [ + "aCaB", + "C", + 6 + ], + [ + "aCaBAC", + "ab", + 9 + ], + [ + "aCaBBa", + "B", + 10 + ], + [ + "aCaBCB", + "a", + 10 + ], + [ + "aCaBCCB", + "cAccc", + 10 + ], + [ + "aCaBa", + "aC", + 6 + ], + [ + "aCaBacB", + "AAbcaB", + 9 + ], + [ + "aCaC", + "BBcc", + 7 + ], + [ + "aCaC", + "CAAaCAB", + 9 + ], + [ + "aCaC", + "accBcAAbA", + 14 + ], + [ + "aCaCAAac", + "CcCAB", + 10 + ], + [ + "aCaCBaaBb", + "CC", + 14 + ], + [ + "aCaCacAba", + "cbaAbBB", + 13 + ], + [ + "aCaCccca", + "CbACAc", + 11 + ], + [ + "aCaa", + "BAbB", + 8 + ], + [ + "aCaaC", + "ABacC", + 5 + ], + [ + "aCaac", + "AcabAC", + 6 + ], + [ + "aCaacB", + "CA", + 9 + ], + [ + "aCaacacbC", + "b", + 16 + ], + [ + "aCab", + "AcccBcB", + 11 + ], + [ + "aCab", + "BaaacBbbA", + 12 + ], + [ + "aCabBacCB", + "AAc", + 14 + ], + [ + "aCabCBBB", + "CBBcBbBBB", + 10 + ], + [ + "aCabCbbb", + "CBCCCcA", + 12 + ], + [ + "aCabaA", + "bABbaB", + 8 + ], + [ + "aCabbbCA", + "Ba", + 14 + ], + [ + "aCabcb", + "bBBb", + 9 + ], + [ + "aCacA", + "BbAB", + 9 + ], + [ + "aCacCa", + "baABcBcAb", + 12 + ], + [ + "aCacCcAaC", + "CbAbBa", + 14 + ], + [ + "aCacbC", + "CcbABcb", + 10 + ], + [ + "aCb", + "AaBaAc", + 10 + ], + [ + "aCb", + "BB", + 5 + ], + [ + "aCb", + "a", + 4 + ], + [ + "aCb", + "cCbBC", + 6 + ], + [ + "aCbA", + "CcCccBc", + 11 + ], + [ + "aCbA", + "bBaC", + 8 + ], + [ + "aCbAa", + "AABbbcCAC", + 13 + ], + [ + "aCbBAA", + "BcCC", + 11 + ], + [ + "aCbBAB", + "bcCBb", + 8 + ], + [ + "aCbBB", + "B", + 8 + ], + [ + "aCbBCaB", + "aCcAcaAbB", + 9 + ], + [ + "aCbBb", + "CBbA", + 6 + ], + [ + "aCbCBB", + "CAaACaA", + 11 + ], + [ + "aCbCBc", + "ABaBAcAcC", + 13 + ], + [ + "aCbCcCBCB", + "CBbc", + 13 + ], + [ + "aCba", + "aCba", + 0 + ], + [ + "aCbaa", + "BacbBccb", + 11 + ], + [ + "aCbb", + "bBcCBAAcc", + 15 + ], + [ + "aCbbCCCB", + "AaAC", + 13 + ], + [ + "aCbbaBAa", + "BbB", + 11 + ], + [ + "aCbbc", + "BBCAb", + 8 + ], + [ + "aCbbcAcB", + "accCaaB", + 9 + ], + [ + "aCbca", + "AbAc", + 7 + ], + [ + "aCbcb", + "cBcacaa", + 11 + ], + [ + "aCbcc", + "cBCbBAbB", + 12 + ], + [ + "aCbcca", + "CBbCaaCC", + 11 + ], + [ + "aCbccb", + "cAAab", + 9 + ], + [ + "aCc", + "AaAbC", + 7 + ], + [ + "aCc", + "BCbb", + 6 + ], + [ + "aCc", + "CCABBCAa", + 13 + ], + [ + "aCc", + "aAB", + 4 + ], + [ + "aCc", + "bbAcb", + 8 + ], + [ + "aCc", + "bbCcccb", + 10 + ], + [ + "aCc", + "caCAC", + 5 + ], + [ + "aCcA", + "a", + 6 + ], + [ + "aCcA", + "aC", + 4 + ], + [ + "aCcA", + "aaAAaaCBa", + 13 + ], + [ + "aCcA", + "cABb", + 8 + ], + [ + "aCcAAA", + "AbaBACBC", + 13 + ], + [ + "aCcABAc", + "BC", + 11 + ], + [ + "aCcABCB", + "b", + 13 + ], + [ + "aCcACA", + "baCcaaAa", + 7 + ], + [ + "aCcAaAba", + "aCAca", + 8 + ], + [ + "aCcAbB", + "cBCAB", + 7 + ], + [ + "aCcB", + "CCbcBaBBb", + 12 + ], + [ + "aCcBbCAC", + "CACbBb", + 11 + ], + [ + "aCcCC", + "A", + 9 + ], + [ + "aCcCCa", + "CcCabCc", + 8 + ], + [ + "aCcCabC", + "BcACaABCC", + 10 + ], + [ + "aCcCcAaA", + "baaAacBAb", + 13 + ], + [ + "aCcCca", + "cBCbCCAcb", + 11 + ], + [ + "aCcaAABb", + "BCACAAb", + 8 + ], + [ + "aCcaCAA", + "AabCbBBAC", + 12 + ], + [ + "aCcacbabC", + "a", + 16 + ], + [ + "aCcbAb", + "CbbcbaCAB", + 11 + ], + [ + "aCcbB", + "aA", + 8 + ], + [ + "aCcbBbAcC", + "bBBc", + 11 + ], + [ + "aCcbaCbca", + "bcBCCcbC", + 12 + ], + [ + "aCcbb", + "ccAabbABa", + 13 + ], + [ + "aCcbbaccb", + "AaCaCaCC", + 12 + ], + [ + "aCcbcBBA", + "CaBccABCa", + 11 + ], + [ + "aCcbcb", + "AcaacBCc", + 10 + ], + [ + "aCcc", + "baCACCa", + 8 + ], + [ + "aCccAaBa", + "BAaBcB", + 12 + ], + [ + "aCccAb", + "bBbaAcCC", + 13 + ], + [ + "aCccBB", + "CCa", + 9 + ], + [ + "aCccaaABC", + "CcBac", + 11 + ], + [ + "aCcccAaba", + "ccBbaA", + 11 + ], + [ + "aa", + "AACcaACB", + 13 + ], + [ + "aa", + "AcBCa", + 7 + ], + [ + "aa", + "Bbaba", + 6 + ], + [ + "aa", + "Bbc", + 6 + ], + [ + "aa", + "BcaBA", + 7 + ], + [ + "aa", + "C", + 4 + ], + [ + "aa", + "CBcC", + 8 + ], + [ + "aa", + "CbB", + 6 + ], + [ + "aa", + "CbBC", + 8 + ], + [ + "aa", + "a", + 2 + ], + [ + "aa", + "aABACBBb", + 13 + ], + [ + "aa", + "aBC", + 4 + ], + [ + "aa", + "aBcaaCAC", + 12 + ], + [ + "aa", + "aCbbAaaCa", + 14 + ], + [ + "aa", + "aaAaA", + 6 + ], + [ + "aa", + "aaAbcb", + 8 + ], + [ + "aa", + "aabCb", + 6 + ], + [ + "aa", + "abCBc", + 8 + ], + [ + "aa", + "abCbAb", + 9 + ], + [ + "aa", + "acAAbAAB", + 13 + ], + [ + "aa", + "bAaB", + 5 + ], + [ + "aa", + "bB", + 4 + ], + [ + "aa", + "bBAaBCA", + 11 + ], + [ + "aa", + "bCaaBbCA", + 12 + ], + [ + "aa", + "baCCBCBB", + 14 + ], + [ + "aa", + "bbAabBcCc", + 15 + ], + [ + "aa", + "bbcAC", + 9 + ], + [ + "aa", + "cAaabc", + 8 + ], + [ + "aa", + "cB", + 4 + ], + [ + "aa", + "cBB", + 6 + ], + [ + "aa", + "cBcBAAbA", + 14 + ], + [ + "aa", + "cCAB", + 7 + ], + [ + "aa", + "cCB", + 6 + ], + [ + "aa", + "caccAcB", + 11 + ], + [ + "aa", + "cb", + 4 + ], + [ + "aa", + "cbbcCB", + 12 + ], + [ + "aaA", + "A", + 4 + ], + [ + "aaA", + "ACBACBC", + 11 + ], + [ + "aaA", + "bCCA", + 6 + ], + [ + "aaA", + "bcaaAB", + 6 + ], + [ + "aaAA", + "aab", + 4 + ], + [ + "aaAAaaAaB", + "BAA", + 14 + ], + [ + "aaAAbbAC", + "cCbBbCAab", + 14 + ], + [ + "aaAAcC", + "cabbCa", + 9 + ], + [ + "aaAB", + "cAa", + 6 + ], + [ + "aaAB", + "cCaBCcB", + 10 + ], + [ + "aaABAbAC", + "CCAB", + 12 + ], + [ + "aaABAbb", + "CC", + 14 + ], + [ + "aaABB", + "Ccb", + 9 + ], + [ + "aaABCC", + "acABACB", + 6 + ], + [ + "aaABCb", + "CABbAaa", + 12 + ], + [ + "aaABaaA", + "CaaaBBabc", + 9 + ], + [ + "aaABbbCbc", + "B", + 16 + ], + [ + "aaAC", + "Ac", + 5 + ], + [ + "aaAC", + "bccBBBb", + 14 + ], + [ + "aaACa", + "cbAAaCB", + 9 + ], + [ + "aaAaAcbBC", + "A", + 16 + ], + [ + "aaAaaaAb", + "bcBc", + 16 + ], + [ + "aaAabBCbc", + "B", + 16 + ], + [ + "aaAabCaAa", + "BbbbcA", + 13 + ], + [ + "aaAabbbaC", + "bbbCaAbC", + 14 + ], + [ + "aaAac", + "cBCCc", + 8 + ], + [ + "aaAbaBbBa", + "baaCCa", + 12 + ], + [ + "aaAbcbBa", + "BbBbaaC", + 12 + ], + [ + "aaAc", + "bbCBca", + 10 + ], + [ + "aaAccCbC", + "BBa", + 15 + ], + [ + "aaB", + "bAAa", + 6 + ], + [ + "aaB", + "cccCAcAaC", + 15 + ], + [ + "aaBA", + "ABccb", + 9 + ], + [ + "aaBA", + "aca", + 5 + ], + [ + "aaBABCc", + "Aaa", + 10 + ], + [ + "aaBABaCc", + "acBaac", + 7 + ], + [ + "aaBABbCAc", + "cCb", + 16 + ], + [ + "aaBB", + "BbbBbcAB", + 12 + ], + [ + "aaBB", + "bAaAaAB", + 8 + ], + [ + "aaBB", + "bBabBA", + 7 + ], + [ + "aaBBa", + "caaB", + 6 + ], + [ + "aaBC", + "BacaAB", + 8 + ], + [ + "aaBCAaaaB", + "aaACA", + 10 + ], + [ + "aaBCC", + "BAAaBb", + 9 + ], + [ + "aaBCCa", + "CA", + 9 + ], + [ + "aaBCCbAac", + "AcaaBacc", + 13 + ], + [ + "aaBa", + "BabbcC", + 9 + ], + [ + "aaBaAaCa", + "aBbcAcAb", + 11 + ], + [ + "aaBaC", + "bcCAbac", + 9 + ], + [ + "aaBaCc", + "BCAaCCb", + 9 + ], + [ + "aaBaa", + "AAb", + 7 + ], + [ + "aaBaa", + "ccba", + 7 + ], + [ + "aaBaaAcBA", + "aCcBCAc", + 12 + ], + [ + "aaBabACBC", + "CCCc", + 15 + ], + [ + "aaBabbBA", + "aAbB", + 9 + ], + [ + "aaBacaB", + "BCABCCCbc", + 13 + ], + [ + "aaBbAaBA", + "BAc", + 12 + ], + [ + "aaBbb", + "aA", + 7 + ], + [ + "aaBbcA", + "CBcb", + 8 + ], + [ + "aaBbcbb", + "C", + 13 + ], + [ + "aaBcBaBa", + "aaBaaaB", + 6 + ], + [ + "aaBccACb", + "bCCBccC", + 10 + ], + [ + "aaC", + "ABcAca", + 9 + ], + [ + "aaC", + "BAbaCaa", + 9 + ], + [ + "aaC", + "CaCbcCc", + 10 + ], + [ + "aaC", + "Cc", + 5 + ], + [ + "aaC", + "aCa", + 4 + ], + [ + "aaC", + "caBBCb", + 8 + ], + [ + "aaCA", + "B", + 8 + ], + [ + "aaCAA", + "BcbBC", + 10 + ], + [ + "aaCABbb", + "CCcbBAb", + 9 + ], + [ + "aaCACa", + "Bb", + 12 + ], + [ + "aaCAcbc", + "AaaAAa", + 9 + ], + [ + "aaCAccaAB", + "CcCABcA", + 10 + ], + [ + "aaCC", + "B", + 8 + ], + [ + "aaCCC", + "ABAcC", + 6 + ], + [ + "aaCCCc", + "cCAaaAbAb", + 14 + ], + [ + "aaCCa", + "CAA", + 7 + ], + [ + "aaCCbaCaB", + "cCCcCAccC", + 13 + ], + [ + "aaCCcbCA", + "acAcb", + 9 + ], + [ + "aaCa", + "aCBca", + 5 + ], + [ + "aaCa", + "cAAca", + 5 + ], + [ + "aaCaABa", + "ba", + 11 + ], + [ + "aaCaABbc", + "B", + 14 + ], + [ + "aaCaBaA", + "aBcAa", + 8 + ], + [ + "aaCaa", + "Ac", + 8 + ], + [ + "aaCacA", + "CbabcA", + 8 + ], + [ + "aaCacAAb", + "CcCAc", + 10 + ], + [ + "aaCacaBC", + "BBaCcA", + 11 + ], + [ + "aaCacaba", + "ACbc", + 11 + ], + [ + "aaCbAAB", + "ABc", + 12 + ], + [ + "aaCbabb", + "Bc", + 13 + ], + [ + "aaCbabc", + "C", + 12 + ], + [ + "aaCbbAAc", + "BaBa", + 12 + ], + [ + "aaCcAcc", + "aC", + 10 + ], + [ + "aaCcaBcB", + "ccAaAbaBC", + 12 + ], + [ + "aaCcaC", + "AAABCccC", + 8 + ], + [ + "aaCccbAaa", + "B", + 17 + ], + [ + "aaa", + "ABAbCbC", + 12 + ], + [ + "aaa", + "abABCcbA", + 12 + ], + [ + "aaa", + "abaC", + 4 + ], + [ + "aaa", + "baa", + 2 + ], + [ + "aaa", + "cCbC", + 8 + ], + [ + "aaaA", + "BaCacbbc", + 12 + ], + [ + "aaaA", + "aaaabAC", + 6 + ], + [ + "aaaAAb", + "abCC", + 10 + ], + [ + "aaaAAcb", + "cbABcb", + 8 + ], + [ + "aaaABb", + "BBCCbBaba", + 14 + ], + [ + "aaaACcA", + "aaa", + 8 + ], + [ + "aaaAac", + "CCAaC", + 7 + ], + [ + "aaaAb", + "ACc", + 9 + ], + [ + "aaaB", + "b", + 7 + ], + [ + "aaaBBB", + "CcACCaCB", + 13 + ], + [ + "aaaBCAbBc", + "baCaab", + 12 + ], + [ + "aaaBaBCAb", + "BaBccAb", + 9 + ], + [ + "aaaBbAC", + "baBCAc", + 7 + ], + [ + "aaaBbBA", + "a", + 12 + ], + [ + "aaaC", + "aca", + 4 + ], + [ + "aaaCAAcAA", + "cCacb", + 13 + ], + [ + "aaaCBacCc", + "aA", + 15 + ], + [ + "aaaCaBcCC", + "ACAbBb", + 13 + ], + [ + "aaaCaaC", + "C", + 12 + ], + [ + "aaaCaaCb", + "bC", + 14 + ], + [ + "aaaCb", + "bcBbaB", + 11 + ], + [ + "aaaCbC", + "AabAc", + 8 + ], + [ + "aaaCcB", + "A", + 11 + ], + [ + "aaaa", + "CacBC", + 8 + ], + [ + "aaaaB", + "aCBcbC", + 9 + ], + [ + "aaaaBBCc", + "aCcCcaab", + 14 + ], + [ + "aaaaBCc", + "bcbaB", + 10 + ], + [ + "aaaaC", + "CBb", + 10 + ], + [ + "aaabCBaAB", + "cbBccA", + 14 + ], + [ + "aaac", + "BAaB", + 5 + ], + [ + "aaacCCB", + "aaAaacA", + 8 + ], + [ + "aab", + "CcCcaACB", + 12 + ], + [ + "aab", + "ab", + 2 + ], + [ + "aab", + "b", + 4 + ], + [ + "aab", + "bBb", + 4 + ], + [ + "aab", + "ccbCc", + 8 + ], + [ + "aabAA", + "CBCcBcac", + 14 + ], + [ + "aabACB", + "AACA", + 7 + ], + [ + "aabACB", + "Ca", + 10 + ], + [ + "aabAacba", + "bCbcBC", + 11 + ], + [ + "aabAbBc", + "AbCaCcCB", + 13 + ], + [ + "aabBAA", + "CbaACAbAA", + 10 + ], + [ + "aabBBcB", + "CbACCCc", + 13 + ], + [ + "aabCAAc", + "CBaA", + 10 + ], + [ + "aabCAcB", + "BBCA", + 9 + ], + [ + "aabaCc", + "Acab", + 9 + ], + [ + "aabb", + "a", + 6 + ], + [ + "aabbA", + "bA", + 6 + ], + [ + "aabbCAA", + "AaAAAbbC", + 11 + ], + [ + "aabbacA", + "AbC", + 10 + ], + [ + "aabbcABba", + "AC", + 16 + ], + [ + "aabc", + "A", + 7 + ], + [ + "aabc", + "Baab", + 4 + ], + [ + "aabcA", + "cabBcCc", + 8 + ], + [ + "aabcBcB", + "Ab", + 11 + ], + [ + "aabcaAA", + "abccacA", + 6 + ], + [ + "aabcbBc", + "BaCaaaA", + 12 + ], + [ + "aabcbaaac", + "BbCbccCc", + 11 + ], + [ + "aac", + "BCC", + 5 + ], + [ + "aac", + "BbCaCBbBc", + 14 + ], + [ + "aac", + "C", + 5 + ], + [ + "aac", + "a", + 4 + ], + [ + "aacA", + "CABCabaa", + 12 + ], + [ + "aacAaCCA", + "CBCbB", + 13 + ], + [ + "aacAaCCAb", + "BBCAB", + 13 + ], + [ + "aacAcb", + "baAacaCAB", + 9 + ], + [ + "aacB", + "ABcCcBAca", + 13 + ], + [ + "aacB", + "CACAABc", + 10 + ], + [ + "aacB", + "c", + 6 + ], + [ + "aacBAabc", + "caCBa", + 9 + ], + [ + "aacBc", + "aBCaCbAc", + 8 + ], + [ + "aacC", + "Ba", + 6 + ], + [ + "aacC", + "Bc", + 6 + ], + [ + "aacC", + "CAbBAbCaA", + 14 + ], + [ + "aacCCb", + "ccBAc", + 10 + ], + [ + "aacCc", + "CaB", + 8 + ], + [ + "aacaA", + "Bbaa", + 7 + ], + [ + "aacaa", + "cCB", + 8 + ], + [ + "aacaaCbBc", + "BCab", + 13 + ], + [ + "aacabaBA", + "cbbA", + 9 + ], + [ + "aacac", + "ABBCabcc", + 10 + ], + [ + "aacacAaB", + "aAbbA", + 11 + ], + [ + "aacb", + "BCcAb", + 6 + ], + [ + "aacbAAC", + "BbabBAC", + 8 + ], + [ + "aacbAcA", + "acbccC", + 6 + ], + [ + "aacbCABAC", + "AAbaCAbbA", + 11 + ], + [ + "aacbb", + "BBAbAC", + 10 + ], + [ + "aacc", + "acACBAa", + 10 + ], + [ + "aaccAaABA", + "BAbBacAC", + 14 + ], + [ + "aacccAccB", + "BBCAb", + 14 + ], + [ + "ab", + "ABca", + 6 + ], + [ + "ab", + "ACA", + 5 + ], + [ + "ab", + "AaB", + 3 + ], + [ + "ab", + "B", + 3 + ], + [ + "ab", + "BAabAa", + 8 + ], + [ + "ab", + "BAacbb", + 8 + ], + [ + "ab", + "Ba", + 4 + ], + [ + "ab", + "BbaBBa", + 9 + ], + [ + "ab", + "CACABaC", + 12 + ], + [ + "ab", + "CCbaA", + 8 + ], + [ + "ab", + "CbBACbc", + 11 + ], + [ + "ab", + "CbcbcbB", + 12 + ], + [ + "ab", + "CcB", + 5 + ], + [ + "ab", + "CcacBC", + 9 + ], + [ + "ab", + "aBBAbbaB", + 12 + ], + [ + "ab", + "aBCbBCACa", + 14 + ], + [ + "ab", + "abAbbc", + 8 + ], + [ + "ab", + "abB", + 2 + ], + [ + "ab", + "bAbCcBAaA", + 15 + ], + [ + "ab", + "baBcBa", + 9 + ], + [ + "ab", + "bb", + 2 + ], + [ + "ab", + "c", + 4 + ], + [ + "ab", + "cAAabacc", + 12 + ], + [ + "ab", + "cABcCCb", + 11 + ], + [ + "ab", + "cCbbbCa", + 12 + ], + [ + "ab", + "cbcb", + 6 + ], + [ + "ab", + "ccb", + 4 + ], + [ + "ab", + "cccaaCAA", + 14 + ], + [ + "abA", + "AaacB", + 8 + ], + [ + "abA", + "BAB", + 5 + ], + [ + "abA", + "abca", + 3 + ], + [ + "abA", + "bAAcC", + 8 + ], + [ + "abA", + "bBbaa", + 7 + ], + [ + "abA", + "bbbAcabb", + 12 + ], + [ + "abA", + "cAbCCcbb", + 13 + ], + [ + "abA", + "cBaaC", + 8 + ], + [ + "abAAB", + "BAbACC", + 7 + ], + [ + "abAAcaCa", + "c", + 14 + ], + [ + "abAB", + "AaCBBbb", + 10 + ], + [ + "abABAA", + "aBABbaabA", + 8 + ], + [ + "abABcAaC", + "aCbAcb", + 10 + ], + [ + "abABcb", + "b", + 10 + ], + [ + "abAC", + "BAC", + 3 + ], + [ + "abACAC", + "CBCaaCcA", + 12 + ], + [ + "abACC", + "BbCbBb", + 10 + ], + [ + "abACCBBa", + "abb", + 11 + ], + [ + "abACCaABb", + "aAcbb", + 10 + ], + [ + "abACb", + "Ca", + 8 + ], + [ + "abACbBAaA", + "cAAcCaCaB", + 13 + ], + [ + "abACccA", + "bBaCBC", + 9 + ], + [ + "abAaAaCCC", + "cACCbAbCC", + 13 + ], + [ + "abAaBAAC", + "ABca", + 11 + ], + [ + "abAacCCaa", + "acbCAC", + 11 + ], + [ + "abAbAcCc", + "b", + 14 + ], + [ + "abAbbBC", + "CAbbBbBb", + 8 + ], + [ + "abAc", + "bC", + 5 + ], + [ + "abAcBBbBA", + "CabccbcaA", + 11 + ], + [ + "abAccAbba", + "caCACCC", + 14 + ], + [ + "abAccBA", + "ACAbbABaa", + 12 + ], + [ + "abAccCbBa", + "cbccBbBac", + 8 + ], + [ + "abB", + "BAAA", + 7 + ], + [ + "abB", + "BACbb", + 6 + ], + [ + "abB", + "BcaBabA", + 10 + ], + [ + "abB", + "a", + 4 + ], + [ + "abB", + "cbBaACaAB", + 14 + ], + [ + "abBA", + "AACBCAbA", + 11 + ], + [ + "abBAA", + "bA", + 6 + ], + [ + "abBAAcaA", + "AACCC", + 11 + ], + [ + "abBAAcbb", + "bCCcbb", + 8 + ], + [ + "abBAB", + "aa", + 7 + ], + [ + "abBABBA", + "BBCBccbaA", + 12 + ], + [ + "abBAaBa", + "bbaBCcbc", + 11 + ], + [ + "abBAaCacb", + "bbaca", + 10 + ], + [ + "abBAaCcc", + "ccCACB", + 12 + ], + [ + "abBAc", + "bB", + 6 + ], + [ + "abBAc", + "bBB", + 6 + ], + [ + "abBB", + "BbaBbCbb", + 10 + ], + [ + "abBBCc", + "b", + 10 + ], + [ + "abBBaAcBc", + "bbCaB", + 11 + ], + [ + "abBBaBbCc", + "Cac", + 14 + ], + [ + "abBBac", + "BAc", + 7 + ], + [ + "abBBb", + "A", + 9 + ], + [ + "abBBb", + "ACAAc", + 9 + ], + [ + "abBBbB", + "b", + 10 + ], + [ + "abBCACBBc", + "B", + 16 + ], + [ + "abBCBAB", + "cCA", + 10 + ], + [ + "abBCCb", + "AbaCcCcA", + 9 + ], + [ + "abBCCcc", + "BcBCCbCbb", + 11 + ], + [ + "abBCac", + "cCcbACcC", + 11 + ], + [ + "abBCcCbA", + "bC", + 12 + ], + [ + "abBa", + "cbabba", + 5 + ], + [ + "abBaA", + "CA", + 8 + ], + [ + "abBaAAc", + "BB", + 11 + ], + [ + "abBaBcabA", + "BBBAabAba", + 10 + ], + [ + "abBaCbB", + "CaCaacbC", + 9 + ], + [ + "abBabAb", + "Aacb", + 9 + ], + [ + "abBacbc", + "cAccCaaa", + 15 + ], + [ + "abBacca", + "bAACac", + 10 + ], + [ + "abBb", + "ABCaACb", + 10 + ], + [ + "abBb", + "BAABc", + 7 + ], + [ + "abBb", + "c", + 8 + ], + [ + "abBb", + "cAB", + 6 + ], + [ + "abBbBB", + "Aaccb", + 10 + ], + [ + "abBbC", + "aaccbac", + 9 + ], + [ + "abBbCBBa", + "C", + 14 + ], + [ + "abBbb", + "BBA", + 7 + ], + [ + "abBbba", + "CBbAAb", + 9 + ], + [ + "abBc", + "AABBCcBcc", + 12 + ], + [ + "abBc", + "bcbbCABca", + 12 + ], + [ + "abBcB", + "CbCaCca", + 10 + ], + [ + "abBcBBCA", + "BCCCacAac", + 15 + ], + [ + "abBcaBa", + "bcBCCc", + 11 + ], + [ + "abC", + "ACAbb", + 7 + ], + [ + "abC", + "Cc", + 5 + ], + [ + "abC", + "aAabBb", + 8 + ], + [ + "abC", + "b", + 4 + ], + [ + "abC", + "bccBAC", + 9 + ], + [ + "abC", + "bccabCBc", + 10 + ], + [ + "abC", + "cBA", + 5 + ], + [ + "abC", + "cCB", + 6 + ], + [ + "abCA", + "Acb", + 6 + ], + [ + "abCA", + "cc", + 7 + ], + [ + "abCAA", + "BAcbCCBc", + 11 + ], + [ + "abCAAcbCB", + "acCabB", + 9 + ], + [ + "abCAaA", + "AcbaAB", + 8 + ], + [ + "abCB", + "BABBbAABA", + 13 + ], + [ + "abCBB", + "Bb", + 7 + ], + [ + "abCBCB", + "baCaaa", + 10 + ], + [ + "abCC", + "CA", + 6 + ], + [ + "abCC", + "CCcCAcaA", + 13 + ], + [ + "abCCAacb", + "babaA", + 12 + ], + [ + "abCCBB", + "baB", + 8 + ], + [ + "abCCBBC", + "aAc", + 11 + ], + [ + "abCCCBaAC", + "CCBbCbBA", + 13 + ], + [ + "abCCaAca", + "bBCBCaC", + 10 + ], + [ + "abCCbBBaA", + "Ab", + 15 + ], + [ + "abCCc", + "BcbBabBc", + 12 + ], + [ + "abCCcAcBB", + "BaAAAaAB", + 13 + ], + [ + "abCa", + "ABA", + 5 + ], + [ + "abCaBC", + "bCCaCaA", + 10 + ], + [ + "abCaC", + "acc", + 6 + ], + [ + "abCaCAbB", + "aCaba", + 8 + ], + [ + "abCabA", + "ACba", + 6 + ], + [ + "abCabbccb", + "CcAcA", + 14 + ], + [ + "abCacaaC", + "aC", + 12 + ], + [ + "abCb", + "cBCBaA", + 8 + ], + [ + "abCbBCcAa", + "a", + 16 + ], + [ + "abCbaCC", + "CAabaabBB", + 14 + ], + [ + "abCbbba", + "CcbBCBAA", + 12 + ], + [ + "abCc", + "Ca", + 6 + ], + [ + "abCcAbbC", + "B", + 15 + ], + [ + "abCcCCC", + "BbAaca", + 11 + ], + [ + "abCcCc", + "ACacA", + 8 + ], + [ + "abCcaA", + "aca", + 6 + ], + [ + "abCcaABB", + "AbcBb", + 8 + ], + [ + "aba", + "CBb", + 5 + ], + [ + "aba", + "CBbbaAb", + 10 + ], + [ + "aba", + "aCccA", + 7 + ], + [ + "aba", + "acaB", + 4 + ], + [ + "aba", + "bcCCa", + 8 + ], + [ + "aba", + "cBB", + 5 + ], + [ + "abaAAA", + "cAbB", + 10 + ], + [ + "abaACCcC", + "abacCBA", + 7 + ], + [ + "abaAaccC", + "CCaacc", + 8 + ], + [ + "abaAbb", + "a", + 10 + ], + [ + "abaBCABc", + "aCcccB", + 11 + ], + [ + "abaBaCca", + "aAcAbaB", + 12 + ], + [ + "abaC", + "Bba", + 4 + ], + [ + "abaCACccC", + "BBBBB", + 17 + ], + [ + "abaCBABaB", + "abacbacc", + 9 + ], + [ + "abaCCbcA", + "AcBBc", + 11 + ], + [ + "abaCbB", + "cC", + 10 + ], + [ + "abaCbaCB", + "AAcC", + 11 + ], + [ + "abaCbaa", + "aCCaCCA", + 9 + ], + [ + "abaaACcB", + "cBBCBBAbC", + 16 + ], + [ + "abab", + "CcaCC", + 8 + ], + [ + "abab", + "aCAAaaaB", + 11 + ], + [ + "abab", + "acBbA", + 6 + ], + [ + "ababA", + "CBBC", + 8 + ], + [ + "ababACcC", + "BC", + 13 + ], + [ + "ababCBBbB", + "CbC", + 14 + ], + [ + "ababa", + "CABCcCcb", + 14 + ], + [ + "ababcCb", + "aCAbCc", + 7 + ], + [ + "abacA", + "ABb", + 8 + ], + [ + "abacABc", + "babCCAbb", + 8 + ], + [ + "abacAc", + "BacbCaaB", + 11 + ], + [ + "abacBBa", + "CbCAa", + 9 + ], + [ + "abacBbaCC", + "a", + 16 + ], + [ + "abacCb", + "b", + 10 + ], + [ + "abaca", + "cAB", + 9 + ], + [ + "abacaCbBc", + "bACcACB", + 10 + ], + [ + "abaccABb", + "cBcCc", + 12 + ], + [ + "abaccaAcA", + "B", + 17 + ], + [ + "abacccc", + "Cba", + 10 + ], + [ + "abb", + "B", + 5 + ], + [ + "abb", + "CbBBAc", + 9 + ], + [ + "abb", + "aC", + 4 + ], + [ + "abb", + "bbbcaca", + 10 + ], + [ + "abbAAABBb", + "CbCcB", + 14 + ], + [ + "abbAcB", + "BbaaBc", + 8 + ], + [ + "abbBBCcB", + "aaa", + 14 + ], + [ + "abbBbAbcc", + "c", + 16 + ], + [ + "abbBbbbcC", + "aBbBaB", + 10 + ], + [ + "abbCBA", + "aA", + 8 + ], + [ + "abbCBA", + "bb", + 8 + ], + [ + "abbCBabac", + "CBAB", + 12 + ], + [ + "abbCaBB", + "Ab", + 11 + ], + [ + "abbCaBC", + "aBcc", + 9 + ], + [ + "abbCbbBB", + "cbbaaca", + 12 + ], + [ + "abbCc", + "bC", + 6 + ], + [ + "abbaA", + "BabaAAaBb", + 11 + ], + [ + "abbaBaaBc", + "CAcBbc", + 13 + ], + [ + "abbaBbc", + "aABbAcc", + 8 + ], + [ + "abbaCcba", + "Cba", + 10 + ], + [ + "abbbA", + "ACAcc", + 9 + ], + [ + "abbbBAbAA", + "aCACcAbA", + 10 + ], + [ + "abbbCCCaC", + "acBCBABcC", + 13 + ], + [ + "abbbb", + "cCcbBcb", + 9 + ], + [ + "abbbc", + "C", + 9 + ], + [ + "abbbca", + "BBAcBbAB", + 12 + ], + [ + "abbcABB", + "ac", + 10 + ], + [ + "abbcB", + "ABbcCB", + 4 + ], + [ + "abbcBac", + "bACca", + 9 + ], + [ + "abbccAa", + "AcC", + 10 + ], + [ + "abbccb", + "cbCb", + 7 + ], + [ + "abc", + "AbC", + 2 + ], + [ + "abc", + "BAAb", + 7 + ], + [ + "abc", + "C", + 5 + ], + [ + "abc", + "aB", + 3 + ], + [ + "abcAA", + "CAAabC", + 11 + ], + [ + "abcAaaccb", + "cAcAAcbcA", + 11 + ], + [ + "abcAbB", + "AbcCacc", + 8 + ], + [ + "abcAcaB", + "acBcBb", + 7 + ], + [ + "abcAcbbb", + "CaABc", + 13 + ], + [ + "abcB", + "AaCcAbCa", + 11 + ], + [ + "abcB", + "bbab", + 5 + ], + [ + "abcBaBb", + "cBBa", + 8 + ], + [ + "abcBacc", + "ACBa", + 8 + ], + [ + "abcBb", + "BBbcccBB", + 9 + ], + [ + "abcBbBAb", + "cB", + 12 + ], + [ + "abcC", + "BCc", + 5 + ], + [ + "abcCAB", + "CbCcC", + 8 + ], + [ + "abcCBAc", + "ACCBaA", + 7 + ], + [ + "abcCaBcCB", + "AAba", + 15 + ], + [ + "abcCbBCA", + "Ba", + 13 + ], + [ + "abca", + "cbaccAA", + 9 + ], + [ + "abcaCB", + "CCaaAB", + 8 + ], + [ + "abcb", + "a", + 6 + ], + [ + "abcbbC", + "BBc", + 9 + ], + [ + "abcbbbcb", + "b", + 14 + ], + [ + "abccC", + "bbcBab", + 8 + ], + [ + "abccCBabC", + "AbABaaabc", + 10 + ], + [ + "abccCaB", + "Bbaa", + 10 + ], + [ + "abccCcAa", + "BCbb", + 13 + ], + [ + "abccaB", + "aCABaba", + 9 + ], + [ + "abccaC", + "ABbCB", + 9 + ], + [ + "abccaCcBB", + "AAB", + 14 + ], + [ + "abccbCC", + "BAc", + 11 + ], + [ + "ac", + "A", + 3 + ], + [ + "ac", + "AAaCcbBa", + 12 + ], + [ + "ac", + "AAcCbbCca", + 15 + ], + [ + "ac", + "ACAcCa", + 9 + ], + [ + "ac", + "ACCBbAcCc", + 15 + ], + [ + "ac", + "B", + 4 + ], + [ + "ac", + "BAab", + 6 + ], + [ + "ac", + "BCCBbccb", + 14 + ], + [ + "ac", + "BCc", + 4 + ], + [ + "ac", + "BaBbbAA", + 12 + ], + [ + "ac", + "BcCACbB", + 12 + ], + [ + "ac", + "CBA", + 6 + ], + [ + "ac", + "CccBcaB", + 12 + ], + [ + "ac", + "aB", + 2 + ], + [ + "ac", + "aBBCacBbC", + 14 + ], + [ + "ac", + "aC", + 1 + ], + [ + "ac", + "aCcCC", + 6 + ], + [ + "ac", + "aaCaCaca", + 12 + ], + [ + "ac", + "ab", + 2 + ], + [ + "ac", + "acacCAbBA", + 14 + ], + [ + "ac", + "bACaaCCCC", + 15 + ], + [ + "ac", + "bAbaCCcBC", + 14 + ], + [ + "ac", + "bCCaAaa", + 12 + ], + [ + "ac", + "cBAcc", + 7 + ], + [ + "acA", + "AaCCAaabc", + 13 + ], + [ + "acA", + "aABBBACc", + 12 + ], + [ + "acA", + "ac", + 2 + ], + [ + "acA", + "cBcBb", + 8 + ], + [ + "acAA", + "bc", + 6 + ], + [ + "acAAABBa", + "cbcC", + 13 + ], + [ + "acAACBA", + "BcbaCbbA", + 8 + ], + [ + "acAB", + "ccbAAAa", + 10 + ], + [ + "acABCCA", + "BCcABBAa", + 9 + ], + [ + "acABc", + "BBcABbCba", + 11 + ], + [ + "acAC", + "A", + 6 + ], + [ + "acACCB", + "AAcBCba", + 9 + ], + [ + "acAaaAcba", + "BabCcbCbb", + 15 + ], + [ + "acAaaaBc", + "BacCBc", + 10 + ], + [ + "acAabACa", + "acaCB", + 8 + ], + [ + "acAabBbA", + "cabCb", + 8 + ], + [ + "acAacCc", + "BBCC", + 11 + ], + [ + "acAb", + "cc", + 6 + ], + [ + "acAbabcBa", + "ABABcaB", + 11 + ], + [ + "acAc", + "ABa", + 6 + ], + [ + "acAc", + "CcBCaa", + 9 + ], + [ + "acAc", + "aBbCaB", + 8 + ], + [ + "acAcBCCa", + "BaB", + 13 + ], + [ + "acAcCAc", + "cAbab", + 9 + ], + [ + "acAcaC", + "B", + 12 + ], + [ + "acAcabB", + "baaacbB", + 7 + ], + [ + "acAcbcAC", + "ccaCbCcc", + 8 + ], + [ + "acB", + "ABcAcBAcb", + 13 + ], + [ + "acB", + "acb", + 1 + ], + [ + "acB", + "bCccA", + 8 + ], + [ + "acB", + "cbabb", + 7 + ], + [ + "acBA", + "BcAaBCBAA", + 11 + ], + [ + "acBAcc", + "AAabb", + 10 + ], + [ + "acBB", + "AaaAbB", + 7 + ], + [ + "acBB", + "cA", + 6 + ], + [ + "acBBAb", + "bCcAbBCAC", + 11 + ], + [ + "acBBAbacB", + "aaCBB", + 12 + ], + [ + "acBBBbAcb", + "BAa", + 14 + ], + [ + "acBBCC", + "ccAbcBbCb", + 10 + ], + [ + "acBBCc", + "bcCaAbAa", + 13 + ], + [ + "acBBbBa", + "Cba", + 9 + ], + [ + "acBBcCC", + "ABbb", + 10 + ], + [ + "acBCBcabA", + "caCbca", + 9 + ], + [ + "acBCCbab", + "ccA", + 12 + ], + [ + "acBCa", + "BabbAcb", + 10 + ], + [ + "acBa", + "AC", + 6 + ], + [ + "acBaA", + "ABBCC", + 7 + ], + [ + "acBaAacc", + "bbab", + 13 + ], + [ + "acBaBCab", + "ac", + 12 + ], + [ + "acBaBaC", + "bbccbac", + 10 + ], + [ + "acBacCbAc", + "bBBACbaAA", + 11 + ], + [ + "acBb", + "CA", + 7 + ], + [ + "acBb", + "bCBAAB", + 8 + ], + [ + "acBbAc", + "aaaBCA", + 8 + ], + [ + "acBbCca", + "acA", + 9 + ], + [ + "acBcCcaB", + "CC", + 13 + ], + [ + "acBcabcAC", + "Aac", + 13 + ], + [ + "acC", + "AABcbaC", + 9 + ], + [ + "acC", + "AcBAaC", + 7 + ], + [ + "acC", + "CabBabaBC", + 14 + ], + [ + "acC", + "b", + 6 + ], + [ + "acC", + "baBCBCbAC", + 13 + ], + [ + "acC", + "baaCBCBC", + 11 + ], + [ + "acCA", + "C", + 6 + ], + [ + "acCA", + "aabBCaaaC", + 13 + ], + [ + "acCAAaCB", + "ba", + 14 + ], + [ + "acCAB", + "BccCcCbB", + 10 + ], + [ + "acCAa", + "c", + 8 + ], + [ + "acCAabBcc", + "Bb", + 16 + ], + [ + "acCAb", + "abB", + 7 + ], + [ + "acCAcc", + "cccCCacCC", + 10 + ], + [ + "acCB", + "B", + 6 + ], + [ + "acCBAcb", + "acaBcA", + 6 + ], + [ + "acCBBA", + "caccBbabC", + 9 + ], + [ + "acCBBb", + "aCbc", + 7 + ], + [ + "acCBCbaBC", + "bCabBc", + 11 + ], + [ + "acCBaaAc", + "BAAABCA", + 13 + ], + [ + "acCC", + "BAB", + 8 + ], + [ + "acCCABBb", + "Bbcca", + 13 + ], + [ + "acCCCaAb", + "acCCbAcca", + 9 + ], + [ + "acCCab", + "aB", + 9 + ], + [ + "acCCbA", + "CCAbCbBA", + 9 + ], + [ + "acCCbBcc", + "aAAaA", + 14 + ], + [ + "acCCcaC", + "AcbBabcCb", + 13 + ], + [ + "acCCcab", + "ccab", + 6 + ], + [ + "acCa", + "aaAcAbBbB", + 14 + ], + [ + "acCaAA", + "BAbcAcbC", + 13 + ], + [ + "acCaABcbb", + "A", + 16 + ], + [ + "acCaC", + "abBACBCB", + 10 + ], + [ + "acCaCa", + "aBa", + 8 + ], + [ + "acCb", + "aACBa", + 5 + ], + [ + "acCbBBa", + "BbbACCccb", + 15 + ], + [ + "acCbCCCBb", + "B", + 16 + ], + [ + "acCbbBB", + "bcca", + 11 + ], + [ + "acCc", + "A", + 7 + ], + [ + "acCc", + "AABBa", + 9 + ], + [ + "acCcA", + "CBBcaBCCA", + 11 + ], + [ + "acCcAcB", + "b", + 13 + ], + [ + "acCcB", + "BBCCBA", + 7 + ], + [ + "acCcaAA", + "b", + 14 + ], + [ + "acCcacB", + "cbAccBcb", + 10 + ], + [ + "acCcc", + "AC", + 7 + ], + [ + "aca", + "ACAB", + 5 + ], + [ + "aca", + "AaAc", + 6 + ], + [ + "aca", + "cAAbacBCa", + 12 + ], + [ + "aca", + "cbCCabAaB", + 14 + ], + [ + "acaA", + "C", + 7 + ], + [ + "acaA", + "aCbcACABa", + 11 + ], + [ + "acaAAAbc", + "B", + 15 + ], + [ + "acaABAcA", + "Cacca", + 10 + ], + [ + "acaAcb", + "cbc", + 8 + ], + [ + "acaBA", + "baBCbCBBC", + 13 + ], + [ + "acaBcacBc", + "b", + 17 + ], + [ + "acaBccaC", + "C", + 14 + ], + [ + "acaC", + "bccCAccA", + 12 + ], + [ + "acaCAaBB", + "Bc", + 14 + ], + [ + "acaCBBB", + "A", + 13 + ], + [ + "acaCaA", + "BCB", + 10 + ], + [ + "acaCaC", + "c", + 10 + ], + [ + "acaCbC", + "acaabCaa", + 6 + ], + [ + "acaCcaAB", + "ABAaaaba", + 12 + ], + [ + "acaCcbcB", + "CcbACBBc", + 10 + ], + [ + "acaCcbcc", + "C", + 14 + ], + [ + "acaa", + "AabBa", + 6 + ], + [ + "acaaBAA", + "cBBbaAB", + 10 + ], + [ + "acaaCC", + "cAACA", + 6 + ], + [ + "acaaaCCA", + "AaC", + 11 + ], + [ + "acaacAbB", + "bCbb", + 12 + ], + [ + "acaaca", + "cBACccCa", + 11 + ], + [ + "acaacb", + "ccCbBca", + 10 + ], + [ + "acabCb", + "ccaCbA", + 6 + ], + [ + "acabaBCbB", + "cab", + 12 + ], + [ + "acabcA", + "AB", + 10 + ], + [ + "acacA", + "bcBACcaa", + 10 + ], + [ + "acacbA", + "A", + 10 + ], + [ + "acacbABa", + "bCbcBa", + 9 + ], + [ + "acacbBa", + "BAbA", + 10 + ], + [ + "acb", + "AabAbA", + 8 + ], + [ + "acb", + "BCCa", + 7 + ], + [ + "acb", + "cBCB", + 6 + ], + [ + "acbA", + "AcBCbCa", + 8 + ], + [ + "acbA", + "aAaBCAB", + 9 + ], + [ + "acbAA", + "bc", + 8 + ], + [ + "acbACbbA", + "a", + 14 + ], + [ + "acbAbacc", + "caAcB", + 10 + ], + [ + "acbB", + "aBAb", + 5 + ], + [ + "acbBBC", + "caB", + 8 + ], + [ + "acbBaAAb", + "bbBcABBaC", + 13 + ], + [ + "acbBaC", + "CaA", + 9 + ], + [ + "acbBc", + "abB", + 4 + ], + [ + "acbC", + "ABABaBbca", + 13 + ], + [ + "acbC", + "bbBbcaaA", + 13 + ], + [ + "acbCA", + "A", + 8 + ], + [ + "acbCA", + "cBb", + 7 + ], + [ + "acbCBb", + "acAbCCaBc", + 8 + ], + [ + "acbCaaAaB", + "CaCc", + 14 + ], + [ + "acbCabC", + "cBACbcBC", + 10 + ], + [ + "acbaBaA", + "BBcA", + 9 + ], + [ + "acbabAa", + "CaAbcbc", + 10 + ], + [ + "acbabbaB", + "c", + 14 + ], + [ + "acbb", + "AAa", + 7 + ], + [ + "acbb", + "AbCaBCcBA", + 13 + ], + [ + "acbb", + "BbAca", + 9 + ], + [ + "acbb", + "CBaCBcCbA", + 12 + ], + [ + "acbbA", + "AcCAAbc", + 9 + ], + [ + "acbbAbA", + "CCCaccab", + 13 + ], + [ + "acbbcABBB", + "BcCBbb", + 11 + ], + [ + "acbc", + "aBAbB", + 6 + ], + [ + "acbcAa", + "BaCC", + 10 + ], + [ + "acbcBbc", + "B", + 12 + ], + [ + "acbcCAC", + "ACaAab", + 11 + ], + [ + "acbcCab", + "BAb", + 10 + ], + [ + "acbcaacbc", + "CbccCCBcb", + 11 + ], + [ + "acbcbbcc", + "CcAbaCCbb", + 13 + ], + [ + "acbccACA", + "b", + 14 + ], + [ + "acbccBcB", + "CCcc", + 10 + ], + [ + "acbccCB", + "bbbaaCAAB", + 12 + ], + [ + "acc", + "C", + 5 + ], + [ + "acc", + "bAac", + 5 + ], + [ + "accA", + "CBBaA", + 8 + ], + [ + "accAA", + "C", + 9 + ], + [ + "accABbb", + "cBbAAbaC", + 12 + ], + [ + "accAac", + "CCcacb", + 7 + ], + [ + "accAb", + "bcbAbB", + 6 + ], + [ + "accAcbAa", + "B", + 15 + ], + [ + "accB", + "bBbAb", + 9 + ], + [ + "accBC", + "bCCCCab", + 10 + ], + [ + "accBbcCaa", + "aaCcaBACB", + 12 + ], + [ + "accC", + "CCcbBc", + 8 + ], + [ + "accC", + "aa", + 6 + ], + [ + "accCAcACa", + "AABbCCc", + 14 + ], + [ + "accCCaCB", + "aCBba", + 11 + ], + [ + "accCCcA", + "cBbA", + 10 + ], + [ + "accCaB", + "aBaB", + 6 + ], + [ + "accCbbC", + "cBBcaa", + 12 + ], + [ + "acca", + "BBCAcbaba", + 13 + ], + [ + "acca", + "BBcCC", + 7 + ], + [ + "accaACAAc", + "cbbCABA", + 12 + ], + [ + "accaaAcAb", + "ccB", + 13 + ], + [ + "accaaC", + "CbA", + 10 + ], + [ + "accab", + "AAaAcA", + 9 + ], + [ + "accabbcB", + "aa", + 12 + ], + [ + "accac", + "C", + 9 + ], + [ + "accb", + "CBCAcCaBB", + 13 + ], + [ + "accbAA", + "Ba", + 10 + ], + [ + "accbAa", + "Ca", + 9 + ], + [ + "accbBac", + "caC", + 9 + ], + [ + "accbCcBAc", + "cbA", + 12 + ], + [ + "accbaAc", + "ACAaCAAA", + 11 + ], + [ + "accbb", + "CCcC", + 7 + ], + [ + "acccAB", + "cb", + 9 + ], + [ + "acccC", + "baaaaB", + 10 + ], + [ + "acccaAaB", + "C", + 15 + ], + [ + "acccc", + "cAAC", + 7 + ], + [ + "b", + "A", + 2 + ], + [ + "b", + "AAbAaCb", + 12 + ], + [ + "b", + "ABBBC", + 9 + ], + [ + "b", + "ABbCCBC", + 12 + ], + [ + "b", + "ABc", + 5 + ], + [ + "b", + "ABcba", + 8 + ], + [ + "b", + "ACAAC", + 10 + ], + [ + "b", + "ACBbAcabc", + 16 + ], + [ + "b", + "ACBbCBcCC", + 16 + ], + [ + "b", + "ACCaA", + 10 + ], + [ + "b", + "Aa", + 4 + ], + [ + "b", + "AaAbBCA", + 12 + ], + [ + "b", + "Aac", + 6 + ], + [ + "b", + "AacaBc", + 11 + ], + [ + "b", + "AbaC", + 6 + ], + [ + "b", + "Abcba", + 8 + ], + [ + "b", + "Ac", + 4 + ], + [ + "b", + "AcCbB", + 8 + ], + [ + "b", + "AcCbBca", + 12 + ], + [ + "b", + "AcaaCaAa", + 16 + ], + [ + "b", + "B", + 1 + ], + [ + "b", + "BBA", + 5 + ], + [ + "b", + "BBaaabB", + 12 + ], + [ + "b", + "BBbaBcba", + 14 + ], + [ + "b", + "BCBaC", + 9 + ], + [ + "b", + "BCcAABb", + 12 + ], + [ + "b", + "Ba", + 3 + ], + [ + "b", + "BaC", + 5 + ], + [ + "b", + "BaCbBc", + 10 + ], + [ + "b", + "Bac", + 5 + ], + [ + "b", + "BacBAc", + 11 + ], + [ + "b", + "Bb", + 2 + ], + [ + "b", + "BbA", + 4 + ], + [ + "b", + "BbBacbb", + 12 + ], + [ + "b", + "BbacaCc", + 12 + ], + [ + "b", + "BbbcAcAab", + 16 + ], + [ + "b", + "Bc", + 3 + ], + [ + "b", + "BcBA", + 7 + ], + [ + "b", + "BcBCAAaCB", + 17 + ], + [ + "b", + "BcC", + 5 + ], + [ + "b", + "BcCa", + 7 + ], + [ + "b", + "C", + 2 + ], + [ + "b", + "CAB", + 5 + ], + [ + "b", + "CACCBca", + 13 + ], + [ + "b", + "CBABaBAB", + 15 + ], + [ + "b", + "CBCCa", + 9 + ], + [ + "b", + "CBCCcC", + 11 + ], + [ + "b", + "CBCcaBBAB", + 17 + ], + [ + "b", + "CBaBaaCbB", + 16 + ], + [ + "b", + "CBaabA", + 10 + ], + [ + "b", + "CCBABBcC", + 15 + ], + [ + "b", + "CCC", + 6 + ], + [ + "b", + "CCbCA", + 8 + ], + [ + "b", + "Ca", + 4 + ], + [ + "b", + "CaCBcA", + 11 + ], + [ + "b", + "Cabacc", + 10 + ], + [ + "b", + "CabccCBCB", + 16 + ], + [ + "b", + "CacAbAAac", + 16 + ], + [ + "b", + "CbA", + 4 + ], + [ + "b", + "CbcBbA", + 10 + ], + [ + "b", + "Cc", + 4 + ], + [ + "b", + "CcAABbA", + 12 + ], + [ + "b", + "CcabBB", + 10 + ], + [ + "b", + "CcacCcAcC", + 18 + ], + [ + "b", + "CcbCACB", + 12 + ], + [ + "b", + "Ccbc", + 6 + ], + [ + "b", + "a", + 2 + ], + [ + "b", + "aA", + 4 + ], + [ + "b", + "aABBCcC", + 13 + ], + [ + "b", + "aABccbaCc", + 16 + ], + [ + "b", + "aACCCCba", + 14 + ], + [ + "b", + "aACCa", + 10 + ], + [ + "b", + "aAaBA", + 9 + ], + [ + "b", + "aAacBab", + 12 + ], + [ + "b", + "aB", + 3 + ], + [ + "b", + "aBCAac", + 11 + ], + [ + "b", + "aBCBC", + 9 + ], + [ + "b", + "aBCbacab", + 14 + ], + [ + "b", + "aBaCa", + 9 + ], + [ + "b", + "aBc", + 5 + ], + [ + "b", + "aC", + 4 + ], + [ + "b", + "aCBA", + 7 + ], + [ + "b", + "aCCa", + 8 + ], + [ + "b", + "aa", + 4 + ], + [ + "b", + "aabCCBca", + 14 + ], + [ + "b", + "aabbAb", + 10 + ], + [ + "b", + "aabbcC", + 10 + ], + [ + "b", + "ab", + 2 + ], + [ + "b", + "abB", + 4 + ], + [ + "b", + "abBBcb", + 10 + ], + [ + "b", + "abCCBCA", + 12 + ], + [ + "b", + "abcACcaa", + 14 + ], + [ + "b", + "abcBccCa", + 14 + ], + [ + "b", + "abca", + 6 + ], + [ + "b", + "ac", + 4 + ], + [ + "b", + "acACCaAca", + 18 + ], + [ + "b", + "acC", + 6 + ], + [ + "b", + "aca", + 6 + ], + [ + "b", + "acaabCC", + 12 + ], + [ + "b", + "b", + 0 + ], + [ + "b", + "bACAbB", + 10 + ], + [ + "b", + "bBAccb", + 10 + ], + [ + "b", + "bBBC", + 6 + ], + [ + "b", + "bBbaAA", + 10 + ], + [ + "b", + "bC", + 2 + ], + [ + "b", + "bCA", + 4 + ], + [ + "b", + "bCBCAbaCA", + 16 + ], + [ + "b", + "bCCbA", + 8 + ], + [ + "b", + "bCaAC", + 8 + ], + [ + "b", + "bCaCAB", + 10 + ], + [ + "b", + "bCabBB", + 10 + ], + [ + "b", + "bCbBbcA", + 12 + ], + [ + "b", + "bCcbAb", + 10 + ], + [ + "b", + "baBACBaaB", + 16 + ], + [ + "b", + "baCCbBC", + 12 + ], + [ + "b", + "baabBABb", + 14 + ], + [ + "b", + "baabbaCBB", + 16 + ], + [ + "b", + "baaccc", + 10 + ], + [ + "b", + "babBAcb", + 12 + ], + [ + "b", + "bb", + 2 + ], + [ + "b", + "bcBACbaBb", + 16 + ], + [ + "b", + "bcCa", + 6 + ], + [ + "b", + "bcCcAcbc", + 14 + ], + [ + "b", + "c", + 2 + ], + [ + "b", + "cABCaB", + 11 + ], + [ + "b", + "cAC", + 6 + ], + [ + "b", + "cAbBbBcc", + 14 + ], + [ + "b", + "cAbaBABBc", + 16 + ], + [ + "b", + "cAbaCBAa", + 14 + ], + [ + "b", + "cBAAAcaA", + 15 + ], + [ + "b", + "cBBcbAabb", + 16 + ], + [ + "b", + "cBCCAcb", + 12 + ], + [ + "b", + "cBCCa", + 9 + ], + [ + "b", + "cBCaCAc", + 13 + ], + [ + "b", + "cBbaBbcB", + 14 + ], + [ + "b", + "cBbbBaAa", + 14 + ], + [ + "b", + "cCAACba", + 12 + ], + [ + "b", + "cCaccAaCb", + 16 + ], + [ + "b", + "cCcbA", + 8 + ], + [ + "b", + "caA", + 6 + ], + [ + "b", + "caAb", + 6 + ], + [ + "b", + "caAccaCbb", + 16 + ], + [ + "b", + "caB", + 5 + ], + [ + "b", + "cabBbcA", + 12 + ], + [ + "b", + "cacbbBAb", + 14 + ], + [ + "b", + "cb", + 2 + ], + [ + "b", + "cbAa", + 6 + ], + [ + "b", + "cbAaCa", + 10 + ], + [ + "b", + "cbcBAcCbA", + 16 + ], + [ + "b", + "ccCaacc", + 14 + ], + [ + "b", + "ccCbaaaa", + 14 + ], + [ + "b", + "ccaC", + 8 + ], + [ + "b", + "ccbbcb", + 10 + ], + [ + "bA", + "AAAcACAbA", + 14 + ], + [ + "bA", + "AAbB", + 6 + ], + [ + "bA", + "ABaC", + 6 + ], + [ + "bA", + "ACBbbA", + 8 + ], + [ + "bA", + "ACBbccB", + 12 + ], + [ + "bA", + "ACbbBB", + 10 + ], + [ + "bA", + "Accc", + 8 + ], + [ + "bA", + "BAcCabc", + 11 + ], + [ + "bA", + "BCCcccB", + 13 + ], + [ + "bA", + "BabcCBCaa", + 15 + ], + [ + "bA", + "BbCaCBca", + 13 + ], + [ + "bA", + "BbbcbCCB", + 14 + ], + [ + "bA", + "Bbca", + 5 + ], + [ + "bA", + "C", + 4 + ], + [ + "bA", + "CAbABbBC", + 12 + ], + [ + "bA", + "CB", + 4 + ], + [ + "bA", + "CBCBa", + 8 + ], + [ + "bA", + "CbAaAaB", + 10 + ], + [ + "bA", + "CbcAAc", + 8 + ], + [ + "bA", + "CcAAcbcBC", + 16 + ], + [ + "bA", + "aAc", + 4 + ], + [ + "bA", + "aBCCcAcb", + 13 + ], + [ + "bA", + "aCb", + 6 + ], + [ + "bA", + "aCcbbc", + 10 + ], + [ + "bA", + "bAbcaAA", + 10 + ], + [ + "bA", + "bAc", + 2 + ], + [ + "bA", + "ba", + 1 + ], + [ + "bA", + "bb", + 2 + ], + [ + "bA", + "cAcCABaAb", + 15 + ], + [ + "bA", + "cCAABbBb", + 14 + ], + [ + "bA", + "cCC", + 6 + ], + [ + "bA", + "cabc", + 6 + ], + [ + "bA", + "ccCbcC", + 10 + ], + [ + "bAA", + "AabBB", + 8 + ], + [ + "bAA", + "BBAAaBCbC", + 13 + ], + [ + "bAA", + "C", + 6 + ], + [ + "bAA", + "CBaBcBb", + 12 + ], + [ + "bAA", + "CCBAc", + 7 + ], + [ + "bAA", + "abBBBAA", + 8 + ], + [ + "bAA", + "baacAbb", + 9 + ], + [ + "bAA", + "bcbBccAb", + 12 + ], + [ + "bAAA", + "C", + 8 + ], + [ + "bAAA", + "bBAAC", + 4 + ], + [ + "bAAAA", + "c", + 10 + ], + [ + "bAAAAbaCa", + "abacbC", + 12 + ], + [ + "bAAACbbb", + "Aaa", + 12 + ], + [ + "bAAACccC", + "CC", + 12 + ], + [ + "bAAAacAc", + "caC", + 12 + ], + [ + "bAAAbABC", + "CBCbBa", + 12 + ], + [ + "bAAB", + "bC", + 6 + ], + [ + "bAABaCA", + "A", + 12 + ], + [ + "bAABb", + "CaBBB", + 6 + ], + [ + "bAABcbbB", + "CbACCB", + 11 + ], + [ + "bAAC", + "AabbbaBA", + 13 + ], + [ + "bAAC", + "Acb", + 6 + ], + [ + "bAAC", + "aaB", + 6 + ], + [ + "bAACcACbc", + "Aaa", + 14 + ], + [ + "bAAa", + "AaABbabC", + 11 + ], + [ + "bAAa", + "bCAbbaa", + 7 + ], + [ + "bAAaCba", + "bAca", + 7 + ], + [ + "bAAaaaa", + "BC", + 13 + ], + [ + "bAAbAab", + "CBcA", + 12 + ], + [ + "bAAbCa", + "C", + 10 + ], + [ + "bAAbb", + "A", + 8 + ], + [ + "bAAc", + "CCAbca", + 8 + ], + [ + "bAAc", + "cCBAba", + 9 + ], + [ + "bAAcA", + "bbc", + 6 + ], + [ + "bAAccABbb", + "aAc", + 13 + ], + [ + "bAAccaCC", + "A", + 14 + ], + [ + "bAB", + "ACBcB", + 7 + ], + [ + "bAB", + "CbAA", + 4 + ], + [ + "bAB", + "bbABC", + 4 + ], + [ + "bABAC", + "BCaabaCBC", + 12 + ], + [ + "bABACCCB", + "abB", + 12 + ], + [ + "bABAaB", + "cCCCBbc", + 13 + ], + [ + "bABAaCBBA", + "BAbA", + 11 + ], + [ + "bABAaba", + "A", + 12 + ], + [ + "bABAba", + "BBBAccBA", + 9 + ], + [ + "bABAbbCbB", + "bA", + 14 + ], + [ + "bABAcCCb", + "abCaB", + 11 + ], + [ + "bABBCCC", + "bbCbBc", + 10 + ], + [ + "bABBa", + "bA", + 6 + ], + [ + "bABBcAc", + "bAC", + 9 + ], + [ + "bABC", + "AbBca", + 7 + ], + [ + "bABCBcAc", + "cBCAA", + 10 + ], + [ + "bABCC", + "aBCa", + 5 + ], + [ + "bABCCbbbC", + "cABACBBa", + 10 + ], + [ + "bABCaBCa", + "CaBbB", + 10 + ], + [ + "bABCbaB", + "ACCaCBc", + 10 + ], + [ + "bABa", + "BA", + 5 + ], + [ + "bABa", + "BAB", + 3 + ], + [ + "bABaCBBAa", + "Aa", + 14 + ], + [ + "bABb", + "acA", + 7 + ], + [ + "bABb", + "ccCA", + 8 + ], + [ + "bABbAB", + "ABCBCACc", + 11 + ], + [ + "bABbAC", + "bcBaCcAb", + 10 + ], + [ + "bABbBCB", + "cb", + 12 + ], + [ + "bABbaB", + "cAcAbbCAb", + 11 + ], + [ + "bABbaBAA", + "C", + 16 + ], + [ + "bABbbABBB", + "cAcb", + 14 + ], + [ + "bABbbbC", + "a", + 13 + ], + [ + "bABbcCab", + "ccccaa", + 11 + ], + [ + "bAC", + "AcAbc", + 7 + ], + [ + "bAC", + "bCcbAabb", + 12 + ], + [ + "bACA", + "ACaAAAaC", + 12 + ], + [ + "bACA", + "CAc", + 5 + ], + [ + "bACA", + "c", + 7 + ], + [ + "bACACAAa", + "cbBbC", + 14 + ], + [ + "bACAb", + "Cbc", + 8 + ], + [ + "bACAcc", + "BacBcAB", + 9 + ], + [ + "bACB", + "cBccC", + 8 + ], + [ + "bACBAC", + "BCCABaC", + 6 + ], + [ + "bACBBaA", + "bBACB", + 8 + ], + [ + "bACBCbb", + "b", + 12 + ], + [ + "bACBa", + "BCC", + 7 + ], + [ + "bACBaAcAb", + "C", + 16 + ], + [ + "bACBabABB", + "aCA", + 13 + ], + [ + "bACBcB", + "accBbaCcC", + 13 + ], + [ + "bACCaCcBb", + "aBaBcb", + 11 + ], + [ + "bACaBC", + "bA", + 8 + ], + [ + "bACaCCBAB", + "aaBacb", + 13 + ], + [ + "bACaCcC", + "aBCac", + 8 + ], + [ + "bACab", + "aBcBcAB", + 10 + ], + [ + "bACac", + "Bc", + 7 + ], + [ + "bACac", + "cBAacCAcB", + 10 + ], + [ + "bACbAA", + "B", + 11 + ], + [ + "bACbBbc", + "CbcAcBcA", + 11 + ], + [ + "bACc", + "bcA", + 5 + ], + [ + "bACc", + "cCb", + 6 + ], + [ + "bACcABb", + "BaC", + 10 + ], + [ + "bACcCaaC", + "AAbAbb", + 13 + ], + [ + "bAa", + "AaaACbAcc", + 14 + ], + [ + "bAa", + "AcAabb", + 8 + ], + [ + "bAa", + "bCbBCABBA", + 13 + ], + [ + "bAaA", + "aACcbaB", + 10 + ], + [ + "bAaAA", + "BacbCbBaa", + 14 + ], + [ + "bAaAAAa", + "cabAbA", + 9 + ], + [ + "bAaABAAC", + "AB", + 12 + ], + [ + "bAaAcA", + "cbC", + 11 + ], + [ + "bAaB", + "B", + 6 + ], + [ + "bAaB", + "acCCbb", + 11 + ], + [ + "bAaBAA", + "a", + 10 + ], + [ + "bAaBAcac", + "AABcaa", + 7 + ], + [ + "bAaBC", + "CBcaaaBa", + 10 + ], + [ + "bAaBa", + "C", + 10 + ], + [ + "bAaBaCBAB", + "CCbCCC", + 15 + ], + [ + "bAaBb", + "bCAA", + 7 + ], + [ + "bAaBbBc", + "AC", + 11 + ], + [ + "bAaBbc", + "cbbcAaab", + 10 + ], + [ + "bAaBc", + "a", + 8 + ], + [ + "bAaBc", + "cbCCBBbcc", + 12 + ], + [ + "bAaC", + "ABA", + 6 + ], + [ + "bAaC", + "BAaACAB", + 7 + ], + [ + "bAaCCa", + "CbCBB", + 10 + ], + [ + "bAaCa", + "bCBaABcbB", + 13 + ], + [ + "bAaCab", + "caBC", + 9 + ], + [ + "bAaa", + "AAcCbAC", + 11 + ], + [ + "bAaa", + "bBAAb", + 5 + ], + [ + "bAaaA", + "AbA", + 6 + ], + [ + "bAaaB", + "CABBC", + 8 + ], + [ + "bAaaB", + "bABBaC", + 6 + ], + [ + "bAaabaAc", + "cBaa", + 12 + ], + [ + "bAab", + "CcC", + 8 + ], + [ + "bAab", + "bAc", + 4 + ], + [ + "bAabAbC", + "A", + 12 + ], + [ + "bAabBACA", + "cCca", + 14 + ], + [ + "bAabBBCb", + "CbaCbA", + 12 + ], + [ + "bAabCAB", + "aA", + 10 + ], + [ + "bAabb", + "AaacAAA", + 11 + ], + [ + "bAabbC", + "babAb", + 6 + ], + [ + "bAabcA", + "bA", + 8 + ], + [ + "bAabcbc", + "BaAabcAaa", + 9 + ], + [ + "bAabccAa", + "aCbaB", + 12 + ], + [ + "bAac", + "ccACbbAA", + 13 + ], + [ + "bAacA", + "BBaACC", + 8 + ], + [ + "bAacAb", + "bacccBCc", + 10 + ], + [ + "bAacAbBca", + "C", + 17 + ], + [ + "bAacC", + "bbaAb", + 6 + ], + [ + "bAaccCB", + "c", + 12 + ], + [ + "bAb", + "ABCCAACCA", + 15 + ], + [ + "bAb", + "CCBAA", + 7 + ], + [ + "bAbACaC", + "bABcCAb", + 6 + ], + [ + "bAbAaC", + "BAbbA", + 6 + ], + [ + "bAbAabcab", + "CAabcBa", + 10 + ], + [ + "bAbAbcA", + "CAABBca", + 8 + ], + [ + "bAbBC", + "AACcbCca", + 11 + ], + [ + "bAbBCbaB", + "acacab", + 11 + ], + [ + "bAbBaCBcb", + "BaA", + 14 + ], + [ + "bAbBbbBbb", + "cAaacAC", + 16 + ], + [ + "bAbCAABaB", + "ACcBAccB", + 11 + ], + [ + "bAbCbCcCA", + "aBB", + 15 + ], + [ + "bAbCcB", + "aCAbA", + 10 + ], + [ + "bAba", + "CaAAb", + 8 + ], + [ + "bAbaCC", + "Bba", + 7 + ], + [ + "bAbab", + "CbCAcBcB", + 10 + ], + [ + "bAbb", + "acAbCBb", + 8 + ], + [ + "bAbb", + "b", + 6 + ], + [ + "bAbb", + "bAaAcBBab", + 11 + ], + [ + "bAbbB", + "AaACcBacC", + 14 + ], + [ + "bAbbaa", + "cCBC", + 11 + ], + [ + "bAbbbbBc", + "cABcBBC", + 9 + ], + [ + "bAbbbc", + "aCA", + 11 + ], + [ + "bAbc", + "Accbacc", + 9 + ], + [ + "bAbcBB", + "A", + 10 + ], + [ + "bAbcCaA", + "bACaCAcbC", + 11 + ], + [ + "bAbcCab", + "ABC", + 9 + ], + [ + "bAbcCbAA", + "BcBb", + 11 + ], + [ + "bAbcbA", + "CBCBCac", + 11 + ], + [ + "bAc", + "aaC", + 4 + ], + [ + "bAcA", + "bb", + 6 + ], + [ + "bAcAABA", + "aAAbca", + 9 + ], + [ + "bAcAAbA", + "BABabc", + 8 + ], + [ + "bAcAAbb", + "aC", + 12 + ], + [ + "bAcAbc", + "CAAcaaBa", + 10 + ], + [ + "bAcAc", + "BaC", + 7 + ], + [ + "bAcB", + "bcBC", + 4 + ], + [ + "bAcB", + "bccaA", + 6 + ], + [ + "bAcBAaaCa", + "aABCccaBa", + 12 + ], + [ + "bAcBAbBcC", + "B", + 16 + ], + [ + "bAcBBBABa", + "caA", + 14 + ], + [ + "bAcBCCbac", + "accBBAB", + 12 + ], + [ + "bAcBabbC", + "ca", + 12 + ], + [ + "bAcBbAb", + "cbaBA", + 9 + ], + [ + "bAcBbaA", + "AaBaaA", + 6 + ], + [ + "bAcC", + "Acac", + 5 + ], + [ + "bAcC", + "bba", + 6 + ], + [ + "bAcCAa", + "C", + 10 + ], + [ + "bAcCaCc", + "abbCcb", + 11 + ], + [ + "bAcCb", + "Bbcacac", + 9 + ], + [ + "bAcCbACCB", + "aA", + 15 + ], + [ + "bAcCcACA", + "ABCAaBB", + 11 + ], + [ + "bAcaAbA", + "baCCbBca", + 10 + ], + [ + "bAcac", + "BBbAbCccA", + 11 + ], + [ + "bAcb", + "aa", + 7 + ], + [ + "bAcb", + "cAbbb", + 6 + ], + [ + "bAcbAbBAC", + "CbBc", + 12 + ], + [ + "bAcbBbcb", + "cCcBBCaa", + 11 + ], + [ + "bAcc", + "aCcABC", + 9 + ], + [ + "bAccA", + "ccBaaCA", + 9 + ], + [ + "bAccCabB", + "aABCAAaAA", + 13 + ], + [ + "bAccCbb", + "BaAAcB", + 10 + ], + [ + "bAcca", + "CBbcBBBba", + 13 + ], + [ + "bAccaACaa", + "CCcBCaA", + 10 + ], + [ + "bAccaCCA", + "aABAABBCb", + 13 + ], + [ + "bAcccAC", + "ABC", + 10 + ], + [ + "bAcccAbaA", + "CBcCAbcC", + 11 + ], + [ + "bAcccaB", + "BccACBBC", + 10 + ], + [ + "bB", + "ACaAbC", + 10 + ], + [ + "bB", + "BCbCb", + 7 + ], + [ + "bB", + "Baa", + 5 + ], + [ + "bB", + "BbbbBAb", + 10 + ], + [ + "bB", + "Bcba", + 6 + ], + [ + "bB", + "CAAcBb", + 10 + ], + [ + "bB", + "CbbC", + 5 + ], + [ + "bB", + "aACCab", + 11 + ], + [ + "bB", + "aBaccb", + 10 + ], + [ + "bB", + "aBbC", + 6 + ], + [ + "bB", + "aCACcaA", + 14 + ], + [ + "bB", + "aaAABaCCc", + 16 + ], + [ + "bB", + "ac", + 4 + ], + [ + "bB", + "acACccAac", + 18 + ], + [ + "bB", + "bAAacb", + 9 + ], + [ + "bB", + "bBCccbcC", + 12 + ], + [ + "bB", + "bBbbcb", + 8 + ], + [ + "bB", + "bCbBAAbCc", + 14 + ], + [ + "bB", + "ba", + 2 + ], + [ + "bB", + "baAbcC", + 9 + ], + [ + "bB", + "bcBbACaa", + 12 + ], + [ + "bB", + "c", + 4 + ], + [ + "bB", + "cBCcc", + 8 + ], + [ + "bB", + "cCBa", + 6 + ], + [ + "bB", + "caBc", + 6 + ], + [ + "bB", + "caCCACb", + 13 + ], + [ + "bBA", + "AB", + 4 + ], + [ + "bBA", + "B", + 4 + ], + [ + "bBA", + "abBCbccC", + 12 + ], + [ + "bBA", + "bCaaa", + 7 + ], + [ + "bBA", + "cBB", + 4 + ], + [ + "bBAA", + "cAb", + 6 + ], + [ + "bBAAba", + "C", + 12 + ], + [ + "bBAAcb", + "BCcBABB", + 10 + ], + [ + "bBAB", + "ccaa", + 7 + ], + [ + "bBABbBc", + "Bb", + 10 + ], + [ + "bBAC", + "aCBb", + 8 + ], + [ + "bBACA", + "bbcaBB", + 8 + ], + [ + "bBACBA", + "CA", + 8 + ], + [ + "bBACBABc", + "ca", + 14 + ], + [ + "bBACCbCB", + "B", + 14 + ], + [ + "bBACbaC", + "aabAbaCC", + 9 + ], + [ + "bBAa", + "CBBbBaa", + 7 + ], + [ + "bBAa", + "CabC", + 8 + ], + [ + "bBAa", + "cCcCCCA", + 13 + ], + [ + "bBAaBAcB", + "CbCaaAbAA", + 12 + ], + [ + "bBAaBcAaB", + "ACcb", + 13 + ], + [ + "bBAaC", + "acBaAcC", + 8 + ], + [ + "bBAaCB", + "bcBbCBBca", + 12 + ], + [ + "bBAaCc", + "Aca", + 9 + ], + [ + "bBAac", + "AcccCbBc", + 14 + ], + [ + "bBAacA", + "aBCbaAAA", + 10 + ], + [ + "bBAbCA", + "cBAaaCACb", + 10 + ], + [ + "bBAba", + "B", + 8 + ], + [ + "bBAbac", + "cBbAaccb", + 10 + ], + [ + "bBAc", + "Bc", + 4 + ], + [ + "bBAcAbca", + "baB", + 12 + ], + [ + "bBAcBA", + "aAa", + 9 + ], + [ + "bBAcC", + "abbBbCBc", + 10 + ], + [ + "bBAcC", + "c", + 8 + ], + [ + "bBAca", + "Ba", + 6 + ], + [ + "bBAca", + "b", + 8 + ], + [ + "bBAca", + "c", + 8 + ], + [ + "bBB", + "ABACC", + 8 + ], + [ + "bBB", + "AcaBacAc", + 14 + ], + [ + "bBB", + "BCCBCB", + 7 + ], + [ + "bBB", + "Cba", + 5 + ], + [ + "bBB", + "bACbcbAc", + 12 + ], + [ + "bBBABBb", + "Cbab", + 10 + ], + [ + "bBBAa", + "c", + 10 + ], + [ + "bBBBB", + "AacCA", + 10 + ], + [ + "bBBBBAa", + "AbbBAB", + 8 + ], + [ + "bBBBCb", + "B", + 10 + ], + [ + "bBBBabC", + "abBacCCBA", + 13 + ], + [ + "bBBBc", + "AB", + 8 + ], + [ + "bBBC", + "B", + 6 + ], + [ + "bBBC", + "abAbBBcab", + 11 + ], + [ + "bBBCABbB", + "caaCC", + 14 + ], + [ + "bBBCAbB", + "BcBCBCcBc", + 10 + ], + [ + "bBBa", + "abbCAA", + 8 + ], + [ + "bBBaB", + "cacCCbAA", + 14 + ], + [ + "bBBaC", + "Bcc", + 7 + ], + [ + "bBBaaBC", + "bcbcbBba", + 11 + ], + [ + "bBBaaBb", + "a", + 12 + ], + [ + "bBBaabc", + "caaBba", + 10 + ], + [ + "bBBabac", + "aaAA", + 11 + ], + [ + "bBBb", + "Accaab", + 10 + ], + [ + "bBBb", + "aAacAbca", + 14 + ], + [ + "bBBbAcBB", + "BBAbBAb", + 9 + ], + [ + "bBBbBbbbB", + "cc", + 18 + ], + [ + "bBBbCa", + "BcC", + 8 + ], + [ + "bBBbaB", + "caAaaC", + 10 + ], + [ + "bBBbaBbCa", + "a", + 16 + ], + [ + "bBBbacabB", + "Ba", + 14 + ], + [ + "bBBbcc", + "bcA", + 8 + ], + [ + "bBBc", + "ABBbACc", + 8 + ], + [ + "bBBc", + "BAAb", + 7 + ], + [ + "bBBcA", + "caB", + 8 + ], + [ + "bBBcaca", + "CBBBbABcb", + 10 + ], + [ + "bBC", + "AB", + 4 + ], + [ + "bBC", + "aaCaCAcBc", + 15 + ], + [ + "bBC", + "bcaBACB", + 8 + ], + [ + "bBC", + "cAB", + 6 + ], + [ + "bBC", + "cBaCcbca", + 12 + ], + [ + "bBCA", + "AAc", + 7 + ], + [ + "bBCA", + "bCCACBc", + 8 + ], + [ + "bBCA", + "babCCBAC", + 9 + ], + [ + "bBCA", + "cBaabcA", + 9 + ], + [ + "bBCAB", + "ACcBBBBab", + 13 + ], + [ + "bBCABBA", + "BCabbCAB", + 9 + ], + [ + "bBCAbAb", + "caAbA", + 8 + ], + [ + "bBCAbBba", + "CbbBbbC", + 10 + ], + [ + "bBCAcBcCA", + "B", + 16 + ], + [ + "bBCBa", + "cbAAB", + 8 + ], + [ + "bBCBaBCaa", + "bA", + 15 + ], + [ + "bBCBaC", + "aABCAca", + 10 + ], + [ + "bBCBabBC", + "cCAc", + 12 + ], + [ + "bBCC", + "baAbacaA", + 12 + ], + [ + "bBCCA", + "aa", + 9 + ], + [ + "bBCCCCABc", + "babacAA", + 13 + ], + [ + "bBCCCb", + "ACc", + 9 + ], + [ + "bBCCbAb", + "a", + 13 + ], + [ + "bBCaABB", + "CbACac", + 10 + ], + [ + "bBCaCCC", + "ABccBC", + 8 + ], + [ + "bBCaabcAA", + "CBABCC", + 13 + ], + [ + "bBCac", + "BA", + 7 + ], + [ + "bBCacbCA", + "BaCBcCBC", + 10 + ], + [ + "bBCb", + "bBcCA", + 4 + ], + [ + "bBCbAAaAC", + "CCbCCA", + 12 + ], + [ + "bBCbBa", + "AbBaACCB", + 10 + ], + [ + "bBCbb", + "aCcaC", + 9 + ], + [ + "bBCc", + "ccB", + 7 + ], + [ + "bBCcaAAa", + "caabAbC", + 13 + ], + [ + "bBa", + "Bba", + 2 + ], + [ + "bBa", + "b", + 4 + ], + [ + "bBa", + "bBcAca", + 6 + ], + [ + "bBa", + "bCA", + 3 + ], + [ + "bBa", + "cACaAbBa", + 10 + ], + [ + "bBa", + "ccBcbcAcc", + 15 + ], + [ + "bBaABCcC", + "ABA", + 12 + ], + [ + "bBaACBaB", + "BCBBABbb", + 10 + ], + [ + "bBaAaA", + "Bb", + 10 + ], + [ + "bBaAaBc", + "BAbcaACBc", + 8 + ], + [ + "bBaAaC", + "cCBACaaA", + 10 + ], + [ + "bBaAb", + "bACbbacAB", + 10 + ], + [ + "bBaBCAAaa", + "CACBAc", + 13 + ], + [ + "bBaBCCcA", + "bb", + 13 + ], + [ + "bBaBCaAaB", + "bCAbCbbB", + 10 + ], + [ + "bBaBCcBcB", + "cabcc", + 11 + ], + [ + "bBaBa", + "BC", + 8 + ], + [ + "bBaBbAbAB", + "aBAAbCCBA", + 12 + ], + [ + "bBaBbb", + "BaCAbbb", + 7 + ], + [ + "bBaC", + "BAaAcb", + 8 + ], + [ + "bBaCbcC", + "bA", + 11 + ], + [ + "bBaCcAA", + "cAbb", + 12 + ], + [ + "bBaaBb", + "Caa", + 8 + ], + [ + "bBaaBcBbb", + "cABAaA", + 15 + ], + [ + "bBaaCABa", + "bcbaBcbC", + 11 + ], + [ + "bBaaCa", + "bAAC", + 6 + ], + [ + "bBaabbbbc", + "cbA", + 16 + ], + [ + "bBaacaC", + "bccaBC", + 8 + ], + [ + "bBabAcBA", + "ccaAbA", + 9 + ], + [ + "bBabB", + "BabcBBB", + 8 + ], + [ + "bBabC", + "ABc", + 7 + ], + [ + "bBabaAC", + "ccaCb", + 12 + ], + [ + "bBac", + "AbaAcAaB", + 11 + ], + [ + "bBac", + "BBCc", + 3 + ], + [ + "bBacbBcb", + "bCbc", + 9 + ], + [ + "bBb", + "AA", + 6 + ], + [ + "bBb", + "AbCaacc", + 12 + ], + [ + "bBb", + "CaaCCCaA", + 16 + ], + [ + "bBb", + "Ccac", + 8 + ], + [ + "bBb", + "a", + 6 + ], + [ + "bBb", + "aCC", + 6 + ], + [ + "bBb", + "baAC", + 6 + ], + [ + "bBb", + "cBA", + 4 + ], + [ + "bBb", + "caBaCBa", + 11 + ], + [ + "bBbA", + "C", + 8 + ], + [ + "bBbA", + "CBcBAA", + 7 + ], + [ + "bBbA", + "caBBABaBa", + 13 + ], + [ + "bBbAAa", + "aabbb", + 10 + ], + [ + "bBbB", + "AcbAA", + 8 + ], + [ + "bBbB", + "a", + 8 + ], + [ + "bBbBBaBAc", + "c", + 16 + ], + [ + "bBbBBba", + "Bbc", + 10 + ], + [ + "bBbBb", + "B", + 8 + ], + [ + "bBbCA", + "abBBcBBbc", + 12 + ], + [ + "bBbCCca", + "aCaCBCB", + 11 + ], + [ + "bBba", + "aAA", + 7 + ], + [ + "bBba", + "abB", + 6 + ], + [ + "bBbaA", + "cBCBCcCCB", + 15 + ], + [ + "bBbaC", + "bc", + 7 + ], + [ + "bBbaCba", + "aBBccAaB", + 10 + ], + [ + "bBbabBAC", + "bCBa", + 11 + ], + [ + "bBbacbBCA", + "BA", + 14 + ], + [ + "bBbbCA", + "bACbBAa", + 8 + ], + [ + "bBbbbc", + "c", + 10 + ], + [ + "bBbcAAba", + "cC", + 14 + ], + [ + "bBbcBBCa", + "b", + 14 + ], + [ + "bBbcCC", + "aacCBb", + 10 + ], + [ + "bBbcCC", + "acabCCb", + 9 + ], + [ + "bBbcbA", + "BcBCabB", + 9 + ], + [ + "bBbcbA", + "cBaCcCbB", + 10 + ], + [ + "bBbcbBa", + "BbA", + 9 + ], + [ + "bBc", + "A", + 6 + ], + [ + "bBc", + "ACA", + 6 + ], + [ + "bBc", + "CC", + 5 + ], + [ + "bBc", + "bCbA", + 5 + ], + [ + "bBc", + "bbcA", + 3 + ], + [ + "bBcA", + "BaCba", + 7 + ], + [ + "bBcAACbB", + "bCba", + 10 + ], + [ + "bBcABCb", + "BAca", + 9 + ], + [ + "bBcAa", + "Ca", + 7 + ], + [ + "bBcAcBabB", + "ccaCAb", + 11 + ], + [ + "bBcAccBAc", + "cAc", + 12 + ], + [ + "bBcB", + "Ca", + 7 + ], + [ + "bBcBAbB", + "A", + 12 + ], + [ + "bBcBAca", + "BcbC", + 8 + ], + [ + "bBcBc", + "C", + 9 + ], + [ + "bBcCCbcb", + "CAAcacCaC", + 14 + ], + [ + "bBcCaAb", + "CbbbCa", + 9 + ], + [ + "bBcCbAAa", + "c", + 14 + ], + [ + "bBcaaaCAC", + "ACBbc", + 16 + ], + [ + "bBcb", + "bCCac", + 7 + ], + [ + "bBcba", + "bba", + 4 + ], + [ + "bBcbaB", + "BaCCbc", + 10 + ], + [ + "bBcbaBAbB", + "AAb", + 13 + ], + [ + "bBcbaBbAB", + "c", + 16 + ], + [ + "bBcc", + "CcBABA", + 10 + ], + [ + "bBcc", + "aAc", + 6 + ], + [ + "bBccACBbA", + "CaCCCB", + 12 + ], + [ + "bBccBc", + "Caac", + 9 + ], + [ + "bBccC", + "bAcc", + 4 + ], + [ + "bBccb", + "bccAbBbC", + 10 + ], + [ + "bBccb", + "caBAA", + 10 + ], + [ + "bBccbBbCb", + "AAAbbaBA", + 15 + ], + [ + "bBccbBccC", + "c", + 16 + ], + [ + "bBccbb", + "AaABa", + 11 + ], + [ + "bBcccB", + "bcCAca", + 7 + ], + [ + "bC", + "A", + 4 + ], + [ + "bC", + "ACBBaBa", + 12 + ], + [ + "bC", + "Abc", + 3 + ], + [ + "bC", + "BAAcb", + 8 + ], + [ + "bC", + "BAa", + 5 + ], + [ + "bC", + "BAcaBCAcc", + 15 + ], + [ + "bC", + "BCbcCA", + 8 + ], + [ + "bC", + "BacAcb", + 10 + ], + [ + "bC", + "CC", + 2 + ], + [ + "bC", + "CbBcAbcc", + 13 + ], + [ + "bC", + "aBaCA", + 7 + ], + [ + "bC", + "aCCbCABBB", + 14 + ], + [ + "bC", + "aaBBCAA", + 11 + ], + [ + "bC", + "aaCbaAcB", + 13 + ], + [ + "bC", + "acCcbcB", + 11 + ], + [ + "bC", + "b", + 2 + ], + [ + "bC", + "bBBBC", + 6 + ], + [ + "bC", + "bBb", + 4 + ], + [ + "bC", + "bC", + 0 + ], + [ + "bC", + "baAAcb", + 9 + ], + [ + "bC", + "bac", + 3 + ], + [ + "bC", + "bbBb", + 6 + ], + [ + "bC", + "c", + 3 + ], + [ + "bC", + "cABAac", + 10 + ], + [ + "bC", + "cABcACACa", + 15 + ], + [ + "bC", + "cAaCb", + 8 + ], + [ + "bC", + "cBbB", + 6 + ], + [ + "bC", + "cBcCbcc", + 11 + ], + [ + "bC", + "cBcCcAa", + 11 + ], + [ + "bC", + "cccCBcaba", + 16 + ], + [ + "bCA", + "BbCaCc", + 7 + ], + [ + "bCA", + "ac", + 5 + ], + [ + "bCA", + "bCB", + 2 + ], + [ + "bCA", + "ba", + 3 + ], + [ + "bCA", + "cbcbAc", + 7 + ], + [ + "bCAAA", + "Cc", + 8 + ], + [ + "bCAAA", + "cBabbA", + 9 + ], + [ + "bCAAABc", + "Aabab", + 11 + ], + [ + "bCAABaCbc", + "Cab", + 12 + ], + [ + "bCAABccA", + "AaacCBBcB", + 13 + ], + [ + "bCAAC", + "bBAaCcc", + 7 + ], + [ + "bCAACaCa", + "CcAACaC", + 5 + ], + [ + "bCAAaBbCA", + "CbaAcB", + 13 + ], + [ + "bCAB", + "bAAA", + 4 + ], + [ + "bCABB", + "bA", + 6 + ], + [ + "bCABBCAA", + "bBCbcCc", + 11 + ], + [ + "bCABBcB", + "bBbACbaBb", + 11 + ], + [ + "bCABa", + "AaAa", + 6 + ], + [ + "bCABaBAcc", + "CBbaCCba", + 13 + ], + [ + "bCAC", + "ABCcc", + 6 + ], + [ + "bCAC", + "caCa", + 6 + ], + [ + "bCAC", + "cbCcb", + 6 + ], + [ + "bCACBA", + "cbac", + 10 + ], + [ + "bCACBB", + "Bcb", + 9 + ], + [ + "bCACa", + "c", + 9 + ], + [ + "bCACaaC", + "Cacaaa", + 6 + ], + [ + "bCACaaa", + "cC", + 11 + ], + [ + "bCACb", + "B", + 9 + ], + [ + "bCACbA", + "Cb", + 8 + ], + [ + "bCAaCB", + "aCAbcCbB", + 8 + ], + [ + "bCAaCbac", + "a", + 14 + ], + [ + "bCAaccaCC", + "b", + 16 + ], + [ + "bCAbAAcA", + "bBBCAabAc", + 10 + ], + [ + "bCAbab", + "ca", + 9 + ], + [ + "bCAbb", + "CBAcBAcBB", + 12 + ], + [ + "bCAc", + "BaCAaaBa", + 11 + ], + [ + "bCAcBA", + "CBBcCCA", + 10 + ], + [ + "bCAcC", + "BbaccaCA", + 10 + ], + [ + "bCAcC", + "BcA", + 6 + ], + [ + "bCAcc", + "cbCAB", + 6 + ], + [ + "bCB", + "CCabaC", + 9 + ], + [ + "bCB", + "abaaAbbAC", + 15 + ], + [ + "bCB", + "bCAca", + 6 + ], + [ + "bCB", + "cBAB", + 5 + ], + [ + "bCBA", + "BccBbcAA", + 10 + ], + [ + "bCBAAa", + "c", + 11 + ], + [ + "bCBBBCbc", + "CAbbbB", + 10 + ], + [ + "bCBBBcB", + "BBaBCBbC", + 10 + ], + [ + "bCBBa", + "CccaaC", + 9 + ], + [ + "bCBBbCc", + "bBaAB", + 10 + ], + [ + "bCBC", + "AbaCcaCB", + 10 + ], + [ + "bCBC", + "CcbaCaA", + 10 + ], + [ + "bCBC", + "aCAbcbCC", + 10 + ], + [ + "bCBCAB", + "C", + 10 + ], + [ + "bCBCACBb", + "bBacBCaA", + 11 + ], + [ + "bCBCBcbc", + "bA", + 14 + ], + [ + "bCBCaAc", + "a", + 12 + ], + [ + "bCBCbC", + "A", + 12 + ], + [ + "bCBa", + "Ba", + 4 + ], + [ + "bCBaAb", + "CABCCcb", + 10 + ], + [ + "bCBaabC", + "ACb", + 10 + ], + [ + "bCBabCa", + "cAbaccCC", + 11 + ], + [ + "bCBacABab", + "ccAacAaCc", + 11 + ], + [ + "bCBacaAB", + "ACbABB", + 10 + ], + [ + "bCBb", + "aaAbAbbC", + 11 + ], + [ + "bCBb", + "cBabcA", + 9 + ], + [ + "bCBb", + "cBcccaA", + 12 + ], + [ + "bCBbAaAb", + "BabA", + 11 + ], + [ + "bCBbAac", + "CBcca", + 8 + ], + [ + "bCBbAcC", + "CBbBbb", + 8 + ], + [ + "bCBbC", + "CA", + 8 + ], + [ + "bCBbaCCA", + "BAAab", + 13 + ], + [ + "bCBbabb", + "BcCBA", + 10 + ], + [ + "bCBbbCb", + "ABcbAba", + 10 + ], + [ + "bCBc", + "bbBACc", + 6 + ], + [ + "bCBcB", + "bCAbccACb", + 10 + ], + [ + "bCBcBBc", + "b", + 12 + ], + [ + "bCBca", + "cAABaCCa", + 11 + ], + [ + "bCBcabA", + "CAcbbcBcB", + 14 + ], + [ + "bCBcba", + "ABAcbc", + 8 + ], + [ + "bCBcccbC", + "Cbbbc", + 10 + ], + [ + "bCC", + "A", + 6 + ], + [ + "bCC", + "ABBbCCC", + 8 + ], + [ + "bCC", + "AaC", + 4 + ], + [ + "bCC", + "BB", + 5 + ], + [ + "bCC", + "a", + 6 + ], + [ + "bCC", + "aCbBaaCB", + 12 + ], + [ + "bCC", + "b", + 4 + ], + [ + "bCC", + "bbBaCaCAc", + 12 + ], + [ + "bCC", + "cB", + 5 + ], + [ + "bCCAACA", + "bc", + 11 + ], + [ + "bCCAAbAa", + "aC", + 14 + ], + [ + "bCCACBb", + "BcAA", + 10 + ], + [ + "bCCAc", + "bcBCABabC", + 10 + ], + [ + "bCCBACAa", + "cCB", + 11 + ], + [ + "bCCBC", + "CbbbACC", + 10 + ], + [ + "bCCBaBa", + "BC", + 11 + ], + [ + "bCCBbBAA", + "ACBbbBc", + 9 + ], + [ + "bCCCABcb", + "BaBbbAb", + 12 + ], + [ + "bCCCa", + "aA", + 9 + ], + [ + "bCCCbccc", + "aCAA", + 14 + ], + [ + "bCCa", + "AabcB", + 9 + ], + [ + "bCCa", + "BccBa", + 5 + ], + [ + "bCCa", + "bCACAaBb", + 8 + ], + [ + "bCCaA", + "cbAbaAA", + 8 + ], + [ + "bCCaAbBB", + "CcC", + 13 + ], + [ + "bCCaBa", + "AcCabCAc", + 9 + ], + [ + "bCCaCc", + "abAcCA", + 9 + ], + [ + "bCCaa", + "cA", + 8 + ], + [ + "bCCaaCaa", + "cbcc", + 14 + ], + [ + "bCCab", + "B", + 9 + ], + [ + "bCCaccbbB", + "cC", + 15 + ], + [ + "bCCb", + "cBBaBB", + 10 + ], + [ + "bCCbA", + "bcBC", + 6 + ], + [ + "bCCbACBBB", + "CA", + 14 + ], + [ + "bCCbB", + "aaBcCbAA", + 10 + ], + [ + "bCCbCc", + "aACbC", + 6 + ], + [ + "bCCbcA", + "caACc", + 10 + ], + [ + "bCCcAcAB", + "aCAB", + 10 + ], + [ + "bCCcc", + "aACCcacB", + 8 + ], + [ + "bCCccC", + "aabAb", + 12 + ], + [ + "bCa", + "Cb", + 4 + ], + [ + "bCa", + "aBbb", + 7 + ], + [ + "bCa", + "bBACAbba", + 10 + ], + [ + "bCa", + "baba", + 4 + ], + [ + "bCa", + "cbacb", + 7 + ], + [ + "bCa", + "ccaCaCCc", + 12 + ], + [ + "bCaA", + "AABbbA", + 9 + ], + [ + "bCaA", + "CBc", + 6 + ], + [ + "bCaAC", + "ACaaABaCa", + 10 + ], + [ + "bCaBAc", + "bbbBcbcAc", + 10 + ], + [ + "bCaBBBA", + "caCcBBCcB", + 12 + ], + [ + "bCaBBC", + "cacbBC", + 6 + ], + [ + "bCaBbCAC", + "aaCAcCAbC", + 11 + ], + [ + "bCaCAabc", + "bBaB", + 11 + ], + [ + "bCaCAcaC", + "bcccaa", + 8 + ], + [ + "bCaCCC", + "cAbabbACA", + 14 + ], + [ + "bCaCcB", + "ABbbcCABC", + 12 + ], + [ + "bCaCcac", + "CBbCAA", + 10 + ], + [ + "bCaa", + "a", + 6 + ], + [ + "bCaa", + "aBC", + 7 + ], + [ + "bCaabBC", + "CCABcA", + 10 + ], + [ + "bCabAacbC", + "BAB", + 14 + ], + [ + "bCabCc", + "CA", + 9 + ], + [ + "bCabaAAB", + "A", + 14 + ], + [ + "bCabbAacb", + "Cba", + 12 + ], + [ + "bCabbC", + "CAacA", + 9 + ], + [ + "bCac", + "C", + 6 + ], + [ + "bCac", + "cC", + 6 + ], + [ + "bCacAbBa", + "a", + 14 + ], + [ + "bCacBA", + "abB", + 8 + ], + [ + "bCacCcB", + "aAabBBbC", + 13 + ], + [ + "bCaccBAa", + "CB", + 12 + ], + [ + "bCb", + "aCBc", + 5 + ], + [ + "bCb", + "ab", + 4 + ], + [ + "bCb", + "ccA", + 5 + ], + [ + "bCbA", + "ab", + 6 + ], + [ + "bCbAAAB", + "AccbcBaB", + 10 + ], + [ + "bCbAB", + "BcBB", + 5 + ], + [ + "bCbACcbc", + "b", + 14 + ], + [ + "bCbAa", + "acCBABB", + 9 + ], + [ + "bCbAbcBc", + "b", + 14 + ], + [ + "bCbB", + "b", + 6 + ], + [ + "bCbBabC", + "cbacBCaBc", + 10 + ], + [ + "bCbBacaA", + "aBAaBCaa", + 11 + ], + [ + "bCbBbbbC", + "a", + 16 + ], + [ + "bCbBbcAb", + "ba", + 13 + ], + [ + "bCbCAbB", + "CABBbb", + 9 + ], + [ + "bCbCCCAcb", + "a", + 17 + ], + [ + "bCbCbb", + "AAACCCB", + 11 + ], + [ + "bCbCccc", + "abbAbC", + 11 + ], + [ + "bCbaACBbb", + "bCCccaa", + 13 + ], + [ + "bCbaAc", + "BA", + 9 + ], + [ + "bCbaaBc", + "CbAAcaBc", + 7 + ], + [ + "bCbac", + "aAcBABbaA", + 13 + ], + [ + "bCbac", + "b", + 8 + ], + [ + "bCbacABa", + "cCaac", + 10 + ], + [ + "bCbb", + "bbCBabAC", + 9 + ], + [ + "bCbbb", + "AcBc", + 8 + ], + [ + "bCbbba", + "AaBbc", + 9 + ], + [ + "bCbc", + "ABb", + 6 + ], + [ + "bCbc", + "CCb", + 4 + ], + [ + "bCbcB", + "AbAab", + 9 + ], + [ + "bCbcCBA", + "BCbBacC", + 9 + ], + [ + "bCbcCC", + "BbbABA", + 9 + ], + [ + "bCbcCbAC", + "ccccC", + 10 + ], + [ + "bCbca", + "bcbBCBa", + 6 + ], + [ + "bCbcaAAAA", + "A", + 16 + ], + [ + "bCbcaca", + "bBBAcc", + 8 + ], + [ + "bCbcbbBbC", + "CaC", + 14 + ], + [ + "bCc", + "BabBBCb", + 10 + ], + [ + "bCc", + "CCBcAA", + 8 + ], + [ + "bCc", + "CCCcCBcC", + 12 + ], + [ + "bCc", + "aaAABCCaB", + 14 + ], + [ + "bCc", + "aabbbaaB", + 14 + ], + [ + "bCc", + "bccB", + 3 + ], + [ + "bCcABC", + "aAcBcCC", + 10 + ], + [ + "bCcACCBa", + "cac", + 12 + ], + [ + "bCcACCaC", + "AbcaC", + 9 + ], + [ + "bCcB", + "BBB", + 5 + ], + [ + "bCcBA", + "CcAbcCbaA", + 11 + ], + [ + "bCcBB", + "cBb", + 5 + ], + [ + "bCcC", + "ACBaB", + 8 + ], + [ + "bCcCb", + "ccbBac", + 10 + ], + [ + "bCcCcCCCB", + "BCAaAa", + 15 + ], + [ + "bCca", + "CCaaaBbc", + 12 + ], + [ + "bCcaA", + "cACB", + 9 + ], + [ + "bCcaBbac", + "bBa", + 10 + ], + [ + "bCcaCc", + "A", + 11 + ], + [ + "bCcaCcC", + "bcCB", + 8 + ], + [ + "bCcaaa", + "B", + 11 + ], + [ + "bCcaabCB", + "bbAbcaAA", + 13 + ], + [ + "bCcacBCA", + "aACBccC", + 11 + ], + [ + "bCcbAaBBc", + "c", + 16 + ], + [ + "bCcbCCbb", + "bBbacaC", + 11 + ], + [ + "bCcbbaCab", + "BC", + 15 + ], + [ + "bCcbbc", + "bBBCB", + 9 + ], + [ + "bCcbc", + "acBCAcc", + 9 + ], + [ + "bCcc", + "CAbacbA", + 10 + ], + [ + "bCccbbbc", + "bBABAaBAc", + 13 + ], + [ + "ba", + "ABbbC", + 8 + ], + [ + "ba", + "BA", + 2 + ], + [ + "ba", + "BAABBb", + 10 + ], + [ + "ba", + "BacAAbA", + 11 + ], + [ + "ba", + "BbAaaCACc", + 14 + ], + [ + "ba", + "BcCAAC", + 10 + ], + [ + "ba", + "Bcc", + 5 + ], + [ + "ba", + "CCccCCa", + 12 + ], + [ + "ba", + "CaAbAb", + 9 + ], + [ + "ba", + "Cba", + 2 + ], + [ + "ba", + "Cca", + 4 + ], + [ + "ba", + "Ccac", + 6 + ], + [ + "ba", + "a", + 2 + ], + [ + "ba", + "aBABA", + 8 + ], + [ + "ba", + "aBaABB", + 9 + ], + [ + "ba", + "aCcaCbAbB", + 15 + ], + [ + "ba", + "bBC", + 4 + ], + [ + "ba", + "bBCC", + 6 + ], + [ + "ba", + "baABc", + 6 + ], + [ + "ba", + "baCAbB", + 8 + ], + [ + "ba", + "cA", + 3 + ], + [ + "ba", + "cABA", + 6 + ], + [ + "ba", + "cBbCCaB", + 10 + ], + [ + "ba", + "cBc", + 5 + ], + [ + "ba", + "cCCBcC", + 11 + ], + [ + "ba", + "caBBCAca", + 13 + ], + [ + "ba", + "cbBbb", + 8 + ], + [ + "ba", + "ccb", + 6 + ], + [ + "baA", + "AbAAaCaa", + 11 + ], + [ + "baA", + "B", + 5 + ], + [ + "baA", + "BBB", + 5 + ], + [ + "baA", + "a", + 4 + ], + [ + "baA", + "aCaaB", + 7 + ], + [ + "baA", + "cAaBCBaCB", + 15 + ], + [ + "baA", + "cBBcccca", + 14 + ], + [ + "baA", + "ccABbC", + 10 + ], + [ + "baAA", + "BBAaCacBC", + 14 + ], + [ + "baAA", + "cbbbCcbac", + 15 + ], + [ + "baAAA", + "CCcC", + 10 + ], + [ + "baAAABbC", + "BcACCbBa", + 11 + ], + [ + "baAAAbAC", + "caBb", + 12 + ], + [ + "baAAa", + "cACaAB", + 8 + ], + [ + "baAAc", + "bC", + 7 + ], + [ + "baAAcCa", + "CCABACcBC", + 12 + ], + [ + "baABBcc", + "cbcaaABc", + 9 + ], + [ + "baABCaCA", + "CABCbCAC", + 8 + ], + [ + "baABaBaAA", + "CAabb", + 13 + ], + [ + "baABbAcC", + "BcCBCCC", + 10 + ], + [ + "baAC", + "bCbAbBCA", + 10 + ], + [ + "baACCCcaA", + "Cb", + 16 + ], + [ + "baACca", + "bBcB", + 8 + ], + [ + "baAa", + "AbAab", + 6 + ], + [ + "baAaCAcc", + "acAbB", + 11 + ], + [ + "baAbCAc", + "baAacAac", + 5 + ], + [ + "baAbaAaba", + "cBA", + 15 + ], + [ + "baAbabAb", + "CC", + 16 + ], + [ + "baAcABAC", + "aabCBbBcB", + 13 + ], + [ + "baAcB", + "bbcb", + 5 + ], + [ + "baAcCcaaa", + "ccAA", + 12 + ], + [ + "baAca", + "aaAcCc", + 6 + ], + [ + "baAcaA", + "C", + 11 + ], + [ + "baAcab", + "B", + 11 + ], + [ + "baAcc", + "AbacACCCb", + 10 + ], + [ + "baB", + "BbccbbBbC", + 14 + ], + [ + "baB", + "acbAB", + 5 + ], + [ + "baB", + "accCa", + 10 + ], + [ + "baB", + "cA", + 5 + ], + [ + "baB", + "ca", + 4 + ], + [ + "baB", + "ccBACC", + 10 + ], + [ + "baBA", + "aa", + 5 + ], + [ + "baBAC", + "aA", + 6 + ], + [ + "baBAbccc", + "BCBaC", + 11 + ], + [ + "baBB", + "bCCaAca", + 10 + ], + [ + "baBB", + "cacbb", + 6 + ], + [ + "baBBC", + "BbBAA", + 7 + ], + [ + "baBBCAAc", + "bBaabAccc", + 11 + ], + [ + "baBBCcBCa", + "ABbBca", + 9 + ], + [ + "baBBaa", + "BCABAa", + 6 + ], + [ + "baBBbAb", + "bACa", + 10 + ], + [ + "baBBbCbBC", + "cBABabC", + 11 + ], + [ + "baBBcaBcC", + "CCCb", + 16 + ], + [ + "baBBcb", + "BBCbCCb", + 9 + ], + [ + "baBCcbbA", + "BBacCC", + 11 + ], + [ + "baBa", + "aA", + 5 + ], + [ + "baBa", + "bBbcAAb", + 10 + ], + [ + "baBa", + "cCbbbcBC", + 12 + ], + [ + "baBaABBb", + "bBcB", + 10 + ], + [ + "baBaaC", + "A", + 11 + ], + [ + "baBaaC", + "bBbAcc", + 7 + ], + [ + "baBbC", + "ACAc", + 8 + ], + [ + "baBbcCbaC", + "C", + 16 + ], + [ + "baBc", + "BAAbCa", + 8 + ], + [ + "baBcAAC", + "aBAAbACa", + 8 + ], + [ + "baBcC", + "Abc", + 6 + ], + [ + "baBcCCBBA", + "bbacBaaaC", + 14 + ], + [ + "baBcCCBb", + "Bccacb", + 9 + ], + [ + "baBcaAc", + "bc", + 10 + ], + [ + "baBcbccA", + "bCCa", + 11 + ], + [ + "baBccBC", + "C", + 12 + ], + [ + "baBccccb", + "AAbbaBCb", + 11 + ], + [ + "baC", + "AABBAb", + 10 + ], + [ + "baC", + "AB", + 5 + ], + [ + "baC", + "BBa", + 5 + ], + [ + "baC", + "CAaCCcbb", + 12 + ], + [ + "baC", + "CC", + 4 + ], + [ + "baC", + "caBcB", + 7 + ], + [ + "baCA", + "Bc", + 6 + ], + [ + "baCACBCac", + "a", + 16 + ], + [ + "baCAaAaBa", + "bcBcCab", + 12 + ], + [ + "baCAbc", + "CCc", + 8 + ], + [ + "baCB", + "B", + 6 + ], + [ + "baCBACbBa", + "b", + 16 + ], + [ + "baCBC", + "CAAcaCaB", + 12 + ], + [ + "baCBbCB", + "abc", + 9 + ], + [ + "baCCCCBB", + "b", + 14 + ], + [ + "baCCCcC", + "bbcAcABac", + 13 + ], + [ + "baCCbAB", + "CbBAC", + 9 + ], + [ + "baCCc", + "BcACCa", + 6 + ], + [ + "baCCc", + "ab", + 8 + ], + [ + "baCa", + "BAabaACcC", + 12 + ], + [ + "baCa", + "CCAABc", + 11 + ], + [ + "baCaAAB", + "aCB", + 8 + ], + [ + "baCaCa", + "caC", + 7 + ], + [ + "baCaCcbAb", + "bCB", + 13 + ], + [ + "baCb", + "CAbA", + 7 + ], + [ + "baCbCAB", + "aCBccCcC", + 11 + ], + [ + "baCbCC", + "ACCbA", + 8 + ], + [ + "baCbbCb", + "bACAc", + 8 + ], + [ + "baCbc", + "aaAbAb", + 8 + ], + [ + "baCbcACBc", + "CACcBb", + 11 + ], + [ + "baCc", + "BbcaaB", + 8 + ], + [ + "baCcA", + "bbbBcB", + 8 + ], + [ + "baCcaaC", + "cCCA", + 10 + ], + [ + "baCcb", + "BaCacAa", + 7 + ], + [ + "baa", + "ACbBca", + 8 + ], + [ + "baa", + "BaC", + 3 + ], + [ + "baaABBcBb", + "ABcacAC", + 14 + ], + [ + "baaAC", + "aCAb", + 6 + ], + [ + "baaAaBB", + "ccABBac", + 12 + ], + [ + "baaAb", + "bAcBabC", + 8 + ], + [ + "baaAcA", + "B", + 11 + ], + [ + "baaBAA", + "CbabaCCAA", + 8 + ], + [ + "baaBAAabB", + "cCB", + 16 + ], + [ + "baaBBBb", + "aC", + 12 + ], + [ + "baaBabb", + "cAaB", + 9 + ], + [ + "baaBb", + "CBbaBAbC", + 9 + ], + [ + "baaBcaa", + "CcCCbb", + 13 + ], + [ + "baaCCcAB", + "cCA", + 11 + ], + [ + "baaCaac", + "aCAaCCbA", + 11 + ], + [ + "baaCbA", + "ccBAAcC", + 12 + ], + [ + "baaa", + "CaCbCb", + 10 + ], + [ + "baaa", + "bBbBAAC", + 10 + ], + [ + "baaaAc", + "CBBbBBB", + 13 + ], + [ + "baaaCA", + "aab", + 8 + ], + [ + "baaaaabB", + "BCbbccA", + 15 + ], + [ + "baaacac", + "ABa", + 11 + ], + [ + "baab", + "aABbABC", + 11 + ], + [ + "baabA", + "cACC", + 9 + ], + [ + "baabaBaab", + "aCcA", + 15 + ], + [ + "baac", + "ACBbca", + 10 + ], + [ + "baacACa", + "bccCB", + 8 + ], + [ + "baacBbA", + "AaaCCbaaA", + 9 + ], + [ + "baacCc", + "CACBAA", + 11 + ], + [ + "baacaCcb", + "BBbbbAaBA", + 16 + ], + [ + "baacabBAA", + "aaaaBaB", + 9 + ], + [ + "bab", + "Cc", + 6 + ], + [ + "bab", + "CcBC", + 7 + ], + [ + "bab", + "a", + 4 + ], + [ + "bab", + "aabbAaBca", + 13 + ], + [ + "babA", + "AB", + 6 + ], + [ + "babA", + "bCb", + 4 + ], + [ + "babA", + "cccABCc", + 12 + ], + [ + "babAAaBC", + "Cb", + 14 + ], + [ + "babAaAA", + "bc", + 12 + ], + [ + "babAaBA", + "BbcBAbC", + 10 + ], + [ + "babBBB", + "b", + 10 + ], + [ + "babBa", + "BB", + 7 + ], + [ + "babBbAbAC", + "CbabB", + 12 + ], + [ + "babC", + "BCB", + 6 + ], + [ + "babC", + "CABB", + 6 + ], + [ + "babCABaa", + "BbcCAab", + 9 + ], + [ + "babCB", + "bcCCaABBC", + 12 + ], + [ + "babCBb", + "AAcbb", + 7 + ], + [ + "babCCCc", + "cCAbbca", + 12 + ], + [ + "babCCa", + "c", + 11 + ], + [ + "babCabCab", + "caac", + 13 + ], + [ + "babCbcc", + "AaAAcA", + 10 + ], + [ + "baba", + "abBcbAc", + 9 + ], + [ + "babaBA", + "CbcBbBabB", + 11 + ], + [ + "babaC", + "CbAaAA", + 8 + ], + [ + "babaCBaa", + "BbCa", + 9 + ], + [ + "babaa", + "C", + 10 + ], + [ + "babaaCBCC", + "BbaaAc", + 10 + ], + [ + "babb", + "AcB", + 6 + ], + [ + "babbA", + "BabaAAaB", + 9 + ], + [ + "babbCaaCB", + "cCaAAaBc", + 15 + ], + [ + "babbCbC", + "AA", + 13 + ], + [ + "babbCc", + "acbACaB", + 10 + ], + [ + "babbbbA", + "acA", + 10 + ], + [ + "babbcccAA", + "bAA", + 12 + ], + [ + "babcacA", + "c", + 12 + ], + [ + "bac", + "CBC", + 5 + ], + [ + "bac", + "CcBc", + 6 + ], + [ + "bac", + "Ccbca", + 8 + ], + [ + "bac", + "aacAc", + 6 + ], + [ + "bac", + "ba", + 2 + ], + [ + "bacABcBc", + "ABccc", + 8 + ], + [ + "bacAbca", + "aBCACc", + 9 + ], + [ + "bacBCbBb", + "ccAbcABA", + 12 + ], + [ + "bacBCc", + "cBcBB", + 8 + ], + [ + "bacBaBc", + "AcbcAcA", + 10 + ], + [ + "bacC", + "bbAbaCAB", + 11 + ], + [ + "bacCACCA", + "b", + 14 + ], + [ + "bacCAa", + "b", + 10 + ], + [ + "bacCCCccC", + "CCBbcacA", + 13 + ], + [ + "bacCbaBcB", + "Cab", + 13 + ], + [ + "bacCcBC", + "aBcbcccB", + 10 + ], + [ + "bacaCC", + "BAaAbcAC", + 10 + ], + [ + "bacaCcA", + "BcBAc", + 9 + ], + [ + "bacabaAA", + "AAACCBaC", + 13 + ], + [ + "bacacBa", + "BCAAaAA", + 11 + ], + [ + "bacbABaB", + "bCABBAc", + 10 + ], + [ + "bacbCAbbb", + "BaA", + 13 + ], + [ + "bacc", + "aAcAA", + 7 + ], + [ + "baccAA", + "C", + 11 + ], + [ + "baccAa", + "abCCB", + 9 + ], + [ + "baccBAA", + "ccCCbcCC", + 13 + ], + [ + "baccBBc", + "CAb", + 12 + ], + [ + "baccBbBaa", + "AbcaCcbb", + 12 + ], + [ + "baccCaCA", + "CAccb", + 11 + ], + [ + "baccaBc", + "AccABCbCC", + 11 + ], + [ + "baccccCB", + "cC", + 12 + ], + [ + "bb", + "A", + 4 + ], + [ + "bb", + "AABcCbCB", + 13 + ], + [ + "bb", + "AC", + 4 + ], + [ + "bb", + "Aabc", + 6 + ], + [ + "bb", + "AbC", + 4 + ], + [ + "bb", + "AbCC", + 6 + ], + [ + "bb", + "AbCCbbB", + 10 + ], + [ + "bb", + "Acaa", + 8 + ], + [ + "bb", + "B", + 3 + ], + [ + "bb", + "BAbcaB", + 9 + ], + [ + "bb", + "BCAC", + 7 + ], + [ + "bb", + "Bbccc", + 7 + ], + [ + "bb", + "CC", + 4 + ], + [ + "bb", + "a", + 4 + ], + [ + "bb", + "aBcCcc", + 11 + ], + [ + "bb", + "aaBBCc", + 10 + ], + [ + "bb", + "acCc", + 8 + ], + [ + "bb", + "accBa", + 9 + ], + [ + "bb", + "bA", + 2 + ], + [ + "bb", + "bAc", + 4 + ], + [ + "bb", + "bCbAcbaC", + 12 + ], + [ + "bb", + "baAB", + 5 + ], + [ + "bb", + "cAbBAbC", + 10 + ], + [ + "bb", + "cB", + 3 + ], + [ + "bb", + "cBC", + 5 + ], + [ + "bb", + "cbAacCaca", + 16 + ], + [ + "bb", + "cbBbABC", + 10 + ], + [ + "bbA", + "ABC", + 5 + ], + [ + "bbA", + "BBCabAA", + 9 + ], + [ + "bbA", + "BcCC", + 7 + ], + [ + "bbA", + "CB", + 5 + ], + [ + "bbA", + "bBbaababa", + 13 + ], + [ + "bbA", + "cCabBCAc", + 11 + ], + [ + "bbAA", + "B", + 7 + ], + [ + "bbAA", + "CC", + 8 + ], + [ + "bbAAaBb", + "AcC", + 12 + ], + [ + "bbAAb", + "BBBaaACAb", + 10 + ], + [ + "bbAAc", + "bBaCb", + 6 + ], + [ + "bbAAcAca", + "CCbBaccCB", + 12 + ], + [ + "bbAAcBca", + "bcA", + 11 + ], + [ + "bbAAcbCb", + "bAabCBAAC", + 12 + ], + [ + "bbAB", + "AaABCCB", + 10 + ], + [ + "bbAB", + "AbabABcC", + 8 + ], + [ + "bbAB", + "cBb", + 6 + ], + [ + "bbABACAbC", + "Ccabbc", + 13 + ], + [ + "bbABBC", + "CaAA", + 10 + ], + [ + "bbABCBA", + "ACb", + 9 + ], + [ + "bbABCBCbc", + "b", + 16 + ], + [ + "bbABab", + "CAcaba", + 8 + ], + [ + "bbABc", + "Bac", + 6 + ], + [ + "bbABcBBc", + "Aa", + 14 + ], + [ + "bbABcb", + "c", + 10 + ], + [ + "bbACABB", + "acC", + 12 + ], + [ + "bbACCcCbb", + "CCcC", + 10 + ], + [ + "bbAa", + "CbCaC", + 6 + ], + [ + "bbAa", + "cBcBBC", + 10 + ], + [ + "bbAaC", + "bBcCcAB", + 10 + ], + [ + "bbAaCaBAB", + "aabBc", + 13 + ], + [ + "bbAabBcca", + "BbaabB", + 8 + ], + [ + "bbAaccbAb", + "aCbBAAc", + 14 + ], + [ + "bbAb", + "B", + 7 + ], + [ + "bbAb", + "baaCABAa", + 11 + ], + [ + "bbAb", + "caAABCaac", + 15 + ], + [ + "bbAbAcaCA", + "cacab", + 13 + ], + [ + "bbAbB", + "ccAaCc", + 10 + ], + [ + "bbAbBc", + "AbcAbaBa", + 8 + ], + [ + "bbAbC", + "BBa", + 7 + ], + [ + "bbAbC", + "BcAcaCaaa", + 13 + ], + [ + "bbAbcBA", + "cAbbcBC", + 8 + ], + [ + "bbAc", + "bB", + 5 + ], + [ + "bbAcBabb", + "b", + 14 + ], + [ + "bbAcCaAC", + "CccacCABB", + 12 + ], + [ + "bbAcaA", + "caCabBaAB", + 13 + ], + [ + "bbAcabA", + "bBcbCb", + 9 + ], + [ + "bbB", + "Ac", + 6 + ], + [ + "bbB", + "aCbba", + 6 + ], + [ + "bbB", + "cAcbbBb", + 8 + ], + [ + "bbBA", + "B", + 6 + ], + [ + "bbBACc", + "AcCCBa", + 12 + ], + [ + "bbBAab", + "caAaBCbCb", + 14 + ], + [ + "bbBAbaa", + "aa", + 10 + ], + [ + "bbBBA", + "A", + 8 + ], + [ + "bbBBaaC", + "aCB", + 12 + ], + [ + "bbBBbc", + "ab", + 10 + ], + [ + "bbBCCCAbA", + "aAcCBBc", + 14 + ], + [ + "bbBCCaa", + "AACaAbAA", + 14 + ], + [ + "bbBCaaAC", + "bBAc", + 9 + ], + [ + "bbBCcbcbc", + "BBBaCcbBc", + 7 + ], + [ + "bbBa", + "BBcbc", + 7 + ], + [ + "bbBa", + "Cc", + 8 + ], + [ + "bbBaAcBaC", + "Bcc", + 13 + ], + [ + "bbBaCacbC", + "BAAaBcCa", + 13 + ], + [ + "bbBbA", + "Bbca", + 6 + ], + [ + "bbBbB", + "bBCaCbABa", + 11 + ], + [ + "bbBbBc", + "ABbBbc", + 5 + ], + [ + "bbBbCcBa", + "ba", + 12 + ], + [ + "bbBbaaaBB", + "C", + 18 + ], + [ + "bbBbabc", + "ABACBBacC", + 11 + ], + [ + "bbBbbcCA", + "AaabaCabb", + 15 + ], + [ + "bbBbc", + "Bab", + 7 + ], + [ + "bbBc", + "AaCcBCbCb", + 15 + ], + [ + "bbBcBCA", + "acaba", + 11 + ], + [ + "bbBcCc", + "BAc", + 8 + ], + [ + "bbBcab", + "cAabaC", + 10 + ], + [ + "bbBcbAba", + "b", + 14 + ], + [ + "bbBccBaCB", + "AcbAaCc", + 12 + ], + [ + "bbC", + "A", + 6 + ], + [ + "bbC", + "AAccbCBAA", + 14 + ], + [ + "bbC", + "BCAC", + 5 + ], + [ + "bbC", + "BacBCA", + 8 + ], + [ + "bbC", + "acBbCB", + 7 + ], + [ + "bbCAAb", + "aBAAcAc", + 9 + ], + [ + "bbCABbc", + "aCa", + 11 + ], + [ + "bbCACA", + "cBc", + 10 + ], + [ + "bbCAaBb", + "babcacCA", + 10 + ], + [ + "bbCAaa", + "cBaBbCa", + 11 + ], + [ + "bbCBCca", + "bC", + 10 + ], + [ + "bbCBacAB", + "aC", + 13 + ], + [ + "bbCBcBBcb", + "bCCCaa", + 13 + ], + [ + "bbCC", + "cAbBccA", + 9 + ], + [ + "bbCCAA", + "b", + 10 + ], + [ + "bbCCCBc", + "CCBAcbab", + 14 + ], + [ + "bbCCaccaA", + "ba", + 14 + ], + [ + "bbCCbBA", + "A", + 12 + ], + [ + "bbCCcABa", + "CBBbC", + 13 + ], + [ + "bbCCcCc", + "BbaabbaCc", + 11 + ], + [ + "bbCa", + "BC", + 5 + ], + [ + "bbCa", + "abBbA", + 6 + ], + [ + "bbCaAB", + "CCACcabAC", + 12 + ], + [ + "bbCaBabbC", + "CaBABbacC", + 10 + ], + [ + "bbCaaC", + "BbaBCcbCA", + 11 + ], + [ + "bbCaaCA", + "BcBBB", + 12 + ], + [ + "bbCabA", + "BaBBA", + 8 + ], + [ + "bbCabcAA", + "bc", + 12 + ], + [ + "bbCbAaBaA", + "CbAaAAc", + 9 + ], + [ + "bbCbCB", + "AaBB", + 9 + ], + [ + "bbCbaACb", + "Abbc", + 11 + ], + [ + "bbCbcaBA", + "AB", + 13 + ], + [ + "bbCc", + "aBaC", + 6 + ], + [ + "bbCcAA", + "ccaBAabBA", + 14 + ], + [ + "bbCcBaA", + "aAbCCabAb", + 11 + ], + [ + "bbCcaABc", + "a", + 14 + ], + [ + "bbCcc", + "BBAAA", + 8 + ], + [ + "bbCccBCCa", + "cacaCc", + 12 + ], + [ + "bba", + "ABBcccabc", + 14 + ], + [ + "bba", + "Bb", + 3 + ], + [ + "bba", + "C", + 6 + ], + [ + "bba", + "CBACA", + 8 + ], + [ + "bba", + "aCCAABCcb", + 17 + ], + [ + "bba", + "bbba", + 2 + ], + [ + "bbaA", + "CaCcCbb", + 14 + ], + [ + "bbaACCB", + "bAcAccABC", + 10 + ], + [ + "bbaAaaacc", + "CbaACB", + 11 + ], + [ + "bbaAcBA", + "cBbcaa", + 10 + ], + [ + "bbaB", + "Acc", + 8 + ], + [ + "bbaBAccC", + "cBBAb", + 11 + ], + [ + "bbaBBCc", + "C", + 12 + ], + [ + "bbaBCaAb", + "cCbA", + 12 + ], + [ + "bbaBaAc", + "cABCaA", + 9 + ], + [ + "bbaBbCbCb", + "aBccbBC", + 11 + ], + [ + "bbaBbaAbC", + "acaccca", + 15 + ], + [ + "bbaBc", + "bAbbCc", + 6 + ], + [ + "bbaCbCB", + "CCCA", + 10 + ], + [ + "bbaCbbC", + "c", + 13 + ], + [ + "bbaCc", + "CbCaBAaC", + 11 + ], + [ + "bbaCcBccb", + "C", + 16 + ], + [ + "bbab", + "ACB", + 7 + ], + [ + "bbab", + "CbbCbCbcC", + 12 + ], + [ + "bbabAbca", + "cBbbAB", + 10 + ], + [ + "bbabBcc", + "CAa", + 12 + ], + [ + "bbabC", + "abBAaAba", + 9 + ], + [ + "bbabC", + "cBb", + 7 + ], + [ + "bbabc", + "AAccbABAc", + 12 + ], + [ + "bbacC", + "aabaacA", + 8 + ], + [ + "bbacacb", + "acbCBaAcC", + 12 + ], + [ + "bbacccaa", + "AbBABACaa", + 9 + ], + [ + "bbb", + "ABC", + 5 + ], + [ + "bbb", + "C", + 6 + ], + [ + "bbb", + "aCBc", + 7 + ], + [ + "bbbA", + "bccbbcCCB", + 12 + ], + [ + "bbbABA", + "a", + 11 + ], + [ + "bbbACCCa", + "CBbcC", + 10 + ], + [ + "bbbAaBB", + "B", + 12 + ], + [ + "bbbAaCac", + "cCc", + 12 + ], + [ + "bbbAcbcC", + "aBBACacb", + 9 + ], + [ + "bbbB", + "ABAc", + 7 + ], + [ + "bbbB", + "AbABcABAB", + 12 + ], + [ + "bbbB", + "BB", + 5 + ], + [ + "bbbB", + "aC", + 8 + ], + [ + "bbbB", + "cCCcBcbCb", + 14 + ], + [ + "bbbBBBBCb", + "BcaBa", + 14 + ], + [ + "bbbBbA", + "bbA", + 6 + ], + [ + "bbbBbCBcB", + "bbcBcCC", + 9 + ], + [ + "bbbCa", + "CCB", + 8 + ], + [ + "bbbCccAA", + "bBAa", + 10 + ], + [ + "bbbaABaac", + "abaCbCB", + 13 + ], + [ + "bbbaAa", + "b", + 10 + ], + [ + "bbbaB", + "AC", + 9 + ], + [ + "bbbaa", + "CBBccC", + 10 + ], + [ + "bbbaaBba", + "Baa", + 11 + ], + [ + "bbbaacbb", + "cBbaC", + 10 + ], + [ + "bbbacBbBc", + "bcbcbCCC", + 10 + ], + [ + "bbbb", + "AbaACaC", + 12 + ], + [ + "bbbbbAB", + "aaAaCb", + 13 + ], + [ + "bbbbc", + "BBbC", + 5 + ], + [ + "bbbcAB", + "aBAbBBa", + 11 + ], + [ + "bbbcAcCAC", + "AaBbcCCcb", + 12 + ], + [ + "bbbcB", + "bcCc", + 6 + ], + [ + "bbbcBa", + "CAAccB", + 10 + ], + [ + "bbbcCB", + "aAbBABa", + 10 + ], + [ + "bbbca", + "cBbcabCC", + 9 + ], + [ + "bbbcbBAbc", + "AAacbcc", + 12 + ], + [ + "bbbcbcbc", + "Cbbaac", + 10 + ], + [ + "bbc", + "ACBaABaCC", + 15 + ], + [ + "bbc", + "BcCBaBcCc", + 14 + ], + [ + "bbc", + "CcB", + 6 + ], + [ + "bbc", + "bCCcBAac", + 11 + ], + [ + "bbc", + "c", + 4 + ], + [ + "bbcA", + "AAC", + 7 + ], + [ + "bbcACCca", + "bAABA", + 11 + ], + [ + "bbcAbBa", + "CaaA", + 11 + ], + [ + "bbcAbca", + "CCCCc", + 11 + ], + [ + "bbcAcbBab", + "CBacCca", + 12 + ], + [ + "bbcB", + "BaAbbccBb", + 10 + ], + [ + "bbcBAa", + "baaacc", + 10 + ], + [ + "bbcBCa", + "CaBabc", + 11 + ], + [ + "bbcCA", + "CAb", + 8 + ], + [ + "bbcCBACB", + "bcBBCaBc", + 10 + ], + [ + "bbcCBbAA", + "CcAc", + 12 + ], + [ + "bbcCC", + "BcbCc", + 6 + ], + [ + "bbcCCcB", + "b", + 12 + ], + [ + "bbca", + "AbbcaAcCa", + 10 + ], + [ + "bbca", + "acaBbbB", + 11 + ], + [ + "bbcaACCab", + "aACBab", + 8 + ], + [ + "bbcaAb", + "b", + 10 + ], + [ + "bbcaCab", + "B", + 13 + ], + [ + "bbcaaBCA", + "BCaaC", + 8 + ], + [ + "bbcaacCC", + "bCCcBACB", + 11 + ], + [ + "bbcab", + "bABaa", + 6 + ], + [ + "bbcabAB", + "BcC", + 11 + ], + [ + "bbcb", + "abAA", + 6 + ], + [ + "bbcbB", + "bCbAab", + 7 + ], + [ + "bbcbBccAa", + "bA", + 14 + ], + [ + "bbcbCcAB", + "cacaCCaBA", + 10 + ], + [ + "bbcc", + "B", + 7 + ], + [ + "bbcc", + "CcBBaABa", + 14 + ], + [ + "bbccAcBCB", + "CbacCAaB", + 11 + ], + [ + "bbccC", + "bac", + 6 + ], + [ + "bbccCa", + "baacc", + 7 + ], + [ + "bbccCcA", + "CbbbC", + 10 + ], + [ + "bbccaca", + "bb", + 10 + ], + [ + "bbccb", + "ABCA", + 8 + ], + [ + "bbccbB", + "Cbccbcb", + 5 + ], + [ + "bbccbBB", + "cbCB", + 8 + ], + [ + "bc", + "ACcCb", + 8 + ], + [ + "bc", + "Aa", + 4 + ], + [ + "bc", + "Ac", + 2 + ], + [ + "bc", + "BBAA", + 7 + ], + [ + "bc", + "BcA", + 3 + ], + [ + "bc", + "BcbBaCaCB", + 15 + ], + [ + "bc", + "CACcCBc", + 11 + ], + [ + "bc", + "CBBaAccB", + 13 + ], + [ + "bc", + "CaBa", + 7 + ], + [ + "bc", + "CcCaaABa", + 14 + ], + [ + "bc", + "a", + 4 + ], + [ + "bc", + "aAA", + 6 + ], + [ + "bc", + "aACBcB", + 9 + ], + [ + "bc", + "aBacc", + 7 + ], + [ + "bc", + "aBbAa", + 8 + ], + [ + "bc", + "acb", + 4 + ], + [ + "bc", + "b", + 2 + ], + [ + "bc", + "bA", + 2 + ], + [ + "bc", + "bBCcbA", + 8 + ], + [ + "bc", + "c", + 2 + ], + [ + "bc", + "cBb", + 5 + ], + [ + "bc", + "caaabc", + 8 + ], + [ + "bc", + "ccAB", + 6 + ], + [ + "bcA", + "ABbA", + 5 + ], + [ + "bcA", + "BBB", + 5 + ], + [ + "bcA", + "aabaAAB", + 10 + ], + [ + "bcA", + "acacbc", + 9 + ], + [ + "bcA", + "cbaACAB", + 9 + ], + [ + "bcAA", + "BaCc", + 7 + ], + [ + "bcAABAab", + "CB", + 13 + ], + [ + "bcAABbbBa", + "ABcccc", + 14 + ], + [ + "bcAABcb", + "c", + 12 + ], + [ + "bcAAbc", + "B", + 11 + ], + [ + "bcAB", + "a", + 7 + ], + [ + "bcAB", + "bcB", + 2 + ], + [ + "bcABAABcB", + "Cc", + 15 + ], + [ + "bcABAbBC", + "c", + 14 + ], + [ + "bcABAcB", + "bcCBCAbCa", + 9 + ], + [ + "bcAC", + "AAaC", + 5 + ], + [ + "bcACABab", + "c", + 14 + ], + [ + "bcACBbBaC", + "baaAbBbB", + 10 + ], + [ + "bcACa", + "bbaAcCBB", + 10 + ], + [ + "bcACbCa", + "aaaBBAbC", + 13 + ], + [ + "bcAa", + "BC", + 6 + ], + [ + "bcAa", + "b", + 6 + ], + [ + "bcAaAabA", + "abBB", + 13 + ], + [ + "bcAaBABA", + "CABac", + 10 + ], + [ + "bcAaBAaC", + "AaC", + 10 + ], + [ + "bcAaBCc", + "ca", + 10 + ], + [ + "bcAaaC", + "cbA", + 9 + ], + [ + "bcAabc", + "BAABb", + 7 + ], + [ + "bcAb", + "BcbbbA", + 7 + ], + [ + "bcAbAAAAb", + "abCBaAABc", + 11 + ], + [ + "bcAbACaB", + "cCaCAC", + 10 + ], + [ + "bcAbacAbc", + "BccaBBCbb", + 12 + ], + [ + "bcAc", + "BCBcaBCaB", + 13 + ], + [ + "bcAca", + "A", + 8 + ], + [ + "bcAcba", + "CcAc", + 6 + ], + [ + "bcAcbccB", + "Abbac", + 10 + ], + [ + "bcAccA", + "bbabbAcC", + 11 + ], + [ + "bcAccab", + "CCACcbcC", + 10 + ], + [ + "bcB", + "AaCbCccbb", + 13 + ], + [ + "bcB", + "BaAACbA", + 11 + ], + [ + "bcB", + "CAAABA", + 10 + ], + [ + "bcB", + "aaAaBccC", + 13 + ], + [ + "bcB", + "bAAAACCA", + 13 + ], + [ + "bcB", + "cAbcb", + 5 + ], + [ + "bcBA", + "bBBAb", + 4 + ], + [ + "bcBAAc", + "BccA", + 7 + ], + [ + "bcBACabcB", + "abBA", + 14 + ], + [ + "bcBAab", + "baaaBbcc", + 11 + ], + [ + "bcBAcAC", + "aCCC", + 10 + ], + [ + "bcBBC", + "c", + 8 + ], + [ + "bcBBCbAbA", + "bcCCb", + 10 + ], + [ + "bcBBaA", + "ABCBb", + 9 + ], + [ + "bcBBabABb", + "BcbaBBBCa", + 11 + ], + [ + "bcBBbB", + "bbbCC", + 8 + ], + [ + "bcBCAaaaB", + "CC", + 15 + ], + [ + "bcBCBC", + "aCB", + 8 + ], + [ + "bcBCCAB", + "ABCbc", + 10 + ], + [ + "bcBCCB", + "cAb", + 9 + ], + [ + "bcBCCcbA", + "c", + 14 + ], + [ + "bcBCa", + "CBccAbA", + 10 + ], + [ + "bcBa", + "abaBbcaba", + 11 + ], + [ + "bcBaBB", + "CB", + 9 + ], + [ + "bcBaCc", + "aaCBa", + 9 + ], + [ + "bcBaa", + "BcBB", + 5 + ], + [ + "bcBabcbc", + "accAbcB", + 8 + ], + [ + "bcBac", + "BB", + 7 + ], + [ + "bcBbAB", + "aCAbA", + 7 + ], + [ + "bcBbACCB", + "BAbcCc", + 10 + ], + [ + "bcBbACCac", + "CB", + 15 + ], + [ + "bcBbBCB", + "BcCbb", + 8 + ], + [ + "bcBbbAcac", + "ACbccac", + 9 + ], + [ + "bcBbc", + "caC", + 7 + ], + [ + "bcBc", + "A", + 8 + ], + [ + "bcBcaa", + "BbbCAaBB", + 10 + ], + [ + "bcBccBCBA", + "ABaCCAba", + 13 + ], + [ + "bcBccbcC", + "aC", + 14 + ], + [ + "bcC", + "ACabb", + 9 + ], + [ + "bcC", + "Ac", + 4 + ], + [ + "bcC", + "CACac", + 8 + ], + [ + "bcC", + "CAaCbCbb", + 13 + ], + [ + "bcC", + "CacBAbbac", + 15 + ], + [ + "bcC", + "a", + 6 + ], + [ + "bcC", + "abBBcB", + 8 + ], + [ + "bcC", + "acaAAaCbb", + 14 + ], + [ + "bcC", + "bBCaBBC", + 9 + ], + [ + "bcC", + "bbbCCABb", + 11 + ], + [ + "bcC", + "cBBBBCac", + 13 + ], + [ + "bcC", + "cCAAb", + 8 + ], + [ + "bcC", + "ccBcB", + 7 + ], + [ + "bcCAAA", + "CCCBbBAC", + 11 + ], + [ + "bcCAaA", + "AbCabC", + 9 + ], + [ + "bcCAb", + "CbBccBC", + 9 + ], + [ + "bcCAcAa", + "cB", + 12 + ], + [ + "bcCAcbC", + "AcAAbc", + 7 + ], + [ + "bcCB", + "a", + 8 + ], + [ + "bcCBAB", + "AaBAcB", + 8 + ], + [ + "bcCBBcb", + "CaCbaa", + 11 + ], + [ + "bcCBC", + "cb", + 7 + ], + [ + "bcCBaCAAc", + "aBb", + 16 + ], + [ + "bcCBaCb", + "aaaacC", + 11 + ], + [ + "bcCCAbB", + "BBcCAcA", + 8 + ], + [ + "bcCCa", + "BACbBaAcC", + 13 + ], + [ + "bcCCaa", + "cBcC", + 9 + ], + [ + "bcCCcbA", + "AAaAcCCA", + 12 + ], + [ + "bcCa", + "bCCC", + 3 + ], + [ + "bcCaaAB", + "cAAc", + 9 + ], + [ + "bcCaacC", + "BbcaccCCB", + 10 + ], + [ + "bcCab", + "BcCABbCAC", + 10 + ], + [ + "bcCacAA", + "CcbB", + 10 + ], + [ + "bcCacCaa", + "ABc", + 14 + ], + [ + "bcCacba", + "Bc", + 11 + ], + [ + "bcCb", + "aCbcBbCc", + 10 + ], + [ + "bcCbB", + "bAbCc", + 8 + ], + [ + "bcCbBCbaB", + "BC", + 14 + ], + [ + "bcCbaa", + "BbAACBAaC", + 10 + ], + [ + "bcCbbc", + "baAA", + 10 + ], + [ + "bcCc", + "AAA", + 8 + ], + [ + "bcCcBB", + "BaAccBC", + 8 + ], + [ + "bcCcaCCca", + "AaCc", + 12 + ], + [ + "bcCcbAAc", + "Ca", + 13 + ], + [ + "bca", + "AB", + 6 + ], + [ + "bca", + "AcaaBC", + 8 + ], + [ + "bca", + "CACBbcC", + 10 + ], + [ + "bca", + "bccb", + 4 + ], + [ + "bcaA", + "aBaCBBAa", + 12 + ], + [ + "bcaAAbCbC", + "CCcAA", + 13 + ], + [ + "bcaABAa", + "Ac", + 12 + ], + [ + "bcaACb", + "bAbCaCB", + 8 + ], + [ + "bcaAa", + "BbCcc", + 9 + ], + [ + "bcaAaaAC", + "acaaABc", + 8 + ], + [ + "bcaAba", + "BabBAaccb", + 14 + ], + [ + "bcaB", + "bCBCBAA", + 9 + ], + [ + "bcaBBAC", + "Cb", + 12 + ], + [ + "bcaCA", + "ACA", + 5 + ], + [ + "bcaCA", + "aB", + 8 + ], + [ + "bcaCAABcB", + "bBAbCcbc", + 12 + ], + [ + "bcaCB", + "AbaACbaC", + 10 + ], + [ + "bcaCBa", + "BaAbAB", + 9 + ], + [ + "bcaCa", + "cCbccbA", + 9 + ], + [ + "bcaCbBA", + "bcBabAb", + 8 + ], + [ + "bcaCbCbaA", + "bbCBb", + 11 + ], + [ + "bcaCcB", + "cAab", + 8 + ], + [ + "bcaa", + "CC", + 7 + ], + [ + "bcaa", + "aaBA", + 7 + ], + [ + "bcaaA", + "BAAacaAba", + 11 + ], + [ + "bcaaABBA", + "baa", + 10 + ], + [ + "bcaaAcbC", + "aBACAcaab", + 13 + ], + [ + "bcaaBaAb", + "b", + 14 + ], + [ + "bcaacb", + "acCb", + 7 + ], + [ + "bcab", + "a", + 6 + ], + [ + "bcab", + "cCAabCBCb", + 13 + ], + [ + "bcabAA", + "BAAcca", + 9 + ], + [ + "bcabC", + "Aac", + 7 + ], + [ + "bcabb", + "aAaA", + 8 + ], + [ + "bcac", + "CCAAbCbC", + 12 + ], + [ + "bcb", + "A", + 6 + ], + [ + "bcb", + "aBabCcB", + 9 + ], + [ + "bcb", + "abACACc", + 11 + ], + [ + "bcbA", + "AcbAcbCaB", + 11 + ], + [ + "bcbAAA", + "a", + 11 + ], + [ + "bcbAAAB", + "a", + 13 + ], + [ + "bcbACB", + "CBbcbCca", + 9 + ], + [ + "bcbACCA", + "AabaACAcB", + 11 + ], + [ + "bcbACCC", + "c", + 12 + ], + [ + "bcbAbCbB", + "caA", + 12 + ], + [ + "bcbAc", + "BACCccbCb", + 13 + ], + [ + "bcbBA", + "baaaa", + 7 + ], + [ + "bcbBAABCa", + "cBAcaa", + 10 + ], + [ + "bcbCAaAC", + "bB", + 13 + ], + [ + "bcbCAc", + "caccbaABC", + 11 + ], + [ + "bcbCB", + "c", + 8 + ], + [ + "bcbCcCa", + "CCCbACb", + 11 + ], + [ + "bcbaAACb", + "aBAcA", + 11 + ], + [ + "bcbaBBB", + "AA", + 13 + ], + [ + "bcbaccb", + "ACACCB", + 9 + ], + [ + "bcbb", + "cBcACA", + 9 + ], + [ + "bcbb", + "cCCBaaA", + 12 + ], + [ + "bcbbABC", + "CAAaa", + 11 + ], + [ + "bcbbB", + "ABB", + 7 + ], + [ + "bcbbBc", + "cCc", + 8 + ], + [ + "bcbbCBabA", + "cCccbccBA", + 12 + ], + [ + "bcbc", + "AAA", + 8 + ], + [ + "bcbc", + "Ca", + 7 + ], + [ + "bcbcAC", + "bBAaaacb", + 12 + ], + [ + "bcc", + "C", + 5 + ], + [ + "bcc", + "CaACa", + 9 + ], + [ + "bcc", + "bACb", + 5 + ], + [ + "bcc", + "bbcCcAa", + 8 + ], + [ + "bccA", + "C", + 7 + ], + [ + "bccA", + "aAb", + 8 + ], + [ + "bccA", + "ab", + 8 + ], + [ + "bccABAba", + "acacC", + 13 + ], + [ + "bccAC", + "BbbBcA", + 8 + ], + [ + "bccAbaA", + "aAc", + 12 + ], + [ + "bccAbcbcb", + "CbbcbbA", + 11 + ], + [ + "bccB", + "BcacaCB", + 7 + ], + [ + "bccB", + "cAcb", + 5 + ], + [ + "bccBABc", + "bAacbb", + 10 + ], + [ + "bccBBB", + "aAbbbbC", + 11 + ], + [ + "bccBBCbBa", + "bbbaabCbB", + 11 + ], + [ + "bccBBc", + "cCcbac", + 6 + ], + [ + "bccBBccA", + "bB", + 12 + ], + [ + "bccBCCAC", + "bABBCAabC", + 9 + ], + [ + "bccBCCBc", + "ACBaa", + 13 + ], + [ + "bccBa", + "CAcbB", + 7 + ], + [ + "bccBbA", + "aBCcBBBC", + 9 + ], + [ + "bccCA", + "BaBAAba", + 11 + ], + [ + "bccCAbbB", + "ccC", + 10 + ], + [ + "bccCBab", + "c", + 12 + ], + [ + "bccCCAaBB", + "bBa", + 14 + ], + [ + "bccCaBCC", + "Bb", + 14 + ], + [ + "bcca", + "AaAaabA", + 12 + ], + [ + "bccaCBb", + "bAbcabA", + 9 + ], + [ + "bccaCCC", + "CCBCBbAAA", + 16 + ], + [ + "bccaa", + "C", + 9 + ], + [ + "bccaab", + "cAcc", + 9 + ], + [ + "bccbAbab", + "b", + 14 + ], + [ + "bccbBCcCA", + "B", + 16 + ], + [ + "bccbBb", + "ccba", + 6 + ], + [ + "bccbC", + "bBC", + 5 + ], + [ + "bccbbCC", + "bBccA", + 9 + ], + [ + "bccbbbCB", + "BbcABC", + 10 + ], + [ + "bccbccAc", + "bBC", + 12 + ], + [ + "bccc", + "ACcca", + 5 + ], + [ + "bcccAA", + "bCBAbca", + 10 + ], + [ + "bcccAaBca", + "BaC", + 14 + ], + [ + "bcccBBCaA", + "bcAcaCcb", + 10 + ], + [ + "bcccC", + "aAAaa", + 10 + ], + [ + "bcccCCCbc", + "Bac", + 15 + ], + [ + "c", + "A", + 2 + ], + [ + "c", + "AA", + 4 + ], + [ + "c", + "AABacBC", + 12 + ], + [ + "c", + "AAbBBaBB", + 16 + ], + [ + "c", + "AB", + 4 + ], + [ + "c", + "ABA", + 6 + ], + [ + "c", + "ABBaCCBBB", + 17 + ], + [ + "c", + "ABBbBBCBA", + 17 + ], + [ + "c", + "ABbCb", + 9 + ], + [ + "c", + "ABbaaba", + 14 + ], + [ + "c", + "AC", + 3 + ], + [ + "c", + "ACABa", + 9 + ], + [ + "c", + "ACCB", + 7 + ], + [ + "c", + "ACCcbCCbC", + 16 + ], + [ + "c", + "ACbBBC", + 11 + ], + [ + "c", + "Aa", + 4 + ], + [ + "c", + "AaABA", + 10 + ], + [ + "c", + "Aaa", + 6 + ], + [ + "c", + "AaaaBbBC", + 15 + ], + [ + "c", + "AaabCAb", + 13 + ], + [ + "c", + "Ab", + 4 + ], + [ + "c", + "AbaCAAA", + 13 + ], + [ + "c", + "AcCACaAA", + 14 + ], + [ + "c", + "AcCCBAcaB", + 16 + ], + [ + "c", + "Acb", + 4 + ], + [ + "c", + "AcccbCacB", + 16 + ], + [ + "c", + "B", + 2 + ], + [ + "c", + "BA", + 4 + ], + [ + "c", + "BAAAAbB", + 14 + ], + [ + "c", + "BACA", + 7 + ], + [ + "c", + "BACbAC", + 11 + ], + [ + "c", + "BAbBbAc", + 12 + ], + [ + "c", + "BAbb", + 8 + ], + [ + "c", + "BAcBb", + 8 + ], + [ + "c", + "BB", + 4 + ], + [ + "c", + "BBAACAb", + 13 + ], + [ + "c", + "BBABBC", + 11 + ], + [ + "c", + "BBBcBccc", + 14 + ], + [ + "c", + "BBbbc", + 8 + ], + [ + "c", + "BBbcbbac", + 14 + ], + [ + "c", + "BBcBc", + 8 + ], + [ + "c", + "BC", + 3 + ], + [ + "c", + "BCA", + 5 + ], + [ + "c", + "BCAAcBC", + 12 + ], + [ + "c", + "BCAbCb", + 11 + ], + [ + "c", + "BCBc", + 6 + ], + [ + "c", + "BCa", + 5 + ], + [ + "c", + "BCcbaB", + 10 + ], + [ + "c", + "Ba", + 4 + ], + [ + "c", + "BaAaaA", + 12 + ], + [ + "c", + "BaBacca", + 12 + ], + [ + "c", + "BaCCBaCAC", + 17 + ], + [ + "c", + "BaaBcAcBb", + 16 + ], + [ + "c", + "BaaccC", + 10 + ], + [ + "c", + "BabCcAAcA", + 16 + ], + [ + "c", + "BbABABaBC", + 17 + ], + [ + "c", + "BbCa", + 7 + ], + [ + "c", + "BbaAacbb", + 14 + ], + [ + "c", + "BbaCAAcBa", + 16 + ], + [ + "c", + "BbcBb", + 8 + ], + [ + "c", + "Bbcc", + 6 + ], + [ + "c", + "Bc", + 2 + ], + [ + "c", + "BcBa", + 6 + ], + [ + "c", + "Bcb", + 4 + ], + [ + "c", + "Bcccb", + 8 + ], + [ + "c", + "C", + 1 + ], + [ + "c", + "CB", + 3 + ], + [ + "c", + "CBABBC", + 11 + ], + [ + "c", + "CBAcCA", + 10 + ], + [ + "c", + "CBBAbbA", + 13 + ], + [ + "c", + "CBCBb", + 9 + ], + [ + "c", + "CBCaBa", + 11 + ], + [ + "c", + "CBb", + 5 + ], + [ + "c", + "CCCBaaCB", + 15 + ], + [ + "c", + "CCCCCC", + 11 + ], + [ + "c", + "Ca", + 3 + ], + [ + "c", + "CaAb", + 7 + ], + [ + "c", + "CaCa", + 7 + ], + [ + "c", + "Cab", + 5 + ], + [ + "c", + "CabC", + 7 + ], + [ + "c", + "CbAC", + 7 + ], + [ + "c", + "CbabCAaAc", + 16 + ], + [ + "c", + "CcAABA", + 10 + ], + [ + "c", + "CccaaAa", + 12 + ], + [ + "c", + "a", + 2 + ], + [ + "c", + "aAAa", + 8 + ], + [ + "c", + "aAAc", + 6 + ], + [ + "c", + "aACbcB", + 10 + ], + [ + "c", + "aAbACcCA", + 14 + ], + [ + "c", + "aAbab", + 10 + ], + [ + "c", + "aB", + 4 + ], + [ + "c", + "aBAC", + 7 + ], + [ + "c", + "aBBbbc", + 10 + ], + [ + "c", + "aBaaA", + 10 + ], + [ + "c", + "aBb", + 6 + ], + [ + "c", + "aBcABcCCb", + 16 + ], + [ + "c", + "aBcBBCBbc", + 16 + ], + [ + "c", + "aCa", + 5 + ], + [ + "c", + "aa", + 4 + ], + [ + "c", + "aaCBCbC", + 13 + ], + [ + "c", + "aabcBAAcc", + 16 + ], + [ + "c", + "aabcBcA", + 12 + ], + [ + "c", + "aacABB", + 10 + ], + [ + "c", + "aacb", + 6 + ], + [ + "c", + "aaccAca", + 12 + ], + [ + "c", + "ab", + 4 + ], + [ + "c", + "abAa", + 8 + ], + [ + "c", + "abCAcC", + 10 + ], + [ + "c", + "abCCAB", + 11 + ], + [ + "c", + "abCc", + 6 + ], + [ + "c", + "ac", + 2 + ], + [ + "c", + "accbaBBAB", + 16 + ], + [ + "c", + "b", + 2 + ], + [ + "c", + "bAAccBAa", + 14 + ], + [ + "c", + "bAaBAbcCc", + 16 + ], + [ + "c", + "bAcacAbb", + 14 + ], + [ + "c", + "bBAaCa", + 11 + ], + [ + "c", + "bBAcaCaca", + 16 + ], + [ + "c", + "bBBCbaB", + 13 + ], + [ + "c", + "bBBa", + 8 + ], + [ + "c", + "bBab", + 8 + ], + [ + "c", + "bBcCbcABC", + 16 + ], + [ + "c", + "bBcCca", + 10 + ], + [ + "c", + "bCBC", + 7 + ], + [ + "c", + "bCC", + 5 + ], + [ + "c", + "bCCAbcBBa", + 16 + ], + [ + "c", + "bCCa", + 7 + ], + [ + "c", + "bCabaBC", + 13 + ], + [ + "c", + "bCc", + 4 + ], + [ + "c", + "bCcA", + 6 + ], + [ + "c", + "baaAB", + 10 + ], + [ + "c", + "baaABBC", + 13 + ], + [ + "c", + "baaAccba", + 14 + ], + [ + "c", + "bbBbbBBa", + 16 + ], + [ + "c", + "bbCccAAC", + 14 + ], + [ + "c", + "bc", + 2 + ], + [ + "c", + "bcAbC", + 8 + ], + [ + "c", + "bcBAcAcB", + 14 + ], + [ + "c", + "bcbBbCcb", + 14 + ], + [ + "c", + "bccAAa", + 10 + ], + [ + "c", + "c", + 0 + ], + [ + "c", + "cAABBb", + 10 + ], + [ + "c", + "cAACa", + 8 + ], + [ + "c", + "cABc", + 6 + ], + [ + "c", + "cAaCBc", + 10 + ], + [ + "c", + "cAacbCCA", + 14 + ], + [ + "c", + "cBCCAbA", + 12 + ], + [ + "c", + "cBacAc", + 10 + ], + [ + "c", + "cBcCBb", + 10 + ], + [ + "c", + "cCAB", + 6 + ], + [ + "c", + "cCCbbBBcA", + 16 + ], + [ + "c", + "cCcaBbcba", + 16 + ], + [ + "c", + "ca", + 2 + ], + [ + "c", + "caC", + 4 + ], + [ + "c", + "caa", + 4 + ], + [ + "c", + "cabA", + 6 + ], + [ + "c", + "cb", + 2 + ], + [ + "c", + "cbCaBBab", + 14 + ], + [ + "c", + "cbCbAa", + 10 + ], + [ + "c", + "cbbBbCcC", + 14 + ], + [ + "c", + "cbcaAAC", + 12 + ], + [ + "c", + "cc", + 2 + ], + [ + "c", + "ccB", + 4 + ], + [ + "c", + "ccCBaBAC", + 14 + ], + [ + "c", + "ccc", + 4 + ], + [ + "cA", + "A", + 2 + ], + [ + "cA", + "AAbaCB", + 10 + ], + [ + "cA", + "AC", + 4 + ], + [ + "cA", + "Ac", + 4 + ], + [ + "cA", + "Acc", + 4 + ], + [ + "cA", + "BABCB", + 8 + ], + [ + "cA", + "BBc", + 6 + ], + [ + "cA", + "BaaBCbA", + 11 + ], + [ + "cA", + "Bbb", + 6 + ], + [ + "cA", + "BbbAAccB", + 14 + ], + [ + "cA", + "Bc", + 4 + ], + [ + "cA", + "C", + 3 + ], + [ + "cA", + "CAaCAABb", + 13 + ], + [ + "cA", + "CAaacAcc", + 12 + ], + [ + "cA", + "CbaAccc", + 11 + ], + [ + "cA", + "Cbbb", + 7 + ], + [ + "cA", + "CcC", + 4 + ], + [ + "cA", + "aB", + 4 + ], + [ + "cA", + "aC", + 4 + ], + [ + "cA", + "ab", + 4 + ], + [ + "cA", + "abAAcc", + 10 + ], + [ + "cA", + "abC", + 6 + ], + [ + "cA", + "bBCAbCa", + 11 + ], + [ + "cA", + "bCbbBCAC", + 13 + ], + [ + "cA", + "bCcCBcCA", + 12 + ], + [ + "cA", + "bcaCcbC", + 11 + ], + [ + "cA", + "cABaBCb", + 10 + ], + [ + "cA", + "cBaAB", + 6 + ], + [ + "cA", + "cCBababA", + 12 + ], + [ + "cA", + "ccCBbBab", + 13 + ], + [ + "cA", + "cca", + 3 + ], + [ + "cAA", + "AaaCaBcCb", + 16 + ], + [ + "cAA", + "AbBC", + 8 + ], + [ + "cAA", + "a", + 5 + ], + [ + "cAA", + "cb", + 4 + ], + [ + "cAAA", + "cBaC", + 5 + ], + [ + "cAAAA", + "bcA", + 8 + ], + [ + "cAAAC", + "aBAaCcCca", + 13 + ], + [ + "cAAAacbCB", + "BBaACbA", + 12 + ], + [ + "cAAAbabc", + "BCAacBCBc", + 10 + ], + [ + "cAAAbbc", + "AAbb", + 6 + ], + [ + "cAAAcB", + "bbAB", + 8 + ], + [ + "cAAB", + "ABCaCbCbC", + 15 + ], + [ + "cAAB", + "Aab", + 4 + ], + [ + "cAABaBAa", + "bAaBaBA", + 5 + ], + [ + "cAAC", + "CAB", + 5 + ], + [ + "cAACA", + "CaABCCbAb", + 10 + ], + [ + "cAACAC", + "BC", + 10 + ], + [ + "cAACaCc", + "AcabA", + 9 + ], + [ + "cAACb", + "BCAcca", + 8 + ], + [ + "cAACbABCc", + "CCbAB", + 9 + ], + [ + "cAACbb", + "aAaC", + 7 + ], + [ + "cAACcAA", + "bAaCcacb", + 8 + ], + [ + "cAAa", + "caABcbBAB", + 12 + ], + [ + "cAAaB", + "BBaAb", + 7 + ], + [ + "cAAaBAAA", + "aCb", + 14 + ], + [ + "cAAaCbCc", + "CC", + 12 + ], + [ + "cAAaba", + "bcb", + 10 + ], + [ + "cAAabcCCA", + "aAa", + 13 + ], + [ + "cAAbB", + "AAcaBaa", + 10 + ], + [ + "cAAbBA", + "caCcABAB", + 9 + ], + [ + "cAAbBBaCa", + "CabcB", + 12 + ], + [ + "cAAbC", + "BAbbb", + 6 + ], + [ + "cAAba", + "CCBAabaAB", + 10 + ], + [ + "cAAc", + "bCcCCB", + 10 + ], + [ + "cAAcABCcb", + "BCBB", + 13 + ], + [ + "cAAcB", + "ca", + 7 + ], + [ + "cAAcCCAc", + "Cb", + 14 + ], + [ + "cAAcaabB", + "A", + 14 + ], + [ + "cAAccaaB", + "BBbbAcBc", + 15 + ], + [ + "cAB", + "B", + 4 + ], + [ + "cAB", + "BCAAacb", + 10 + ], + [ + "cAB", + "CaCbCca", + 11 + ], + [ + "cAB", + "CbacbCcbB", + 14 + ], + [ + "cAB", + "b", + 5 + ], + [ + "cAB", + "baabbCA", + 12 + ], + [ + "cAB", + "cbbaBbCb", + 11 + ], + [ + "cAB", + "cccBBCBC", + 12 + ], + [ + "cAB", + "cccCCCCb", + 13 + ], + [ + "cABA", + "CA", + 5 + ], + [ + "cABAABc", + "acabCCb", + 11 + ], + [ + "cABABAACA", + "cBaBCcaa", + 10 + ], + [ + "cABAa", + "c", + 8 + ], + [ + "cABAcaBb", + "BCBaBCBc", + 11 + ], + [ + "cABBAaCac", + "aAacACCcc", + 10 + ], + [ + "cABBCaBbc", + "bBAaBcACC", + 13 + ], + [ + "cABBc", + "B", + 8 + ], + [ + "cABBcacb", + "bBcCcCAA", + 13 + ], + [ + "cABC", + "B", + 6 + ], + [ + "cABCAB", + "bAcb", + 8 + ], + [ + "cABCAc", + "cBCBa", + 6 + ], + [ + "cABCAca", + "ABaCCCbcc", + 12 + ], + [ + "cABCBbbCA", + "ABBccc", + 11 + ], + [ + "cABCa", + "CaA", + 7 + ], + [ + "cABaA", + "aAAbaBb", + 9 + ], + [ + "cABaAaA", + "BAB", + 10 + ], + [ + "cABaBAC", + "acCa", + 12 + ], + [ + "cABaCAbbA", + "A", + 16 + ], + [ + "cABac", + "BaCc", + 6 + ], + [ + "cABbACA", + "BbccCBCBc", + 15 + ], + [ + "cABba", + "AAC", + 8 + ], + [ + "cABc", + "AaccB", + 7 + ], + [ + "cABcABc", + "cAcBBca", + 6 + ], + [ + "cABcBcb", + "cbBCa", + 8 + ], + [ + "cABcb", + "Cac", + 6 + ], + [ + "cABcba", + "BAab", + 8 + ], + [ + "cAC", + "BCaCAbaCa", + 13 + ], + [ + "cAC", + "C", + 4 + ], + [ + "cAC", + "CABbA", + 7 + ], + [ + "cAC", + "CAcABbA", + 10 + ], + [ + "cAC", + "bAa", + 4 + ], + [ + "cAC", + "cBBac", + 6 + ], + [ + "cACAACCbc", + "CAcABb", + 10 + ], + [ + "cACABcACC", + "CAc", + 12 + ], + [ + "cACACbC", + "BbCcAa", + 11 + ], + [ + "cACAabC", + "BbcbBcAAa", + 14 + ], + [ + "cACBCaC", + "C", + 12 + ], + [ + "cACBCcCBb", + "bBaaBaCbC", + 14 + ], + [ + "cACBaaB", + "bccBcBaB", + 9 + ], + [ + "cACCBBcb", + "Ba", + 14 + ], + [ + "cACCaa", + "aBcCaab", + 7 + ], + [ + "cACCc", + "ccAa", + 7 + ], + [ + "cACa", + "aBBcB", + 9 + ], + [ + "cACa", + "cAbCCA", + 5 + ], + [ + "cACaB", + "cAbbaBB", + 6 + ], + [ + "cACac", + "cBCBbbbB", + 12 + ], + [ + "cACbCBB", + "bBbAC", + 12 + ], + [ + "cACbaCc", + "AacCACBaB", + 11 + ], + [ + "cACbaba", + "acAACBcCC", + 11 + ], + [ + "cACbbC", + "Cba", + 8 + ], + [ + "cACbbb", + "ccacBccbA", + 11 + ], + [ + "cACbcb", + "b", + 10 + ], + [ + "cACc", + "Cc", + 4 + ], + [ + "cACc", + "bAba", + 6 + ], + [ + "cACcA", + "Caccc", + 5 + ], + [ + "cACcAcc", + "bbB", + 14 + ], + [ + "cACca", + "acabb", + 9 + ], + [ + "cACcaA", + "bB", + 12 + ], + [ + "cACccAacA", + "bcCBabCc", + 14 + ], + [ + "cAa", + "BacbCAbcA", + 13 + ], + [ + "cAa", + "Bbcb", + 8 + ], + [ + "cAa", + "C", + 5 + ], + [ + "cAa", + "aCBcCAcc", + 12 + ], + [ + "cAa", + "aCacc", + 8 + ], + [ + "cAa", + "caBcA", + 6 + ], + [ + "cAa", + "cacAbaAb", + 10 + ], + [ + "cAa", + "ccccAcaBC", + 12 + ], + [ + "cAaA", + "CbbaAaba", + 10 + ], + [ + "cAaAA", + "CaCbcbab", + 13 + ], + [ + "cAaAA", + "acAc", + 7 + ], + [ + "cAaAABAcb", + "bAba", + 14 + ], + [ + "cAaACBaa", + "cbBabcaac", + 11 + ], + [ + "cAaACc", + "ccbBB", + 10 + ], + [ + "cAaAaaB", + "CBCbBccBa", + 15 + ], + [ + "cAaAc", + "ABCACcb", + 10 + ], + [ + "cAaB", + "C", + 7 + ], + [ + "cAaB", + "aBaBA", + 6 + ], + [ + "cAaBCBbAA", + "aAc", + 14 + ], + [ + "cAaBaA", + "a", + 10 + ], + [ + "cAaC", + "BcbAbbCb", + 10 + ], + [ + "cAaC", + "CbB", + 7 + ], + [ + "cAaCAc", + "ac", + 8 + ], + [ + "cAaCaAc", + "bBBBbaC", + 12 + ], + [ + "cAaCaC", + "ABBa", + 8 + ], + [ + "cAaCc", + "c", + 8 + ], + [ + "cAaaAAA", + "BBCAa", + 11 + ], + [ + "cAaaaBCA", + "AcABc", + 10 + ], + [ + "cAaaacAB", + "aAbCB", + 10 + ], + [ + "cAaaacaa", + "ABB", + 14 + ], + [ + "cAaacB", + "AaCBbACB", + 10 + ], + [ + "cAaaca", + "bAbbc", + 8 + ], + [ + "cAabACa", + "bcaabaaAb", + 9 + ], + [ + "cAabCA", + "CbACAAcb", + 11 + ], + [ + "cAaba", + "a", + 8 + ], + [ + "cAabcacaa", + "aA", + 15 + ], + [ + "cAacAaBBC", + "bBaBAbcaC", + 12 + ], + [ + "cAaca", + "AcacAA", + 7 + ], + [ + "cAaca", + "BcCcBBBA", + 13 + ], + [ + "cAacc", + "BBCaCA", + 9 + ], + [ + "cAacc", + "a", + 8 + ], + [ + "cAacccAb", + "ccbbBACA", + 13 + ], + [ + "cAb", + "B", + 5 + ], + [ + "cAb", + "BAbABAC", + 10 + ], + [ + "cAb", + "BBAAc", + 8 + ], + [ + "cAb", + "CBABCa", + 8 + ], + [ + "cAb", + "CbBBaA", + 10 + ], + [ + "cAb", + "caBACCa", + 10 + ], + [ + "cAbA", + "Cacc", + 6 + ], + [ + "cAbA", + "aAAACA", + 8 + ], + [ + "cAbABbab", + "aCA", + 13 + ], + [ + "cAbAC", + "BAAc", + 5 + ], + [ + "cAbACB", + "bAcCC", + 8 + ], + [ + "cAbACcCaa", + "cBa", + 13 + ], + [ + "cAbAb", + "acBC", + 9 + ], + [ + "cAbAbA", + "bACaC", + 9 + ], + [ + "cAbAbAAAa", + "A", + 16 + ], + [ + "cAbBB", + "AbaaC", + 8 + ], + [ + "cAbBaAAAA", + "BabaaBAa", + 9 + ], + [ + "cAbBaB", + "AcB", + 8 + ], + [ + "cAbaBbBC", + "a", + 14 + ], + [ + "cAbab", + "Ba", + 7 + ], + [ + "cAbabb", + "bC", + 10 + ], + [ + "cAbbB", + "aBBcaAA", + 13 + ], + [ + "cAbbBCAcB", + "aabbaC", + 11 + ], + [ + "cAbbBaca", + "CbCAcBaC", + 12 + ], + [ + "cAbbBbcC", + "CB", + 13 + ], + [ + "cAbbb", + "cCbaBAAAc", + 13 + ], + [ + "cAbcAcBA", + "AcA", + 10 + ], + [ + "cAbcaCccC", + "ABbAbbcB", + 12 + ], + [ + "cAbcbB", + "AAABaac", + 11 + ], + [ + "cAbccBCBc", + "AAACAcbAc", + 12 + ], + [ + "cAc", + "AbBCcaBB", + 13 + ], + [ + "cAc", + "BCCcCaBac", + 13 + ], + [ + "cAc", + "CB", + 5 + ], + [ + "cAc", + "bBb", + 6 + ], + [ + "cAc", + "bCaCaCb", + 11 + ], + [ + "cAc", + "bcb", + 6 + ], + [ + "cAc", + "cAAcaa", + 6 + ], + [ + "cAcA", + "CaaB", + 6 + ], + [ + "cAcAA", + "cbAcBbB", + 8 + ], + [ + "cAcABAAc", + "aABaBBc", + 9 + ], + [ + "cAcACcBb", + "cacbCccC", + 7 + ], + [ + "cAcAb", + "aBbAA", + 8 + ], + [ + "cAcAcAcCc", + "caaAAcbc", + 7 + ], + [ + "cAcAcCB", + "ccbcbBbC", + 10 + ], + [ + "cAcBBaBCb", + "C", + 16 + ], + [ + "cAcBCb", + "abCbAAB", + 11 + ], + [ + "cAcBCcBba", + "A", + 16 + ], + [ + "cAcBbA", + "aBabBACbB", + 13 + ], + [ + "cAcBbB", + "ABBcacBBA", + 10 + ], + [ + "cAcCAB", + "CBBbc", + 11 + ], + [ + "cAcCBCa", + "CCA", + 9 + ], + [ + "cAcCBcBBa", + "CacACBBBB", + 8 + ], + [ + "cAcCCBCBA", + "ccCcbcbc", + 8 + ], + [ + "cAcCbCcAB", + "caC", + 13 + ], + [ + "cAcCcbaaA", + "CcBaa", + 9 + ], + [ + "cAca", + "BAcbCbC", + 10 + ], + [ + "cAcaAa", + "caAAaab", + 7 + ], + [ + "cAcaBAAC", + "BBB", + 14 + ], + [ + "cAcaaa", + "BaBaBC", + 9 + ], + [ + "cAcaaca", + "aaCBA", + 10 + ], + [ + "cAcabCbCB", + "BacBcbABb", + 11 + ], + [ + "cAcabaB", + "Cc", + 11 + ], + [ + "cAcac", + "acbA", + 7 + ], + [ + "cAcbABCBB", + "aABc", + 12 + ], + [ + "cAcbBCBac", + "bCbAAacB", + 13 + ], + [ + "cAcbbccAc", + "AABAaBbCb", + 16 + ], + [ + "cAcc", + "aCa", + 6 + ], + [ + "cAcc", + "bCAAcaCC", + 10 + ], + [ + "cAcc", + "cc", + 4 + ], + [ + "cAccCB", + "aAABca", + 9 + ], + [ + "cAccaBCAb", + "BBcBCCAbb", + 12 + ], + [ + "cAccaaABa", + "bCAcB", + 13 + ], + [ + "cAcccAA", + "bCAb", + 11 + ], + [ + "cAcccaCaA", + "abaB", + 14 + ], + [ + "cAcccacab", + "BbCCbaB", + 13 + ], + [ + "cB", + "AbAB", + 6 + ], + [ + "cB", + "AbBbCA", + 10 + ], + [ + "cB", + "B", + 2 + ], + [ + "cB", + "BACbBc", + 9 + ], + [ + "cB", + "BAcB", + 4 + ], + [ + "cB", + "BBaBaaccC", + 16 + ], + [ + "cB", + "BcAaA", + 8 + ], + [ + "cB", + "C", + 3 + ], + [ + "cB", + "CAA", + 5 + ], + [ + "cB", + "CAAA", + 7 + ], + [ + "cB", + "CAacabCCC", + 15 + ], + [ + "cB", + "CBBB", + 5 + ], + [ + "cB", + "CCBCABaaA", + 15 + ], + [ + "cB", + "Cc", + 3 + ], + [ + "cB", + "CcBAc", + 6 + ], + [ + "cB", + "a", + 4 + ], + [ + "cB", + "aACCba", + 10 + ], + [ + "cB", + "aacbcC", + 9 + ], + [ + "cB", + "acaaAABB", + 12 + ], + [ + "cB", + "b", + 3 + ], + [ + "cB", + "ba", + 4 + ], + [ + "cB", + "baacB", + 6 + ], + [ + "cB", + "bc", + 4 + ], + [ + "cB", + "cABAbCBc", + 12 + ], + [ + "cB", + "cCAB", + 4 + ], + [ + "cB", + "caAAbbBC", + 12 + ], + [ + "cB", + "caBAAa", + 8 + ], + [ + "cBA", + "Ac", + 6 + ], + [ + "cBA", + "CB", + 3 + ], + [ + "cBA", + "a", + 5 + ], + [ + "cBA", + "ac", + 6 + ], + [ + "cBA", + "b", + 5 + ], + [ + "cBA", + "bbaBac", + 9 + ], + [ + "cBA", + "cAaabbab", + 12 + ], + [ + "cBAA", + "AAB", + 6 + ], + [ + "cBAA", + "aCabbA", + 8 + ], + [ + "cBAAB", + "CCa", + 8 + ], + [ + "cBAABa", + "bA", + 9 + ], + [ + "cBAABccc", + "caBcbAAa", + 13 + ], + [ + "cBAAbCcC", + "Bcc", + 11 + ], + [ + "cBAAcAbAC", + "bBaacb", + 10 + ], + [ + "cBAB", + "cBBcaCCb", + 10 + ], + [ + "cBABaBCc", + "BAABc", + 7 + ], + [ + "cBABaac", + "baCbbb", + 12 + ], + [ + "cBABbBAaA", + "c", + 16 + ], + [ + "cBABba", + "AaAccABac", + 12 + ], + [ + "cBABca", + "aBcB", + 7 + ], + [ + "cBACBAAcA", + "BBCbCCBb", + 13 + ], + [ + "cBACa", + "cbaabBBcA", + 12 + ], + [ + "cBACc", + "cBbBaCb", + 7 + ], + [ + "cBACcBBC", + "AbaaBbcC", + 11 + ], + [ + "cBACcbCaa", + "ab", + 15 + ], + [ + "cBAa", + "cCCBaAc", + 8 + ], + [ + "cBAaBcaB", + "b", + 15 + ], + [ + "cBAacbBa", + "aaBb", + 11 + ], + [ + "cBAbCaBA", + "ccaB", + 9 + ], + [ + "cBAba", + "B", + 8 + ], + [ + "cBAbaa", + "CaccCBA", + 12 + ], + [ + "cBAbb", + "ca", + 7 + ], + [ + "cBAbbCaCc", + "aaCA", + 13 + ], + [ + "cBAbca", + "aaACbA", + 9 + ], + [ + "cBAc", + "BA", + 4 + ], + [ + "cBAc", + "aCCcbbbcC", + 13 + ], + [ + "cBAcAb", + "BCabCbcc", + 13 + ], + [ + "cBAcAbaa", + "AAC", + 12 + ], + [ + "cBAcBBc", + "cCcaaa", + 10 + ], + [ + "cBAcCCb", + "cCcAc", + 9 + ], + [ + "cBAcb", + "CaAAACba", + 10 + ], + [ + "cBB", + "BacacBcCA", + 14 + ], + [ + "cBB", + "Bc", + 4 + ], + [ + "cBB", + "bAACA", + 10 + ], + [ + "cBB", + "bCbBBC", + 7 + ], + [ + "cBBA", + "AcAAcABa", + 11 + ], + [ + "cBBA", + "Cca", + 6 + ], + [ + "cBBABC", + "bCcbaAB", + 9 + ], + [ + "cBBABC", + "cCCcBcBc", + 9 + ], + [ + "cBBAac", + "CbbbcB", + 9 + ], + [ + "cBBBABBA", + "AAAbAAAB", + 13 + ], + [ + "cBBBCA", + "caacccAbA", + 13 + ], + [ + "cBBBCacAB", + "Accab", + 13 + ], + [ + "cBBBcbA", + "cB", + 10 + ], + [ + "cBBC", + "acaC", + 6 + ], + [ + "cBBCBaCbA", + "CBcBaAc", + 10 + ], + [ + "cBBCCAca", + "ABCcCa", + 8 + ], + [ + "cBBCb", + "cBBbaaC", + 8 + ], + [ + "cBBCbB", + "aCbBaCaB", + 8 + ], + [ + "cBBaBaAac", + "C", + 17 + ], + [ + "cBBaaCbBb", + "baCCBa", + 11 + ], + [ + "cBBabaAc", + "b", + 14 + ], + [ + "cBBac", + "ba", + 7 + ], + [ + "cBBacC", + "aBcCAC", + 8 + ], + [ + "cBBbAaabA", + "aaA", + 12 + ], + [ + "cBBbB", + "BCcAACb", + 11 + ], + [ + "cBBbBaBcA", + "bbCCCc", + 13 + ], + [ + "cBBbCbc", + "ACccCBaCB", + 13 + ], + [ + "cBBbbaBcb", + "Cc", + 15 + ], + [ + "cBBbbbb", + "acBbaBca", + 10 + ], + [ + "cBBbbcAC", + "bCaBbbcbB", + 9 + ], + [ + "cBBbbcacb", + "BcCcaaBA", + 13 + ], + [ + "cBBbca", + "BbA", + 7 + ], + [ + "cBBcAccA", + "AcbbAC", + 11 + ], + [ + "cBBcBb", + "BaCb", + 7 + ], + [ + "cBBcaBcB", + "Aa", + 14 + ], + [ + "cBBcbC", + "bcCBacb", + 8 + ], + [ + "cBC", + "cACacBc", + 9 + ], + [ + "cBC", + "cCa", + 4 + ], + [ + "cBCA", + "C", + 6 + ], + [ + "cBCACaCaC", + "ccBBBCaac", + 9 + ], + [ + "cBCAaac", + "cB", + 10 + ], + [ + "cBCAacA", + "aaAcA", + 8 + ], + [ + "cBCAcCccC", + "cbB", + 15 + ], + [ + "cBCAcbC", + "CcAbCbbaA", + 13 + ], + [ + "cBCB", + "bbabCAAbB", + 13 + ], + [ + "cBCBCbCa", + "bcCAaCCa", + 10 + ], + [ + "cBCBacA", + "AcbBaA", + 7 + ], + [ + "cBCBbCBa", + "c", + 14 + ], + [ + "cBCCABcC", + "Baa", + 13 + ], + [ + "cBCCB", + "a", + 10 + ], + [ + "cBCCBcaBa", + "AcAAba", + 13 + ], + [ + "cBCCC", + "A", + 10 + ], + [ + "cBCCcBc", + "ccaBCB", + 9 + ], + [ + "cBCaB", + "BBbAA", + 7 + ], + [ + "cBCab", + "aaABbC", + 10 + ], + [ + "cBCacA", + "abcbAbAa", + 11 + ], + [ + "cBCb", + "aCbCc", + 6 + ], + [ + "cBCb", + "b", + 6 + ], + [ + "cBCb", + "cAA", + 6 + ], + [ + "cBCbCAc", + "ACacAB", + 9 + ], + [ + "cBCbCca", + "aaB", + 13 + ], + [ + "cBCbabA", + "bbBbABB", + 9 + ], + [ + "cBCbbBBbc", + "aCaBBbbb", + 10 + ], + [ + "cBCbcCC", + "ACBbBBc", + 10 + ], + [ + "cBCc", + "AcaCBCb", + 8 + ], + [ + "cBCcAbcac", + "aBcac", + 10 + ], + [ + "cBCcbccaa", + "bBbCaBB", + 13 + ], + [ + "cBa", + "b", + 5 + ], + [ + "cBa", + "bAAaA", + 8 + ], + [ + "cBaAA", + "baaCbca", + 11 + ], + [ + "cBaAAB", + "BcbB", + 8 + ], + [ + "cBaAACacA", + "bBa", + 14 + ], + [ + "cBaABacb", + "ACb", + 11 + ], + [ + "cBaB", + "BabB", + 4 + ], + [ + "cBaB", + "CBBBC", + 5 + ], + [ + "cBaBBAB", + "B", + 12 + ], + [ + "cBaBBCc", + "BacCACa", + 10 + ], + [ + "cBaBaABBB", + "bcc", + 17 + ], + [ + "cBaBbCBC", + "cAAB", + 11 + ], + [ + "cBaBc", + "bbC", + 7 + ], + [ + "cBaC", + "BacB", + 5 + ], + [ + "cBaC", + "bA", + 6 + ], + [ + "cBaCA", + "CAcabbb", + 11 + ], + [ + "cBaCAbCC", + "B", + 14 + ], + [ + "cBaCAccbc", + "bbccAcCBB", + 10 + ], + [ + "cBaCCC", + "ab", + 10 + ], + [ + "cBaCaBC", + "Cb", + 11 + ], + [ + "cBaCab", + "BC", + 8 + ], + [ + "cBaa", + "BAab", + 5 + ], + [ + "cBaaA", + "Bb", + 8 + ], + [ + "cBaaCabBb", + "aCACcCbbb", + 11 + ], + [ + "cBaaaBBac", + "bcaAbBcC", + 10 + ], + [ + "cBaab", + "acAacAbCc", + 11 + ], + [ + "cBab", + "ACCAB", + 7 + ], + [ + "cBabB", + "C", + 9 + ], + [ + "cBabBCaca", + "C", + 16 + ], + [ + "cBabBcab", + "CbcAAAa", + 12 + ], + [ + "cBabCaA", + "aaAbAaac", + 10 + ], + [ + "cBabbBCBb", + "C", + 16 + ], + [ + "cBac", + "A", + 7 + ], + [ + "cBacCAB", + "B", + 12 + ], + [ + "cBacabC", + "aAAaB", + 10 + ], + [ + "cBacac", + "c", + 10 + ], + [ + "cBb", + "BbbA", + 5 + ], + [ + "cBb", + "CAbcC", + 7 + ], + [ + "cBb", + "acAB", + 5 + ], + [ + "cBbACBB", + "bCCCAbbC", + 12 + ], + [ + "cBbACC", + "ACa", + 8 + ], + [ + "cBbAcabB", + "aA", + 14 + ], + [ + "cBbC", + "abAabbbc", + 12 + ], + [ + "cBbCAACc", + "bBB", + 13 + ], + [ + "cBbCAB", + "cbCb", + 5 + ], + [ + "cBbCAa", + "cacaBBAb", + 11 + ], + [ + "cBbCAaA", + "AB", + 12 + ], + [ + "cBbCAb", + "bC", + 8 + ], + [ + "cBbCBAAA", + "AbBbc", + 12 + ], + [ + "cBbCCC", + "BCba", + 8 + ], + [ + "cBbCc", + "ccaCBA", + 8 + ], + [ + "cBbCccaC", + "bA", + 13 + ], + [ + "cBba", + "cb", + 4 + ], + [ + "cBbaACaa", + "cC", + 12 + ], + [ + "cBbaCbc", + "ccaaca", + 9 + ], + [ + "cBbaaac", + "b", + 12 + ], + [ + "cBbaab", + "BbbBcC", + 9 + ], + [ + "cBbaccBA", + "c", + 14 + ], + [ + "cBbb", + "CCcCaa", + 10 + ], + [ + "cBbb", + "abCb", + 5 + ], + [ + "cBbbA", + "A", + 8 + ], + [ + "cBbbA", + "B", + 8 + ], + [ + "cBbbBAAb", + "AAaa", + 13 + ], + [ + "cBbbBBacb", + "cCaCCcAac", + 14 + ], + [ + "cBbbBCaa", + "AaBc", + 13 + ], + [ + "cBbbCa", + "CB", + 9 + ], + [ + "cBbbcBB", + "bAACBBb", + 10 + ], + [ + "cBbcBaCBC", + "abA", + 15 + ], + [ + "cBbcCAC", + "b", + 12 + ], + [ + "cBbcCb", + "caBBbb", + 7 + ], + [ + "cBbcaBAAc", + "bB", + 14 + ], + [ + "cBbcbAAb", + "aba", + 13 + ], + [ + "cBbcbcBB", + "AccaA", + 12 + ], + [ + "cBbccCcc", + "aaCBabcBB", + 15 + ], + [ + "cBbccb", + "caaABcBb", + 9 + ], + [ + "cBc", + "BCaAAAbB", + 14 + ], + [ + "cBc", + "CAAC", + 6 + ], + [ + "cBc", + "aAc", + 4 + ], + [ + "cBc", + "aBaAAc", + 8 + ], + [ + "cBc", + "aabbAc", + 9 + ], + [ + "cBc", + "aac", + 4 + ], + [ + "cBc", + "c", + 4 + ], + [ + "cBcA", + "bC", + 6 + ], + [ + "cBcAAbbcb", + "BAbb", + 10 + ], + [ + "cBcACAA", + "a", + 13 + ], + [ + "cBcACb", + "Bc", + 8 + ], + [ + "cBcAaACbc", + "abbB", + 14 + ], + [ + "cBcBaaC", + "AbC", + 11 + ], + [ + "cBcBbCC", + "aBbCaba", + 11 + ], + [ + "cBcBbCbbA", + "cBa", + 13 + ], + [ + "cBcBbb", + "Acb", + 8 + ], + [ + "cBcCaaAcb", + "baccACCCc", + 13 + ], + [ + "cBcCbcCa", + "aCa", + 12 + ], + [ + "cBcCccaAa", + "BCaCCabbA", + 12 + ], + [ + "cBcaAaC", + "bACBCAB", + 12 + ], + [ + "cBcaAbBCb", + "baCbbaa", + 12 + ], + [ + "cBcaAbc", + "aa", + 11 + ], + [ + "cBcaAcaaA", + "CACbcC", + 14 + ], + [ + "cBcaBcBc", + "aBbBCACc", + 11 + ], + [ + "cBcaCcCA", + "Bcc", + 10 + ], + [ + "cBcabC", + "bAAAabb", + 10 + ], + [ + "cBcbACbBB", + "ACBc", + 12 + ], + [ + "cBcbBAbAC", + "A", + 16 + ], + [ + "cBcbC", + "AAbBcCa", + 10 + ], + [ + "cBcbcaCb", + "cCbcCB", + 6 + ], + [ + "cBcc", + "bAcAcaaba", + 14 + ], + [ + "cBcc", + "cacc", + 2 + ], + [ + "cBccaAac", + "aab", + 12 + ], + [ + "cBccaBCB", + "Caa", + 13 + ], + [ + "cBccacbCA", + "bBCBAca", + 11 + ], + [ + "cBccbc", + "CAbA", + 9 + ], + [ + "cBcccCAC", + "BA", + 12 + ], + [ + "cC", + "A", + 4 + ], + [ + "cC", + "ABCCCa", + 9 + ], + [ + "cC", + "AbAacCAc", + 12 + ], + [ + "cC", + "AccAAABc", + 13 + ], + [ + "cC", + "BBAabcC", + 10 + ], + [ + "cC", + "BBc", + 5 + ], + [ + "cC", + "BBcaACaA", + 12 + ], + [ + "cC", + "BCABBCAaC", + 15 + ], + [ + "cC", + "BaAbcaBc", + 13 + ], + [ + "cC", + "BaBaccb", + 11 + ], + [ + "cC", + "CBA", + 5 + ], + [ + "cC", + "CacCac", + 8 + ], + [ + "cC", + "CcACcacbB", + 14 + ], + [ + "cC", + "CcBbb", + 8 + ], + [ + "cC", + "aACbbCAB", + 13 + ], + [ + "cC", + "aBABCBbc", + 14 + ], + [ + "cC", + "aCCcAAca", + 13 + ], + [ + "cC", + "aaBaBbAA", + 16 + ], + [ + "cC", + "acAb", + 6 + ], + [ + "cC", + "b", + 4 + ], + [ + "cC", + "bA", + 4 + ], + [ + "cC", + "bACBaA", + 10 + ], + [ + "cC", + "bacAcbc", + 11 + ], + [ + "cC", + "bcaCBaB", + 10 + ], + [ + "cC", + "c", + 2 + ], + [ + "cC", + "cA", + 2 + ], + [ + "cC", + "cABAc", + 7 + ], + [ + "cC", + "cAbc", + 5 + ], + [ + "cC", + "cBA", + 4 + ], + [ + "cC", + "cBCAccaCb", + 14 + ], + [ + "cC", + "cabcBCB", + 10 + ], + [ + "cC", + "cccaBa", + 9 + ], + [ + "cCA", + "c", + 4 + ], + [ + "cCAABb", + "ACbaBC", + 7 + ], + [ + "cCAACb", + "bbC", + 10 + ], + [ + "cCAAacBb", + "a", + 14 + ], + [ + "cCAAc", + "ABbCcbBb", + 14 + ], + [ + "cCAAc", + "AC", + 7 + ], + [ + "cCAB", + "AAaCAABab", + 12 + ], + [ + "cCABbAb", + "Ab", + 10 + ], + [ + "cCABbaBaA", + "aacC", + 15 + ], + [ + "cCAC", + "bCbbbbAC", + 10 + ], + [ + "cCACAb", + "AbC", + 10 + ], + [ + "cCACCc", + "cccbAabc", + 9 + ], + [ + "cCACc", + "BbAcaB", + 9 + ], + [ + "cCAa", + "CBA", + 5 + ], + [ + "cCAaabBbA", + "bCCCcacb", + 13 + ], + [ + "cCAac", + "BBA", + 8 + ], + [ + "cCAacBCc", + "CCBBaA", + 11 + ], + [ + "cCAacCCbA", + "bCACCa", + 9 + ], + [ + "cCAbA", + "aBBcaccbA", + 11 + ], + [ + "cCAbABC", + "bbACCaACB", + 13 + ], + [ + "cCAbACcc", + "AbBAccbb", + 11 + ], + [ + "cCAbCacAA", + "BcABBAC", + 12 + ], + [ + "cCAbbaA", + "CcACabA", + 8 + ], + [ + "cCAbcA", + "BABABC", + 10 + ], + [ + "cCAcAcbaB", + "BccBaCC", + 13 + ], + [ + "cCAcCAB", + "bcccCbbab", + 11 + ], + [ + "cCAcaabBA", + "bBcbCCBa", + 13 + ], + [ + "cCAcbBBa", + "AaBca", + 10 + ], + [ + "cCAcbcB", + "c", + 12 + ], + [ + "cCB", + "BBCAbba", + 11 + ], + [ + "cCB", + "BCcab", + 7 + ], + [ + "cCB", + "CC", + 3 + ], + [ + "cCB", + "CbBa", + 5 + ], + [ + "cCB", + "a", + 6 + ], + [ + "cCB", + "aC", + 4 + ], + [ + "cCBA", + "abcA", + 6 + ], + [ + "cCBACb", + "abBccaC", + 11 + ], + [ + "cCBAbCCa", + "a", + 14 + ], + [ + "cCBAbaC", + "aAbaAa", + 10 + ], + [ + "cCBAcCB", + "CbaCB", + 6 + ], + [ + "cCBBAb", + "cBcaC", + 7 + ], + [ + "cCBBBc", + "Cc", + 8 + ], + [ + "cCBBaa", + "CBC", + 8 + ], + [ + "cCBCBAc", + "ABCBCBbCB", + 9 + ], + [ + "cCBCa", + "AcacaBCAc", + 10 + ], + [ + "cCBCbB", + "Cc", + 9 + ], + [ + "cCBCcbB", + "cCBaAaBc", + 8 + ], + [ + "cCBaAac", + "C", + 12 + ], + [ + "cCBaCc", + "CcbBBbB", + 10 + ], + [ + "cCBac", + "bb", + 9 + ], + [ + "cCBbAC", + "abAbC", + 8 + ], + [ + "cCBbAaabb", + "BCc", + 16 + ], + [ + "cCBbAcCAC", + "acBCCbA", + 12 + ], + [ + "cCBbAccB", + "abA", + 12 + ], + [ + "cCBbbB", + "bbaaCCBbB", + 11 + ], + [ + "cCBc", + "ABbacCc", + 10 + ], + [ + "cCBc", + "cAcBb", + 5 + ], + [ + "cCBcABc", + "ac", + 11 + ], + [ + "cCBcACBa", + "cACcB", + 10 + ], + [ + "cCBcACCa", + "BACAAaa", + 11 + ], + [ + "cCBca", + "cbCacC", + 6 + ], + [ + "cCBcb", + "BCbAABAba", + 12 + ], + [ + "cCBcbCaCb", + "cbBCa", + 10 + ], + [ + "cCC", + "BbacaAb", + 12 + ], + [ + "cCC", + "CB", + 4 + ], + [ + "cCC", + "accaCCCcb", + 12 + ], + [ + "cCC", + "bAbcCcbcb", + 13 + ], + [ + "cCC", + "bB", + 6 + ], + [ + "cCC", + "bCBbABBCc", + 14 + ], + [ + "cCC", + "baAAb", + 10 + ], + [ + "cCC", + "cABBc", + 7 + ], + [ + "cCC", + "cCCbaac", + 8 + ], + [ + "cCCAbbbaa", + "BccccA", + 15 + ], + [ + "cCCAc", + "ccAbbb", + 9 + ], + [ + "cCCBBaBC", + "bcaA", + 13 + ], + [ + "cCCBCAaA", + "baB", + 13 + ], + [ + "cCCBc", + "bAccaAcB", + 11 + ], + [ + "cCCCC", + "cBC", + 6 + ], + [ + "cCCCaA", + "cCbB", + 8 + ], + [ + "cCCCaac", + "BCAcCb", + 11 + ], + [ + "cCCCbA", + "CcBBBBb", + 11 + ], + [ + "cCCCbb", + "ABccCcA", + 10 + ], + [ + "cCCCbbbAa", + "cA", + 14 + ], + [ + "cCCa", + "AccBc", + 7 + ], + [ + "cCCaA", + "BcACCA", + 6 + ], + [ + "cCCaAAAC", + "aBbcC", + 12 + ], + [ + "cCCaCB", + "CaaAcbCAB", + 12 + ], + [ + "cCCaccA", + "bc", + 12 + ], + [ + "cCCaccAC", + "abAACcCa", + 12 + ], + [ + "cCCb", + "C", + 6 + ], + [ + "cCCb", + "cAcBa", + 6 + ], + [ + "cCCbBCBab", + "CCcaca", + 11 + ], + [ + "cCCbb", + "BCBbbcA", + 8 + ], + [ + "cCCbc", + "bCCC", + 5 + ], + [ + "cCCbcbabA", + "aaCCc", + 14 + ], + [ + "cCCcABBB", + "aBBbABcb", + 11 + ], + [ + "cCCcCCcAa", + "bBCab", + 15 + ], + [ + "cCCcaaC", + "aBC", + 10 + ], + [ + "cCa", + "BacAaCAcA", + 13 + ], + [ + "cCa", + "C", + 4 + ], + [ + "cCa", + "CAACAAaC", + 11 + ], + [ + "cCa", + "aBBcbc", + 10 + ], + [ + "cCa", + "aCBab", + 6 + ], + [ + "cCa", + "aaCCAAAb", + 12 + ], + [ + "cCa", + "bBAcCcB", + 10 + ], + [ + "cCa", + "cBBcaCB", + 9 + ], + [ + "cCaA", + "CCaca", + 4 + ], + [ + "cCaA", + "cBcccC", + 9 + ], + [ + "cCaAAACA", + "cBBbacc", + 12 + ], + [ + "cCaAAacCc", + "cbCaBcA", + 12 + ], + [ + "cCaABCAAA", + "A", + 16 + ], + [ + "cCaAb", + "aBaAB", + 5 + ], + [ + "cCaAbAABA", + "CCabcAAbb", + 8 + ], + [ + "cCaAc", + "ABAbbBBB", + 15 + ], + [ + "cCaAcACaa", + "caaBcBc", + 11 + ], + [ + "cCaAcCBca", + "ABcc", + 12 + ], + [ + "cCaAcbA", + "aBCBcCCba", + 12 + ], + [ + "cCaB", + "ABABAbBBc", + 15 + ], + [ + "cCaBBaAb", + "aA", + 12 + ], + [ + "cCaBBaCAC", + "bC", + 15 + ], + [ + "cCaBBba", + "aa", + 10 + ], + [ + "cCaBCbb", + "BcaCB", + 8 + ], + [ + "cCaBb", + "Ac", + 9 + ], + [ + "cCaC", + "AC", + 5 + ], + [ + "cCaCA", + "BBBbbCAC", + 12 + ], + [ + "cCaCBBBCb", + "B", + 16 + ], + [ + "cCaCCB", + "BcBac", + 9 + ], + [ + "cCaCaB", + "AbcA", + 10 + ], + [ + "cCaCaaaA", + "abcCcc", + 14 + ], + [ + "cCaCc", + "aA", + 8 + ], + [ + "cCaa", + "CcCbcbA", + 9 + ], + [ + "cCaaBCb", + "A", + 13 + ], + [ + "cCaab", + "a", + 8 + ], + [ + "cCabB", + "AbbA", + 8 + ], + [ + "cCabB", + "AcaaACCb", + 11 + ], + [ + "cCabC", + "aBcB", + 8 + ], + [ + "cCabaBC", + "aB", + 10 + ], + [ + "cCabc", + "cabcccAB", + 10 + ], + [ + "cCabcBa", + "abccAaAB", + 12 + ], + [ + "cCac", + "AacBAaA", + 10 + ], + [ + "cCacacaa", + "CACbAbcC", + 13 + ], + [ + "cCacba", + "aAbAbBAc", + 13 + ], + [ + "cCb", + "AccbcbcB", + 11 + ], + [ + "cCb", + "BaC", + 6 + ], + [ + "cCb", + "BabCcAA", + 12 + ], + [ + "cCb", + "aCaBbBbC", + 12 + ], + [ + "cCb", + "accCcBAbA", + 12 + ], + [ + "cCb", + "cC", + 2 + ], + [ + "cCbAB", + "bcaac", + 8 + ], + [ + "cCbAB", + "cca", + 6 + ], + [ + "cCbAc", + "baB", + 7 + ], + [ + "cCbAcaB", + "AaBB", + 10 + ], + [ + "cCbBAc", + "bbBBBCa", + 10 + ], + [ + "cCbBBAbC", + "BCa", + 13 + ], + [ + "cCbBa", + "AAcCCaAB", + 11 + ], + [ + "cCbBacc", + "aBAbCAc", + 11 + ], + [ + "cCbBb", + "aacaBa", + 9 + ], + [ + "cCbBb", + "baAAacaCA", + 17 + ], + [ + "cCbBbc", + "acaAbC", + 8 + ], + [ + "cCbC", + "B", + 7 + ], + [ + "cCbC", + "CcBaA", + 7 + ], + [ + "cCbC", + "aC", + 6 + ], + [ + "cCbCCbCaA", + "acBBbCcC", + 12 + ], + [ + "cCbCaCBa", + "aBBAAbaA", + 13 + ], + [ + "cCbCac", + "CBAB", + 8 + ], + [ + "cCbCcaCc", + "cAbbAab", + 10 + ], + [ + "cCbCcbcbA", + "cbb", + 12 + ], + [ + "cCbaACaB", + "AB", + 12 + ], + [ + "cCbaAcaa", + "BCcCcb", + 12 + ], + [ + "cCbaB", + "aCCbc", + 7 + ], + [ + "cCbbAaaba", + "CccBCA", + 14 + ], + [ + "cCbbAbBC", + "acaAcBC", + 9 + ], + [ + "cCbbBC", + "bca", + 10 + ], + [ + "cCbbCB", + "AaBb", + 9 + ], + [ + "cCbbCBc", + "aBBAbba", + 12 + ], + [ + "cCbbCaA", + "A", + 12 + ], + [ + "cCbbabAC", + "B", + 15 + ], + [ + "cCbbccCb", + "bAcCc", + 10 + ], + [ + "cCbcABAa", + "ABaACa", + 11 + ], + [ + "cCbcB", + "bBBcbbA", + 10 + ], + [ + "cCbcbc", + "Aa", + 12 + ], + [ + "cCbccbB", + "abaaBc", + 11 + ], + [ + "cCbccbbaC", + "aCBAcBB", + 11 + ], + [ + "cCc", + "ABcAcB", + 8 + ], + [ + "cCc", + "BbaaC", + 9 + ], + [ + "cCc", + "CAbAabcAc", + 14 + ], + [ + "cCc", + "CccC", + 4 + ], + [ + "cCcA", + "CBaCbc", + 9 + ], + [ + "cCcAAC", + "cCbaCa", + 7 + ], + [ + "cCcAAbbcB", + "A", + 16 + ], + [ + "cCcAB", + "BCCa", + 6 + ], + [ + "cCcACAaCB", + "cccb", + 12 + ], + [ + "cCcAbbBbc", + "CBbaAb", + 12 + ], + [ + "cCcAcAa", + "c", + 12 + ], + [ + "cCcBC", + "CBBAbBAcC", + 13 + ], + [ + "cCcBCcbcb", + "C", + 16 + ], + [ + "cCcC", + "aAcbBCb", + 10 + ], + [ + "cCcCAb", + "bAaCcaCCB", + 11 + ], + [ + "cCcCaCaCB", + "AC", + 15 + ], + [ + "cCcCcB", + "b", + 11 + ], + [ + "cCcaAbCC", + "cAAaCC", + 7 + ], + [ + "cCcaBA", + "C", + 10 + ], + [ + "cCcaBBB", + "BCaCCcB", + 10 + ], + [ + "cCcaC", + "CCAcbCcCc", + 11 + ], + [ + "cCcaCa", + "AAcb", + 10 + ], + [ + "cCcaabba", + "A", + 15 + ], + [ + "cCcabc", + "bc", + 8 + ], + [ + "cCcb", + "BBaAbbaAb", + 16 + ], + [ + "cCcbAbcb", + "AcCbcAAA", + 12 + ], + [ + "cCcbBCCBC", + "cC", + 14 + ], + [ + "cCcbCCB", + "BAbABC", + 12 + ], + [ + "cCcbCaaC", + "cbbcccbca", + 12 + ], + [ + "cCcbaCca", + "baBCABcC", + 13 + ], + [ + "cCcbaba", + "cCBBA", + 7 + ], + [ + "cCcc", + "a", + 8 + ], + [ + "cCccBaa", + "CBcBA", + 7 + ], + [ + "cCccCaCbA", + "caABbAcB", + 13 + ], + [ + "cCccaA", + "abb", + 12 + ], + [ + "cCccbA", + "BBabb", + 10 + ], + [ + "cCccbB", + "abAcB", + 8 + ], + [ + "cCcccAAb", + "aa", + 14 + ], + [ + "cCcccC", + "CBcBaAAc", + 12 + ], + [ + "cCcccCA", + "bcCACb", + 9 + ], + [ + "ca", + "AC", + 4 + ], + [ + "ca", + "ACA", + 4 + ], + [ + "ca", + "ACACb", + 8 + ], + [ + "ca", + "ACa", + 3 + ], + [ + "ca", + "Aaaa", + 6 + ], + [ + "ca", + "BAacabbab", + 14 + ], + [ + "ca", + "BB", + 4 + ], + [ + "ca", + "BBcaAbBcb", + 14 + ], + [ + "ca", + "BacCBC", + 10 + ], + [ + "ca", + "BbA", + 5 + ], + [ + "ca", + "BbBbBC", + 12 + ], + [ + "ca", + "BbCACC", + 10 + ], + [ + "ca", + "Bcbc", + 6 + ], + [ + "ca", + "BccA", + 5 + ], + [ + "ca", + "C", + 3 + ], + [ + "ca", + "CA", + 2 + ], + [ + "ca", + "CAC", + 4 + ], + [ + "ca", + "CCcbBaaab", + 14 + ], + [ + "ca", + "CbCaAaB", + 11 + ], + [ + "ca", + "a", + 2 + ], + [ + "ca", + "aBAA", + 7 + ], + [ + "ca", + "aC", + 4 + ], + [ + "ca", + "aaAbcACCc", + 15 + ], + [ + "ca", + "acccCcBc", + 14 + ], + [ + "ca", + "bAAaCCAc", + 14 + ], + [ + "ca", + "bBcAABbbB", + 15 + ], + [ + "ca", + "bbb", + 6 + ], + [ + "ca", + "cBC", + 4 + ], + [ + "ca", + "cCbba", + 6 + ], + [ + "ca", + "ccBBaba", + 10 + ], + [ + "caA", + "B", + 6 + ], + [ + "caA", + "CcCcAA", + 7 + ], + [ + "caA", + "aCAbCCCA", + 12 + ], + [ + "caA", + "aCcCbbAAC", + 13 + ], + [ + "caA", + "bBbac", + 8 + ], + [ + "caA", + "bCACCACC", + 12 + ], + [ + "caA", + "bbaA", + 4 + ], + [ + "caA", + "bbbBA", + 8 + ], + [ + "caA", + "cbAacAbbc", + 12 + ], + [ + "caAA", + "bAbaa", + 7 + ], + [ + "caAAAB", + "ab", + 9 + ], + [ + "caAAAcaA", + "CacaAcA", + 6 + ], + [ + "caAACbc", + "CBBCABbbb", + 13 + ], + [ + "caAB", + "A", + 6 + ], + [ + "caAB", + "BCAcbbB", + 10 + ], + [ + "caAB", + "CAaAab", + 6 + ], + [ + "caABabc", + "ACbbCCC", + 12 + ], + [ + "caABb", + "AaCaBC", + 7 + ], + [ + "caAC", + "CbCcB", + 8 + ], + [ + "caAC", + "bBBBCCBA", + 14 + ], + [ + "caACABAA", + "aa", + 13 + ], + [ + "caACBa", + "BbbBcAc", + 13 + ], + [ + "caACCBAaa", + "bbAcA", + 13 + ], + [ + "caACCac", + "a", + 12 + ], + [ + "caAa", + "abBcaB", + 10 + ], + [ + "caAa", + "acaCcBc", + 10 + ], + [ + "caAaBaBc", + "bcbCa", + 14 + ], + [ + "caAaa", + "ac", + 8 + ], + [ + "caAaaaaa", + "ccc", + 14 + ], + [ + "caAac", + "ACcACAbc", + 9 + ], + [ + "caAbAAcBb", + "c", + 16 + ], + [ + "caAbAcACB", + "CBCCaAaaB", + 13 + ], + [ + "caAbBBAbC", + "bacC", + 13 + ], + [ + "caAbBCb", + "bbaaaCc", + 11 + ], + [ + "caAbbBa", + "bAC", + 12 + ], + [ + "caAbbb", + "bbCACabaC", + 13 + ], + [ + "caAc", + "baCBB", + 8 + ], + [ + "caAcCc", + "ACc", + 6 + ], + [ + "caAcbbaCB", + "cbcBc", + 12 + ], + [ + "caAcc", + "BBbabc", + 9 + ], + [ + "caAcccaAb", + "Acab", + 10 + ], + [ + "caB", + "CbAcacAa", + 12 + ], + [ + "caB", + "ccBA", + 4 + ], + [ + "caBAA", + "aCbc", + 8 + ], + [ + "caBABbbC", + "CB", + 13 + ], + [ + "caBAabAcC", + "aCA", + 14 + ], + [ + "caBAbAAc", + "bcCBBa", + 12 + ], + [ + "caBAbaAb", + "ba", + 12 + ], + [ + "caBAcB", + "BaaaBA", + 9 + ], + [ + "caBBA", + "Bc", + 8 + ], + [ + "caBBA", + "CacB", + 5 + ], + [ + "caBBaBAB", + "acAbaa", + 11 + ], + [ + "caBBbaaA", + "bB", + 13 + ], + [ + "caBC", + "CBaaBaAa", + 11 + ], + [ + "caBCBaBC", + "aABaB", + 8 + ], + [ + "caBCac", + "b", + 11 + ], + [ + "caBCc", + "CBCabB", + 9 + ], + [ + "caBa", + "CaBabAC", + 7 + ], + [ + "caBa", + "CcbccA", + 9 + ], + [ + "caBaB", + "BcA", + 8 + ], + [ + "caBaCAb", + "AacCAA", + 8 + ], + [ + "caBaCc", + "cCCA", + 8 + ], + [ + "caBb", + "AaCbCAAa", + 12 + ], + [ + "caBb", + "BCBC", + 6 + ], + [ + "caBb", + "b", + 6 + ], + [ + "caBbCaa", + "caCCbb", + 8 + ], + [ + "caBbCbbBB", + "aBCBCabca", + 11 + ], + [ + "caBbCcBC", + "cccBaB", + 11 + ], + [ + "caBbac", + "bac", + 6 + ], + [ + "caBbbB", + "BcBBA", + 9 + ], + [ + "caBbbcBa", + "b", + 14 + ], + [ + "caBc", + "bA", + 7 + ], + [ + "caBc", + "bBA", + 6 + ], + [ + "caBc", + "cc", + 4 + ], + [ + "caBcAA", + "ccacac", + 7 + ], + [ + "caBcacCb", + "bBc", + 12 + ], + [ + "caC", + "BCbb", + 7 + ], + [ + "caC", + "BbBcA", + 9 + ], + [ + "caC", + "babCacB", + 10 + ], + [ + "caC", + "bb", + 6 + ], + [ + "caC", + "cCbAaa", + 8 + ], + [ + "caCAB", + "CBA", + 7 + ], + [ + "caCAC", + "AcaaCaacc", + 10 + ], + [ + "caCAb", + "BaC", + 6 + ], + [ + "caCAccca", + "bACc", + 11 + ], + [ + "caCBA", + "aBBBBcA", + 10 + ], + [ + "caCBC", + "BcBBabA", + 11 + ], + [ + "caCBC", + "ac", + 7 + ], + [ + "caCBba", + "cBA", + 7 + ], + [ + "caCBbaBB", + "a", + 14 + ], + [ + "caCBbc", + "A", + 11 + ], + [ + "caCC", + "CaBC", + 3 + ], + [ + "caCCAa", + "Baabb", + 10 + ], + [ + "caCCAcaa", + "aC", + 12 + ], + [ + "caCCC", + "bCBaAAbC", + 11 + ], + [ + "caCCaa", + "acabCacA", + 7 + ], + [ + "caCCbCc", + "aCBcC", + 7 + ], + [ + "caCCcc", + "cbaCAacc", + 6 + ], + [ + "caCaBB", + "ccbBaBA", + 8 + ], + [ + "caCaBbcAA", + "baCcAB", + 10 + ], + [ + "caCaCAaaC", + "ABB", + 16 + ], + [ + "caCabcBa", + "CAcCABcc", + 10 + ], + [ + "caCb", + "BcAcABAB", + 11 + ], + [ + "caCb", + "bAbBCb", + 7 + ], + [ + "caCbAAcb", + "aaBbAB", + 9 + ], + [ + "caCbCBBb", + "A", + 15 + ], + [ + "caCbbC", + "cCcc", + 7 + ], + [ + "caCbbcbC", + "CBAAbCC", + 10 + ], + [ + "caCbc", + "B", + 9 + ], + [ + "caCcCaaB", + "AacCbcca", + 11 + ], + [ + "caa", + "CCAcAcaab", + 12 + ], + [ + "caa", + "b", + 6 + ], + [ + "caa", + "bAB", + 5 + ], + [ + "caa", + "bacAa", + 5 + ], + [ + "caaA", + "BaCbcC", + 10 + ], + [ + "caaAABaac", + "AC", + 15 + ], + [ + "caaACA", + "cB", + 10 + ], + [ + "caaAa", + "CaBaAbcB", + 9 + ], + [ + "caaAa", + "bbC", + 10 + ], + [ + "caaAbbc", + "Aa", + 11 + ], + [ + "caaB", + "bAc", + 7 + ], + [ + "caaB", + "cB", + 4 + ], + [ + "caaBA", + "C", + 9 + ], + [ + "caaBAabC", + "BACB", + 11 + ], + [ + "caaBCC", + "Ab", + 10 + ], + [ + "caaBCc", + "CCb", + 9 + ], + [ + "caaCCAa", + "bCccbA", + 11 + ], + [ + "caaCaBcBb", + "aCabaa", + 11 + ], + [ + "caaCbaB", + "CCc", + 11 + ], + [ + "caaCc", + "b", + 10 + ], + [ + "caaCcc", + "cbA", + 9 + ], + [ + "caaa", + "AcCc", + 8 + ], + [ + "caaa", + "bCabaCaa", + 9 + ], + [ + "caaaCb", + "AabC", + 7 + ], + [ + "caaaaC", + "CaaBCCaC", + 7 + ], + [ + "caaab", + "BCC", + 10 + ], + [ + "caaac", + "AaBbACcBa", + 13 + ], + [ + "caab", + "C", + 7 + ], + [ + "caabACbb", + "bb", + 12 + ], + [ + "caabB", + "AbaCbac", + 10 + ], + [ + "caabaABA", + "cbacccaA", + 10 + ], + [ + "caabcbcBB", + "abaAAaC", + 14 + ], + [ + "caac", + "ABb", + 7 + ], + [ + "caacAABaB", + "CbBcbaB", + 10 + ], + [ + "caacaccC", + "BAaccaBA", + 11 + ], + [ + "caacbaAc", + "Abccbc", + 10 + ], + [ + "cab", + "CB", + 4 + ], + [ + "cab", + "a", + 4 + ], + [ + "cab", + "abCCb", + 7 + ], + [ + "cab", + "bAaaBBA", + 11 + ], + [ + "cab", + "bBbCABC", + 11 + ], + [ + "cabAA", + "c", + 8 + ], + [ + "cabACCCCb", + "b", + 16 + ], + [ + "cabACc", + "bBBAb", + 9 + ], + [ + "cabBAaaB", + "BbCCBaBc", + 12 + ], + [ + "cabBC", + "A", + 9 + ], + [ + "cabBCa", + "C", + 10 + ], + [ + "cabBCcBbA", + "BbCbAAaAb", + 16 + ], + [ + "cabBaA", + "cCa", + 8 + ], + [ + "cabC", + "BBCabBCa", + 9 + ], + [ + "cabC", + "BCBb", + 7 + ], + [ + "cabC", + "Baccb", + 7 + ], + [ + "cabCCAcC", + "cCcA", + 9 + ], + [ + "cabCCaBca", + "BCbBc", + 11 + ], + [ + "cabCaba", + "a", + 12 + ], + [ + "cabCb", + "cB", + 7 + ], + [ + "cabCcbB", + "BBbAcBaac", + 13 + ], + [ + "cabaBBb", + "AbC", + 11 + ], + [ + "cabaa", + "ccBCAbAB", + 10 + ], + [ + "cababBBaB", + "AcBCAC", + 14 + ], + [ + "cabac", + "BCa", + 8 + ], + [ + "cabb", + "CACcCcac", + 14 + ], + [ + "cabb", + "a", + 6 + ], + [ + "cabbBBA", + "CCab", + 11 + ], + [ + "cabbBc", + "aCccAAcc", + 12 + ], + [ + "cabbbACA", + "bcaCC", + 11 + ], + [ + "cabc", + "BCBcBca", + 10 + ], + [ + "cabc", + "CaaBBAABA", + 14 + ], + [ + "cabcBCc", + "BABAC", + 10 + ], + [ + "cabca", + "aAaC", + 8 + ], + [ + "cac", + "BBbaabba", + 14 + ], + [ + "cac", + "bb", + 6 + ], + [ + "cac", + "bcBa", + 6 + ], + [ + "cac", + "cBaaBa", + 8 + ], + [ + "cacABBaB", + "BaBAabBbA", + 11 + ], + [ + "cacAaA", + "bc", + 10 + ], + [ + "cacAaaaBb", + "Aba", + 14 + ], + [ + "cacAbbcCB", + "AcCcAaBC", + 11 + ], + [ + "cacAc", + "cAcaAB", + 5 + ], + [ + "cacB", + "BCcbAAcbB", + 11 + ], + [ + "cacB", + "c", + 6 + ], + [ + "cacBAA", + "BbbCCab", + 12 + ], + [ + "cacBAc", + "bbaBB", + 10 + ], + [ + "cacBaAa", + "aBBBCbbbA", + 15 + ], + [ + "cacBab", + "AC", + 10 + ], + [ + "cacCCaB", + "ABCc", + 10 + ], + [ + "caca", + "bCbbAa", + 9 + ], + [ + "cacaAbAAA", + "bc", + 16 + ], + [ + "cacaAcc", + "bc", + 12 + ], + [ + "cacaBc", + "BaabCbbB", + 12 + ], + [ + "cacaaBc", + "caAacCAAa", + 11 + ], + [ + "cacaabBCa", + "AACaccAc", + 13 + ], + [ + "cacacBAbC", + "C", + 16 + ], + [ + "cacaca", + "aB", + 10 + ], + [ + "cacbAAaCa", + "b", + 16 + ], + [ + "cacbB", + "AbBaCB", + 9 + ], + [ + "cacbCcbcB", + "cc", + 14 + ], + [ + "cacbc", + "BbAACAAC", + 13 + ], + [ + "cacc", + "CAccbBacC", + 11 + ], + [ + "caccA", + "ccc", + 4 + ], + [ + "caccbc", + "ab", + 8 + ], + [ + "cb", + "AAaBCAa", + 13 + ], + [ + "cb", + "AAaC", + 8 + ], + [ + "cb", + "ABAcbc", + 8 + ], + [ + "cb", + "ABccA", + 8 + ], + [ + "cb", + "ACABcb", + 8 + ], + [ + "cb", + "Aa", + 4 + ], + [ + "cb", + "AbBAaB", + 10 + ], + [ + "cb", + "BCcbbACB", + 12 + ], + [ + "cb", + "BabCaAAB", + 14 + ], + [ + "cb", + "BbBCACCBb", + 15 + ], + [ + "cb", + "C", + 3 + ], + [ + "cb", + "CAAbCCB", + 11 + ], + [ + "cb", + "CABBCa", + 10 + ], + [ + "cb", + "CCACAAB", + 12 + ], + [ + "cb", + "aBcA", + 6 + ], + [ + "cb", + "aCBAaabaa", + 15 + ], + [ + "cb", + "aCBaacAac", + 16 + ], + [ + "cb", + "aCC", + 5 + ], + [ + "cb", + "aaBcCC", + 10 + ], + [ + "cb", + "acBAC", + 7 + ], + [ + "cb", + "bCBcaCbA", + 12 + ], + [ + "cb", + "bcCCC", + 8 + ], + [ + "cb", + "cBCaBCC", + 11 + ], + [ + "cb", + "cCabBBabA", + 14 + ], + [ + "cb", + "cb", + 0 + ], + [ + "cb", + "cccCa", + 8 + ], + [ + "cbA", + "AaAaB", + 8 + ], + [ + "cbA", + "AcACCa", + 9 + ], + [ + "cbA", + "BbCBABAA", + 12 + ], + [ + "cbA", + "aBcCCAAca", + 14 + ], + [ + "cbA", + "aaab", + 7 + ], + [ + "cbAA", + "BAbBc", + 8 + ], + [ + "cbAAaBcc", + "cABABCbb", + 10 + ], + [ + "cbAAcB", + "a", + 11 + ], + [ + "cbAAcC", + "cacC", + 5 + ], + [ + "cbAB", + "CAABa", + 5 + ], + [ + "cbABBb", + "acBA", + 9 + ], + [ + "cbABc", + "cbcAaCb", + 7 + ], + [ + "cbAC", + "C", + 6 + ], + [ + "cbACAbc", + "aABCAbA", + 8 + ], + [ + "cbACcAC", + "bbcAcbB", + 10 + ], + [ + "cbACcBBC", + "cABbcbbaB", + 12 + ], + [ + "cbACcb", + "cabbaAC", + 10 + ], + [ + "cbAaB", + "babCC", + 9 + ], + [ + "cbAaBc", + "Acc", + 8 + ], + [ + "cbAaBcCA", + "C", + 14 + ], + [ + "cbAaCBbB", + "A", + 14 + ], + [ + "cbAabcC", + "BBBCAc", + 11 + ], + [ + "cbAbAaBbc", + "bC", + 15 + ], + [ + "cbAbBBAA", + "bcaBbAc", + 9 + ], + [ + "cbAbCBA", + "acABcaC", + 10 + ], + [ + "cbAbCBBB", + "AAaaa", + 14 + ], + [ + "cbAbCaA", + "cbAcBB", + 7 + ], + [ + "cbAbaCc", + "BACabcaa", + 11 + ], + [ + "cbAbcCA", + "ccC", + 8 + ], + [ + "cbAbcaB", + "cc", + 10 + ], + [ + "cbAbcbAbB", + "AccBbbcA", + 13 + ], + [ + "cbAbcbb", + "caCaAB", + 10 + ], + [ + "cbAc", + "BABcCB", + 9 + ], + [ + "cbAc", + "bacb", + 5 + ], + [ + "cbAcCBb", + "ABac", + 10 + ], + [ + "cbAcbcccB", + "CCC", + 15 + ], + [ + "cbB", + "Aaa", + 6 + ], + [ + "cbB", + "CbC", + 3 + ], + [ + "cbB", + "caCaB", + 6 + ], + [ + "cbBA", + "BcBBccBA", + 9 + ], + [ + "cbBA", + "aCcbB", + 6 + ], + [ + "cbBAAc", + "bcABA", + 8 + ], + [ + "cbBAB", + "AbcBc", + 8 + ], + [ + "cbBABc", + "Cbba", + 7 + ], + [ + "cbBACACa", + "aA", + 13 + ], + [ + "cbBACBccA", + "Ca", + 15 + ], + [ + "cbBAba", + "AcAca", + 8 + ], + [ + "cbBAbb", + "CbaaC", + 8 + ], + [ + "cbBB", + "cAb", + 5 + ], + [ + "cbBB", + "cAba", + 5 + ], + [ + "cbBBbB", + "BBBBcaA", + 9 + ], + [ + "cbBBbca", + "cACABCb", + 10 + ], + [ + "cbBBcACBb", + "AACbBbbb", + 13 + ], + [ + "cbBCCaA", + "AccCcBc", + 11 + ], + [ + "cbBCacA", + "cB", + 10 + ], + [ + "cbBCb", + "CAAbBA", + 9 + ], + [ + "cbBa", + "aaCC", + 8 + ], + [ + "cbBa", + "cBaB", + 4 + ], + [ + "cbBa", + "cccB", + 6 + ], + [ + "cbBaBcAc", + "Aab", + 13 + ], + [ + "cbBaCAC", + "CCcCCc", + 10 + ], + [ + "cbBac", + "c", + 8 + ], + [ + "cbBacA", + "bCaaaBaa", + 12 + ], + [ + "cbBb", + "AABA", + 6 + ], + [ + "cbBb", + "CCaa", + 7 + ], + [ + "cbBbAbB", + "cC", + 12 + ], + [ + "cbBbCACCC", + "Ccc", + 14 + ], + [ + "cbBba", + "ccb", + 6 + ], + [ + "cbBbbb", + "BccCbaAAB", + 13 + ], + [ + "cbBc", + "A", + 8 + ], + [ + "cbBcCAa", + "acab", + 11 + ], + [ + "cbBcCCbAA", + "caAbaCaBc", + 14 + ], + [ + "cbBcaCB", + "bA", + 11 + ], + [ + "cbBcbaccC", + "CaAACBAAA", + 16 + ], + [ + "cbBcbc", + "ABBBAAc", + 9 + ], + [ + "cbBcc", + "CACaBC", + 10 + ], + [ + "cbC", + "AcABa", + 7 + ], + [ + "cbC", + "bCCBBa", + 10 + ], + [ + "cbCAbaAa", + "ccBCaC", + 11 + ], + [ + "cbCB", + "AABCCaaA", + 13 + ], + [ + "cbCBABAAb", + "a", + 17 + ], + [ + "cbCBAcacb", + "CAa", + 12 + ], + [ + "cbCBC", + "cbcaCaaba", + 11 + ], + [ + "cbCBccbcc", + "BcCbA", + 11 + ], + [ + "cbCC", + "B", + 7 + ], + [ + "cbCCBBCB", + "baBaCbC", + 11 + ], + [ + "cbCCBccBb", + "BaAaC", + 16 + ], + [ + "cbCCC", + "AACACB", + 8 + ], + [ + "cbCCbABBB", + "CcaABC", + 11 + ], + [ + "cbCCbBac", + "CAaA", + 12 + ], + [ + "cbCa", + "CBA", + 5 + ], + [ + "cbCa", + "bbcacb", + 7 + ], + [ + "cbCaB", + "cbBaCb", + 5 + ], + [ + "cbCbAaCCa", + "ACAAb", + 13 + ], + [ + "cbCbaC", + "BACcBC", + 8 + ], + [ + "cbCbaCcaC", + "AaaaBC", + 14 + ], + [ + "cbCbb", + "A", + 10 + ], + [ + "cbCbbCAAc", + "caccc", + 12 + ], + [ + "cbCbcC", + "B", + 11 + ], + [ + "cbCcAAcA", + "AcaAaCcCa", + 13 + ], + [ + "cbCcCAc", + "babCC", + 9 + ], + [ + "cbCcCB", + "BCABA", + 9 + ], + [ + "cbCcCbA", + "cB", + 11 + ], + [ + "cbCcb", + "AacBB", + 8 + ], + [ + "cbCcbca", + "BCB", + 10 + ], + [ + "cbCccca", + "AAA", + 13 + ], + [ + "cba", + "A", + 5 + ], + [ + "cba", + "b", + 4 + ], + [ + "cba", + "bBCaaCc", + 11 + ], + [ + "cba", + "cAAbc", + 6 + ], + [ + "cba", + "caABABbab", + 12 + ], + [ + "cba", + "ccAB", + 5 + ], + [ + "cbaAa", + "cBbbcB", + 8 + ], + [ + "cbaAc", + "cAAc", + 3 + ], + [ + "cbaBACBAc", + "a", + 16 + ], + [ + "cbaBCAAC", + "BAAcCc", + 12 + ], + [ + "cbaBCbbcC", + "BcAaabac", + 12 + ], + [ + "cbaBbCa", + "BBACcA", + 10 + ], + [ + "cbaC", + "BBCaACabc", + 13 + ], + [ + "cbaC", + "aaA", + 6 + ], + [ + "cbaCABA", + "BbAaBccBC", + 11 + ], + [ + "cbaCCA", + "cbCABA", + 6 + ], + [ + "cbaCCCa", + "a", + 12 + ], + [ + "cbaCacac", + "AcBBbAA", + 13 + ], + [ + "cbaCcaA", + "C", + 12 + ], + [ + "cbaa", + "aaBcCBcBa", + 13 + ], + [ + "cbaaA", + "cBaa", + 3 + ], + [ + "cbaabCcC", + "bB", + 13 + ], + [ + "cbabAA", + "Bc", + 11 + ], + [ + "cbabBbc", + "AbcacAC", + 11 + ], + [ + "cbabC", + "CBbaAa", + 7 + ], + [ + "cbabbC", + "aCacAcBAC", + 13 + ], + [ + "cbac", + "cCbBcACa", + 10 + ], + [ + "cbacAAAA", + "BcAbcAbc", + 12 + ], + [ + "cbacAAacc", + "ab", + 16 + ], + [ + "cbacAC", + "BaaABAbC", + 10 + ], + [ + "cbacACa", + "AcBbcBaC", + 10 + ], + [ + "cbacB", + "CaBAa", + 9 + ], + [ + "cbacCCab", + "Acbc", + 12 + ], + [ + "cbacCabBB", + "CCcc", + 14 + ], + [ + "cbb", + "BBbB", + 5 + ], + [ + "cbb", + "CcAaAC", + 10 + ], + [ + "cbb", + "bBcABAcCC", + 15 + ], + [ + "cbb", + "cbccAbA", + 8 + ], + [ + "cbbABaCBa", + "Ca", + 14 + ], + [ + "cbbAa", + "aBBcAa", + 6 + ], + [ + "cbbAaC", + "cbc", + 7 + ], + [ + "cbbAbB", + "BABaBA", + 9 + ], + [ + "cbbAbaaB", + "CCababBa", + 10 + ], + [ + "cbbBAC", + "AacCcb", + 12 + ], + [ + "cbbBbA", + "AbCAACc", + 12 + ], + [ + "cbbBbb", + "b", + 10 + ], + [ + "cbbBbbbCA", + "bBB", + 13 + ], + [ + "cbbBcBbBb", + "BaABc", + 14 + ], + [ + "cbbBcC", + "C", + 10 + ], + [ + "cbbCAaB", + "A", + 12 + ], + [ + "cbbCbC", + "cBaCCABb", + 10 + ], + [ + "cbbCcbA", + "baCBaBab", + 12 + ], + [ + "cbbaCA", + "aCAbbbCcA", + 9 + ], + [ + "cbbaCb", + "ABaCaB", + 8 + ], + [ + "cbbaaC", + "BcAAbAba", + 11 + ], + [ + "cbbabc", + "BaAaacCAc", + 14 + ], + [ + "cbbacB", + "b", + 10 + ], + [ + "cbbb", + "CC", + 7 + ], + [ + "cbbb", + "bBcCaCAC", + 14 + ], + [ + "cbbbB", + "abacb", + 7 + ], + [ + "cbbbBBcb", + "cA", + 14 + ], + [ + "cbbbaBbC", + "CbAABAC", + 8 + ], + [ + "cbbbc", + "c", + 8 + ], + [ + "cbbc", + "aCBAcAc", + 10 + ], + [ + "cbbc", + "abBBCAB", + 10 + ], + [ + "cbbcBC", + "BAB", + 9 + ], + [ + "cbbcBcCCb", + "bc", + 14 + ], + [ + "cbc", + "AACAAbCCB", + 14 + ], + [ + "cbc", + "BcbaA", + 6 + ], + [ + "cbc", + "CbBbACB", + 10 + ], + [ + "cbc", + "b", + 4 + ], + [ + "cbc", + "cBCcBaC", + 9 + ], + [ + "cbcA", + "ABBAcC", + 9 + ], + [ + "cbcAA", + "c", + 8 + ], + [ + "cbcACAabb", + "bccBcbAaA", + 13 + ], + [ + "cbcAaAAb", + "BBccBbCA", + 13 + ], + [ + "cbcAb", + "CCCbbbcba", + 13 + ], + [ + "cbcB", + "Aba", + 6 + ], + [ + "cbcB", + "bCaaaAba", + 14 + ], + [ + "cbcBacB", + "acbB", + 9 + ], + [ + "cbcBbb", + "cBa", + 8 + ], + [ + "cbcBcc", + "bbCcA", + 7 + ], + [ + "cbcC", + "aABacCCC", + 11 + ], + [ + "cbcCA", + "bBAaba", + 10 + ], + [ + "cbcCBcbc", + "acBa", + 12 + ], + [ + "cbcCcBa", + "CbbACa", + 8 + ], + [ + "cbcaB", + "bCaAAc", + 9 + ], + [ + "cbcabBAac", + "bcBCbCBb", + 13 + ], + [ + "cbcb", + "bba", + 6 + ], + [ + "cbcbCCA", + "AaaCCabcc", + 15 + ], + [ + "cbcbaCBb", + "BccCAb", + 9 + ], + [ + "cbcbbC", + "BAaa", + 11 + ], + [ + "cbcbcaCC", + "BaBbbB", + 14 + ], + [ + "cbcccaCc", + "acAAAa", + 13 + ], + [ + "cc", + "AAAbBbC", + 13 + ], + [ + "cc", + "ABBaccbbB", + 14 + ], + [ + "cc", + "ABa", + 6 + ], + [ + "cc", + "ABaA", + 8 + ], + [ + "cc", + "BAbbAab", + 14 + ], + [ + "cc", + "BAbc", + 6 + ], + [ + "cc", + "BBacCb", + 9 + ], + [ + "cc", + "BCbaB", + 9 + ], + [ + "cc", + "BaBcBAAc", + 12 + ], + [ + "cc", + "BaC", + 5 + ], + [ + "cc", + "Bbca", + 6 + ], + [ + "cc", + "CAbbcb", + 9 + ], + [ + "cc", + "CCAcCb", + 9 + ], + [ + "cc", + "Ccb", + 3 + ], + [ + "cc", + "aAaBCaA", + 13 + ], + [ + "cc", + "aB", + 4 + ], + [ + "cc", + "aBA", + 6 + ], + [ + "cc", + "acCcCabA", + 12 + ], + [ + "cc", + "b", + 4 + ], + [ + "cc", + "bACABABA", + 15 + ], + [ + "cc", + "baCaAAAc", + 13 + ], + [ + "cc", + "bacAbacB", + 12 + ], + [ + "cc", + "bcbA", + 6 + ], + [ + "cc", + "cABcAA", + 8 + ], + [ + "cc", + "cACac", + 6 + ], + [ + "cc", + "cAbba", + 8 + ], + [ + "cc", + "cB", + 2 + ], + [ + "cc", + "cCac", + 4 + ], + [ + "cc", + "cCbbAcaA", + 12 + ], + [ + "cc", + "cb", + 2 + ], + [ + "cc", + "ccBc", + 4 + ], + [ + "cc", + "ccCab", + 6 + ], + [ + "ccA", + "AcCccAcc", + 10 + ], + [ + "ccA", + "CBAaC", + 7 + ], + [ + "ccA", + "b", + 6 + ], + [ + "ccAABB", + "ccCBBbC", + 7 + ], + [ + "ccAAb", + "BaABC", + 8 + ], + [ + "ccAAbaA", + "BaA", + 9 + ], + [ + "ccAAbbbaB", + "AABBCc", + 12 + ], + [ + "ccAB", + "BcA", + 4 + ], + [ + "ccABA", + "BcBBcbBA", + 8 + ], + [ + "ccABBAA", + "bCCac", + 12 + ], + [ + "ccABcAA", + "AABBaBaA", + 11 + ], + [ + "ccAC", + "CAACBCaCA", + 13 + ], + [ + "ccAC", + "bA", + 6 + ], + [ + "ccACAAcb", + "AbabCa", + 12 + ], + [ + "ccACABbAc", + "BCbAb", + 12 + ], + [ + "ccACB", + "CBbabcAC", + 11 + ], + [ + "ccACBAaBa", + "ca", + 14 + ], + [ + "ccACBca", + "Cb", + 11 + ], + [ + "ccACC", + "CcaAba", + 7 + ], + [ + "ccACC", + "aCc", + 6 + ], + [ + "ccACaABcb", + "CBBCCABCc", + 10 + ], + [ + "ccACaBbAC", + "cBaAaB", + 11 + ], + [ + "ccACcAAB", + "AbCbaCAbb", + 13 + ], + [ + "ccACcbb", + "abCcBCb", + 9 + ], + [ + "ccAa", + "bca", + 4 + ], + [ + "ccAa", + "c", + 6 + ], + [ + "ccAaACB", + "BCabaaA", + 11 + ], + [ + "ccAaAbccA", + "AAbAAb", + 12 + ], + [ + "ccAaC", + "bAaaACAB", + 11 + ], + [ + "ccAcAC", + "c", + 10 + ], + [ + "ccAcAb", + "AbABBCbBA", + 14 + ], + [ + "ccAcb", + "bcAb", + 4 + ], + [ + "ccAcccc", + "ccCca", + 7 + ], + [ + "ccB", + "B", + 4 + ], + [ + "ccB", + "CcBAB", + 5 + ], + [ + "ccB", + "aAa", + 6 + ], + [ + "ccB", + "b", + 5 + ], + [ + "ccB", + "cAaAbaa", + 11 + ], + [ + "ccB", + "cBbCACaAB", + 13 + ], + [ + "ccBA", + "B", + 6 + ], + [ + "ccBA", + "CAac", + 7 + ], + [ + "ccBACbca", + "Abb", + 12 + ], + [ + "ccBAc", + "baaaCaaB", + 14 + ], + [ + "ccBB", + "BcC", + 6 + ], + [ + "ccBBA", + "BbACCaA", + 11 + ], + [ + "ccBBbAa", + "b", + 12 + ], + [ + "ccBBbb", + "cBbBb", + 4 + ], + [ + "ccBBbcC", + "CAbacA", + 10 + ], + [ + "ccBC", + "AaBAA", + 8 + ], + [ + "ccBC", + "Baba", + 7 + ], + [ + "ccBC", + "b", + 7 + ], + [ + "ccBC", + "cbba", + 5 + ], + [ + "ccBCAaBc", + "bBCCCAA", + 12 + ], + [ + "ccBCAbC", + "abC", + 9 + ], + [ + "ccBCBBcac", + "BCabBA", + 12 + ], + [ + "ccBCC", + "AbbC", + 7 + ], + [ + "ccBCCCa", + "BaCaB", + 10 + ], + [ + "ccBCCaBcB", + "AcC", + 14 + ], + [ + "ccBCCc", + "aBb", + 10 + ], + [ + "ccBCbBcC", + "ca", + 14 + ], + [ + "ccBCcCaA", + "bcbBAcaBA", + 10 + ], + [ + "ccBa", + "ACCaABcCa", + 12 + ], + [ + "ccBaC", + "AC", + 7 + ], + [ + "ccBaaAa", + "Cc", + 11 + ], + [ + "ccBab", + "BACC", + 9 + ], + [ + "ccBabcaBA", + "CBbc", + 11 + ], + [ + "ccBacCAbB", + "cCCcA", + 11 + ], + [ + "ccBbAABB", + "ccb", + 10 + ], + [ + "ccBbAaB", + "Cc", + 11 + ], + [ + "ccBbaCAb", + "Bcc", + 13 + ], + [ + "ccBbab", + "cCbBc", + 7 + ], + [ + "ccBbbbcB", + "Aacaaa", + 16 + ], + [ + "ccBcb", + "bbcACCb", + 9 + ], + [ + "ccC", + "Ab", + 6 + ], + [ + "ccC", + "CBcCaAbAc", + 13 + ], + [ + "ccC", + "aaCB", + 6 + ], + [ + "ccC", + "bBBAA", + 10 + ], + [ + "ccCAA", + "aCC", + 7 + ], + [ + "ccCAcCB", + "baAAbBac", + 14 + ], + [ + "ccCBAa", + "AABbcA", + 10 + ], + [ + "ccCBCc", + "caBcB", + 7 + ], + [ + "ccCBbC", + "BbbA", + 9 + ], + [ + "ccCC", + "A", + 8 + ], + [ + "ccCC", + "CCaBcACBC", + 11 + ], + [ + "ccCC", + "aab", + 8 + ], + [ + "ccCCAaa", + "B", + 14 + ], + [ + "ccCCBAaaC", + "Bb", + 16 + ], + [ + "ccCCBbCaA", + "AcACCaB", + 10 + ], + [ + "ccCCCaaCb", + "BaAbBB", + 16 + ], + [ + "ccCCacA", + "ACAAa", + 10 + ], + [ + "ccCa", + "acbBa", + 6 + ], + [ + "ccCaa", + "aCBCa", + 7 + ], + [ + "ccCab", + "CCAb", + 4 + ], + [ + "ccCbBB", + "AAcabCa", + 10 + ], + [ + "ccCba", + "aacAbCBb", + 11 + ], + [ + "ccCbbCBA", + "AaCBcAb", + 12 + ], + [ + "ccCbc", + "aCbBCcb", + 10 + ], + [ + "ccCbcCcA", + "aAA", + 14 + ], + [ + "ccCbcaaAC", + "abB", + 16 + ], + [ + "ccCcAbCA", + "AB", + 13 + ], + [ + "ccCcAccA", + "CCABcCCbc", + 13 + ], + [ + "ccCcBaA", + "caC", + 10 + ], + [ + "ccCcBbc", + "aaacAca", + 12 + ], + [ + "ccCcBcC", + "CBCCCBAA", + 9 + ], + [ + "ccCcabcb", + "baAb", + 12 + ], + [ + "ccCccbABc", + "b", + 16 + ], + [ + "cca", + "ABc", + 6 + ], + [ + "cca", + "aa", + 4 + ], + [ + "cca", + "bACA", + 6 + ], + [ + "ccaA", + "AcBa", + 5 + ], + [ + "ccaA", + "Ca", + 5 + ], + [ + "ccaABaccb", + "AAaaCBb", + 10 + ], + [ + "ccaACAC", + "BAc", + 11 + ], + [ + "ccaAba", + "Ccb", + 7 + ], + [ + "ccaAc", + "B", + 10 + ], + [ + "ccaAcCB", + "ccBB", + 8 + ], + [ + "ccaB", + "BbcB", + 6 + ], + [ + "ccaBC", + "CbbbB", + 8 + ], + [ + "ccaCA", + "ABBbb", + 10 + ], + [ + "ccaCAB", + "cCbcb", + 7 + ], + [ + "ccaCAcAB", + "acbb", + 11 + ], + [ + "ccaCAcBb", + "ccCbb", + 7 + ], + [ + "ccaCCCcCC", + "AACAc", + 13 + ], + [ + "ccaCaABb", + "bA", + 14 + ], + [ + "ccaCc", + "babb", + 8 + ], + [ + "ccaCcAAaB", + "BcAAaB", + 8 + ], + [ + "ccaCcB", + "B", + 10 + ], + [ + "ccaaBCB", + "AAA", + 12 + ], + [ + "ccaaaa", + "Aa", + 9 + ], + [ + "ccaab", + "acbbC", + 8 + ], + [ + "ccaac", + "BCBBb", + 9 + ], + [ + "ccab", + "C", + 7 + ], + [ + "ccab", + "aabBBcABa", + 14 + ], + [ + "ccabaB", + "CACba", + 7 + ], + [ + "ccabaCa", + "ccAac", + 6 + ], + [ + "ccababcA", + "abc", + 10 + ], + [ + "ccacAbBBB", + "Cb", + 15 + ], + [ + "ccacb", + "cbCA", + 7 + ], + [ + "ccb", + "A", + 6 + ], + [ + "ccb", + "Aa", + 6 + ], + [ + "ccb", + "Aca", + 4 + ], + [ + "ccb", + "acBAcb", + 6 + ], + [ + "ccb", + "acBccbAcA", + 12 + ], + [ + "ccbA", + "ba", + 5 + ], + [ + "ccbAAA", + "BAaca", + 9 + ], + [ + "ccbAACAc", + "CaaCBcBAa", + 14 + ], + [ + "ccbABAA", + "BBaccbb", + 13 + ], + [ + "ccbACa", + "cabACc", + 4 + ], + [ + "ccbAaCA", + "cCccaCCA", + 7 + ], + [ + "ccbAb", + "bbbCaACa", + 12 + ], + [ + "ccbAbA", + "cbcaBb", + 8 + ], + [ + "ccbAbBB", + "cBcbA", + 8 + ], + [ + "ccbAcC", + "AbBCcaacB", + 12 + ], + [ + "ccbBbcb", + "bCbCAC", + 10 + ], + [ + "ccbC", + "ACccBAc", + 8 + ], + [ + "ccbC", + "CB", + 6 + ], + [ + "ccbC", + "aCAA", + 7 + ], + [ + "ccbCC", + "AcCcCcb", + 8 + ], + [ + "ccbCa", + "bcBBcb", + 8 + ], + [ + "ccbCaACca", + "BBcaA", + 12 + ], + [ + "ccbCbbB", + "b", + 12 + ], + [ + "ccba", + "CAbbBc", + 9 + ], + [ + "ccba", + "Caa", + 5 + ], + [ + "ccba", + "bA", + 5 + ], + [ + "ccba", + "cbc", + 4 + ], + [ + "ccbabbC", + "AB", + 12 + ], + [ + "ccbbBbAb", + "CBc", + 13 + ], + [ + "ccbbabB", + "bCcaCa", + 11 + ], + [ + "ccbbcB", + "ACbcaaA", + 11 + ], + [ + "ccbbccCAC", + "CcccBcBC", + 10 + ], + [ + "ccbcABBCb", + "aAcAacC", + 12 + ], + [ + "ccbcAbBbc", + "a", + 17 + ], + [ + "ccbcB", + "CBaCAAcBc", + 12 + ], + [ + "ccbcBBAbB", + "cBccCc", + 13 + ], + [ + "ccbcCaab", + "cCaAAB", + 10 + ], + [ + "ccbcCbcB", + "CCCa", + 12 + ], + [ + "ccbcba", + "CA", + 10 + ], + [ + "ccc", + "AcBcbBcb", + 10 + ], + [ + "ccc", + "BbcCbB", + 9 + ], + [ + "ccc", + "CACABCbb", + 13 + ], + [ + "ccc", + "ac", + 4 + ], + [ + "ccc", + "baAABCbcc", + 13 + ], + [ + "ccc", + "c", + 4 + ], + [ + "ccc", + "cabACBAbB", + 15 + ], + [ + "cccA", + "cbBAbbB", + 10 + ], + [ + "cccAAA", + "AcB", + 10 + ], + [ + "cccAABCcA", + "ba", + 16 + ], + [ + "cccAC", + "ABCaa", + 8 + ], + [ + "cccAaccc", + "bcCC", + 12 + ], + [ + "cccAbC", + "cBbCAb", + 7 + ], + [ + "cccB", + "bACBcb", + 8 + ], + [ + "cccB", + "c", + 6 + ], + [ + "cccBAA", + "cbB", + 8 + ], + [ + "cccBAbb", + "bCCacb", + 9 + ], + [ + "cccBBAC", + "bbbcB", + 12 + ], + [ + "cccBBCc", + "CAaaAb", + 13 + ], + [ + "cccC", + "ACbB", + 7 + ], + [ + "cccC", + "bbbA", + 8 + ], + [ + "cccCabBA", + "AaBc", + 12 + ], + [ + "cccCbbcc", + "CcC", + 11 + ], + [ + "cccCccaC", + "b", + 16 + ], + [ + "ccca", + "cCcCAa", + 5 + ], + [ + "cccaAb", + "Abb", + 9 + ], + [ + "cccaCbA", + "cBcCBBbaA", + 9 + ], + [ + "cccaaa", + "BaABcbcca", + 14 + ], + [ + "cccacB", + "abBB", + 10 + ], + [ + "cccb", + "AABc", + 8 + ], + [ + "cccb", + "CbBAAbA", + 11 + ], + [ + "cccbABcc", + "a", + 15 + ], + [ + "cccbB", + "bcCAcaaba", + 11 + ], + [ + "cccbaBC", + "cbACAac", + 10 + ], + [ + "cccbabBb", + "ACc", + 13 + ], + [ + "cccbbA", + "bbcAc", + 10 + ], + [ + "cccbcaCC", + "AbCACBcCb", + 13 + ], + [ + "cccbcaaaC", + "baCcCc", + 14 + ], + [ + "ccccBa", + "abcbaaCbA", + 13 + ], + [ + "ccccCBcCA", + "Cbbab", + 15 + ], + [ + "ccccCCc", + "aabB", + 14 + ], + [ + "ccccaCA", + "Cb", + 12 + ], + [ + "cccccb", + "a", + 12 + ], + [ + "ccccccAcc", + "AB", + 16 + ] +] \ No newline at end of file diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 123bed6198c615..03a0f8b576f6fc 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1770,7 +1770,6 @@ class TestException(MemoryError): gc_collect() -global_for_suggestions = None class NameErrorTests(unittest.TestCase): def test_name_error_has_name(self): @@ -1779,272 +1778,6 @@ def test_name_error_has_name(self): except NameError as exc: self.assertEqual("bluch", exc.name) - def test_name_error_suggestions(self): - def Substitution(): - noise = more_noise = a = bc = None - blech = None - print(bluch) - - def Elimination(): - noise = more_noise = a = bc = None - blch = None - print(bluch) - - def Addition(): - noise = more_noise = a = bc = None - bluchin = None - print(bluch) - - def SubstitutionOverElimination(): - blach = None - bluc = None - print(bluch) - - def SubstitutionOverAddition(): - blach = None - bluchi = None - print(bluch) - - def EliminationOverAddition(): - blucha = None - bluc = None - print(bluch) - - for func, suggestion in [(Substitution, "'blech'?"), - (Elimination, "'blch'?"), - (Addition, "'bluchin'?"), - (EliminationOverAddition, "'blucha'?"), - (SubstitutionOverElimination, "'blach'?"), - (SubstitutionOverAddition, "'blach'?")]: - err = None - try: - func() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertIn(suggestion, err.getvalue()) - - def test_name_error_suggestions_from_globals(self): - def func(): - print(global_for_suggestio) - try: - func() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertIn("'global_for_suggestions'?", err.getvalue()) - - def test_name_error_suggestions_from_builtins(self): - def func(): - print(ZeroDivisionErrrrr) - try: - func() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertIn("'ZeroDivisionError'?", err.getvalue()) - - def test_name_error_suggestions_do_not_trigger_for_long_names(self): - def f(): - somethingverywronghehehehehehe = None - print(somethingverywronghe) - - try: - f() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("somethingverywronghehe", err.getvalue()) - - def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): - vvv = mom = w = id = pytho = None - - with self.subTest(name="b"): - try: - b - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - with self.subTest(name="v"): - try: - v - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - with self.subTest(name="m"): - try: - m - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - with self.subTest(name="py"): - try: - py - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - def test_name_error_suggestions_do_not_trigger_for_too_many_locals(self): - def f(): - # Mutating locals() is unreliable, so we need to do it by hand - a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = a10 = \ - a11 = a12 = a13 = a14 = a15 = a16 = a17 = a18 = a19 = a20 = \ - a21 = a22 = a23 = a24 = a25 = a26 = a27 = a28 = a29 = a30 = \ - a31 = a32 = a33 = a34 = a35 = a36 = a37 = a38 = a39 = a40 = \ - a41 = a42 = a43 = a44 = a45 = a46 = a47 = a48 = a49 = a50 = \ - a51 = a52 = a53 = a54 = a55 = a56 = a57 = a58 = a59 = a60 = \ - a61 = a62 = a63 = a64 = a65 = a66 = a67 = a68 = a69 = a70 = \ - a71 = a72 = a73 = a74 = a75 = a76 = a77 = a78 = a79 = a80 = \ - a81 = a82 = a83 = a84 = a85 = a86 = a87 = a88 = a89 = a90 = \ - a91 = a92 = a93 = a94 = a95 = a96 = a97 = a98 = a99 = a100 = \ - a101 = a102 = a103 = a104 = a105 = a106 = a107 = a108 = a109 = a110 = \ - a111 = a112 = a113 = a114 = a115 = a116 = a117 = a118 = a119 = a120 = \ - a121 = a122 = a123 = a124 = a125 = a126 = a127 = a128 = a129 = a130 = \ - a131 = a132 = a133 = a134 = a135 = a136 = a137 = a138 = a139 = a140 = \ - a141 = a142 = a143 = a144 = a145 = a146 = a147 = a148 = a149 = a150 = \ - a151 = a152 = a153 = a154 = a155 = a156 = a157 = a158 = a159 = a160 = \ - a161 = a162 = a163 = a164 = a165 = a166 = a167 = a168 = a169 = a170 = \ - a171 = a172 = a173 = a174 = a175 = a176 = a177 = a178 = a179 = a180 = \ - a181 = a182 = a183 = a184 = a185 = a186 = a187 = a188 = a189 = a190 = \ - a191 = a192 = a193 = a194 = a195 = a196 = a197 = a198 = a199 = a200 = \ - a201 = a202 = a203 = a204 = a205 = a206 = a207 = a208 = a209 = a210 = \ - a211 = a212 = a213 = a214 = a215 = a216 = a217 = a218 = a219 = a220 = \ - a221 = a222 = a223 = a224 = a225 = a226 = a227 = a228 = a229 = a230 = \ - a231 = a232 = a233 = a234 = a235 = a236 = a237 = a238 = a239 = a240 = \ - a241 = a242 = a243 = a244 = a245 = a246 = a247 = a248 = a249 = a250 = \ - a251 = a252 = a253 = a254 = a255 = a256 = a257 = a258 = a259 = a260 = \ - a261 = a262 = a263 = a264 = a265 = a266 = a267 = a268 = a269 = a270 = \ - a271 = a272 = a273 = a274 = a275 = a276 = a277 = a278 = a279 = a280 = \ - a281 = a282 = a283 = a284 = a285 = a286 = a287 = a288 = a289 = a290 = \ - a291 = a292 = a293 = a294 = a295 = a296 = a297 = a298 = a299 = a300 = \ - a301 = a302 = a303 = a304 = a305 = a306 = a307 = a308 = a309 = a310 = \ - a311 = a312 = a313 = a314 = a315 = a316 = a317 = a318 = a319 = a320 = \ - a321 = a322 = a323 = a324 = a325 = a326 = a327 = a328 = a329 = a330 = \ - a331 = a332 = a333 = a334 = a335 = a336 = a337 = a338 = a339 = a340 = \ - a341 = a342 = a343 = a344 = a345 = a346 = a347 = a348 = a349 = a350 = \ - a351 = a352 = a353 = a354 = a355 = a356 = a357 = a358 = a359 = a360 = \ - a361 = a362 = a363 = a364 = a365 = a366 = a367 = a368 = a369 = a370 = \ - a371 = a372 = a373 = a374 = a375 = a376 = a377 = a378 = a379 = a380 = \ - a381 = a382 = a383 = a384 = a385 = a386 = a387 = a388 = a389 = a390 = \ - a391 = a392 = a393 = a394 = a395 = a396 = a397 = a398 = a399 = a400 = \ - a401 = a402 = a403 = a404 = a405 = a406 = a407 = a408 = a409 = a410 = \ - a411 = a412 = a413 = a414 = a415 = a416 = a417 = a418 = a419 = a420 = \ - a421 = a422 = a423 = a424 = a425 = a426 = a427 = a428 = a429 = a430 = \ - a431 = a432 = a433 = a434 = a435 = a436 = a437 = a438 = a439 = a440 = \ - a441 = a442 = a443 = a444 = a445 = a446 = a447 = a448 = a449 = a450 = \ - a451 = a452 = a453 = a454 = a455 = a456 = a457 = a458 = a459 = a460 = \ - a461 = a462 = a463 = a464 = a465 = a466 = a467 = a468 = a469 = a470 = \ - a471 = a472 = a473 = a474 = a475 = a476 = a477 = a478 = a479 = a480 = \ - a481 = a482 = a483 = a484 = a485 = a486 = a487 = a488 = a489 = a490 = \ - a491 = a492 = a493 = a494 = a495 = a496 = a497 = a498 = a499 = a500 = \ - a501 = a502 = a503 = a504 = a505 = a506 = a507 = a508 = a509 = a510 = \ - a511 = a512 = a513 = a514 = a515 = a516 = a517 = a518 = a519 = a520 = \ - a521 = a522 = a523 = a524 = a525 = a526 = a527 = a528 = a529 = a530 = \ - a531 = a532 = a533 = a534 = a535 = a536 = a537 = a538 = a539 = a540 = \ - a541 = a542 = a543 = a544 = a545 = a546 = a547 = a548 = a549 = a550 = \ - a551 = a552 = a553 = a554 = a555 = a556 = a557 = a558 = a559 = a560 = \ - a561 = a562 = a563 = a564 = a565 = a566 = a567 = a568 = a569 = a570 = \ - a571 = a572 = a573 = a574 = a575 = a576 = a577 = a578 = a579 = a580 = \ - a581 = a582 = a583 = a584 = a585 = a586 = a587 = a588 = a589 = a590 = \ - a591 = a592 = a593 = a594 = a595 = a596 = a597 = a598 = a599 = a600 = \ - a601 = a602 = a603 = a604 = a605 = a606 = a607 = a608 = a609 = a610 = \ - a611 = a612 = a613 = a614 = a615 = a616 = a617 = a618 = a619 = a620 = \ - a621 = a622 = a623 = a624 = a625 = a626 = a627 = a628 = a629 = a630 = \ - a631 = a632 = a633 = a634 = a635 = a636 = a637 = a638 = a639 = a640 = \ - a641 = a642 = a643 = a644 = a645 = a646 = a647 = a648 = a649 = a650 = \ - a651 = a652 = a653 = a654 = a655 = a656 = a657 = a658 = a659 = a660 = \ - a661 = a662 = a663 = a664 = a665 = a666 = a667 = a668 = a669 = a670 = \ - a671 = a672 = a673 = a674 = a675 = a676 = a677 = a678 = a679 = a680 = \ - a681 = a682 = a683 = a684 = a685 = a686 = a687 = a688 = a689 = a690 = \ - a691 = a692 = a693 = a694 = a695 = a696 = a697 = a698 = a699 = a700 = \ - a701 = a702 = a703 = a704 = a705 = a706 = a707 = a708 = a709 = a710 = \ - a711 = a712 = a713 = a714 = a715 = a716 = a717 = a718 = a719 = a720 = \ - a721 = a722 = a723 = a724 = a725 = a726 = a727 = a728 = a729 = a730 = \ - a731 = a732 = a733 = a734 = a735 = a736 = a737 = a738 = a739 = a740 = \ - a741 = a742 = a743 = a744 = a745 = a746 = a747 = a748 = a749 = a750 = \ - a751 = a752 = a753 = a754 = a755 = a756 = a757 = a758 = a759 = a760 = \ - a761 = a762 = a763 = a764 = a765 = a766 = a767 = a768 = a769 = a770 = \ - a771 = a772 = a773 = a774 = a775 = a776 = a777 = a778 = a779 = a780 = \ - a781 = a782 = a783 = a784 = a785 = a786 = a787 = a788 = a789 = a790 = \ - a791 = a792 = a793 = a794 = a795 = a796 = a797 = a798 = a799 = a800 \ - = None - print(a0) - - try: - f() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotRegex(err.getvalue(), r"NameError.*a1") - - def test_name_error_with_custom_exceptions(self): - def f(): - blech = None - raise NameError() - - try: - f() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("blech", err.getvalue()) - - def f(): - blech = None - raise NameError - - try: - f() - except NameError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("blech", err.getvalue()) - - def test_unbound_local_error_doesn_not_match(self): - def foo(): - something = 3 - print(somethong) - somethong = 3 - - try: - foo() - except UnboundLocalError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("something", err.getvalue()) - def test_issue45826(self): # regression test for bpo-45826 def f(): @@ -2076,6 +1809,8 @@ def f(): self.assertIn("nonsense", err.getvalue()) self.assertIn("ZeroDivisionError", err.getvalue()) + # Note: name suggestion tests live in `test_traceback`. + class AttributeErrorTests(unittest.TestCase): def test_attributes(self): @@ -2117,239 +1852,7 @@ def blech(self): self.assertEqual("bluch", exc.name) self.assertEqual(obj, exc.obj) - def test_getattr_suggestions(self): - class Substitution: - noise = more_noise = a = bc = None - blech = None - - class Elimination: - noise = more_noise = a = bc = None - blch = None - - class Addition: - noise = more_noise = a = bc = None - bluchin = None - - class SubstitutionOverElimination: - blach = None - bluc = None - - class SubstitutionOverAddition: - blach = None - bluchi = None - - class EliminationOverAddition: - blucha = None - bluc = None - - for cls, suggestion in [(Substitution, "'blech'?"), - (Elimination, "'blch'?"), - (Addition, "'bluchin'?"), - (EliminationOverAddition, "'bluc'?"), - (SubstitutionOverElimination, "'blach'?"), - (SubstitutionOverAddition, "'blach'?")]: - try: - cls().bluch - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertIn(suggestion, err.getvalue()) - - def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): - class A: - blech = None - - try: - A().somethingverywrong - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("blech", err.getvalue()) - - def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self): - class MyClass: - vvv = mom = w = id = pytho = None - - with self.subTest(name="b"): - try: - MyClass.b - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - with self.subTest(name="v"): - try: - MyClass.v - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - with self.subTest(name="m"): - try: - MyClass.m - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - with self.subTest(name="py"): - try: - MyClass.py - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - self.assertNotIn("you mean", err.getvalue()) - self.assertNotIn("vvv", err.getvalue()) - self.assertNotIn("mom", err.getvalue()) - self.assertNotIn("'id'", err.getvalue()) - self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'pytho'", err.getvalue()) - - - def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): - class A: - blech = None - # A class with a very big __dict__ will not be consider - # for suggestions. - for index in range(2000): - setattr(A, f"index_{index}", None) - - try: - A().bluch - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("blech", err.getvalue()) - - def test_getattr_suggestions_no_args(self): - class A: - blech = None - def __getattr__(self, attr): - raise AttributeError() - - try: - A().bluch - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertIn("blech", err.getvalue()) - - class A: - blech = None - def __getattr__(self, attr): - raise AttributeError - - try: - A().bluch - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertIn("blech", err.getvalue()) - - def test_getattr_suggestions_invalid_args(self): - class NonStringifyClass: - __str__ = None - __repr__ = None - - class A: - blech = None - def __getattr__(self, attr): - raise AttributeError(NonStringifyClass()) - - class B: - blech = None - def __getattr__(self, attr): - raise AttributeError("Error", 23) - - class C: - blech = None - def __getattr__(self, attr): - raise AttributeError(23) - - for cls in [A, B, C]: - try: - cls().bluch - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertIn("blech", err.getvalue()) - - def test_getattr_suggestions_for_same_name(self): - class A: - def __dir__(self): - return ['blech'] - try: - A().blech - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("Did you mean", err.getvalue()) - - def test_attribute_error_with_failing_dict(self): - class T: - bluch = 1 - def __dir__(self): - raise AttributeError("oh no!") - - try: - T().blich - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("blech", err.getvalue()) - self.assertNotIn("oh no!", err.getvalue()) - - def test_attribute_error_with_bad_name(self): - try: - raise AttributeError(name=12, obj=23) - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertNotIn("?", err.getvalue()) - - def test_attribute_error_inside_nested_getattr(self): - class A: - bluch = 1 - - class B: - def __getattribute__(self, attr): - a = A() - return a.blich - - try: - B().something - except AttributeError as exc: - with support.captured_stderr() as err: - sys.__excepthook__(*sys.exc_info()) - - self.assertIn("Did you mean", err.getvalue()) - self.assertIn("bluch", err.getvalue()) + # Note: name suggestion tests live in `test_traceback`. class ImportErrorTests(unittest.TestCase): diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2089050cadfc2c..4864b5c10b01bf 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -15,9 +15,11 @@ from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok, assert_python_failure +import json import textwrap import traceback from functools import partial +from pathlib import Path MODULE_PREFIX = f'{__name__}.' if __name__ == '__main__' else '' @@ -27,6 +29,9 @@ test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next', 'tb_lasti']) +LEVENSHTEIN_DATA_FILE = Path(__file__).parent / 'levenshtein_examples.json' + + class TracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. @@ -371,20 +376,36 @@ def test_signatures(self): '(exc, /, value=)') -@requires_debug_ranges() -class TracebackErrorLocationCaretTests(unittest.TestCase): - """ - Tests for printing code error expressions as part of PEP 657 - """ - def get_exception(self, callable): +class PurePythonExceptionFormattingMixin: + def get_exception(self, callable, slice_start=0, slice_end=-1): try: callable() self.fail("No exception thrown.") except: - return traceback.format_exc().splitlines()[:-1] + return traceback.format_exc().splitlines()[slice_start:slice_end] callable_line = get_exception.__code__.co_firstlineno + 2 + +class CAPIExceptionFormattingMixin: + def get_exception(self, callable, slice_start=0, slice_end=-1): + from _testcapi import exception_print + try: + callable() + self.fail("No exception thrown.") + except Exception as e: + with captured_output("stderr") as tbstderr: + exception_print(e) + return tbstderr.getvalue().splitlines()[slice_start:slice_end] + + callable_line = get_exception.__code__.co_firstlineno + 3 + + +@requires_debug_ranges() +class TracebackErrorLocationCaretTestBase: + """ + Tests for printing code error expressions as part of PEP 657 + """ def test_basic_caret(self): # NOTE: In caret tests, "if True:" is used as a way to force indicator # display, since the raising expression spans only part of the line. @@ -777,23 +798,29 @@ def f(): ] self.assertEqual(actual, expected) + +@requires_debug_ranges() +class PurePythonTracebackErrorCaretTests( + PurePythonExceptionFormattingMixin, + TracebackErrorLocationCaretTestBase, + unittest.TestCase, +): + """ + Same set of tests as above using the pure Python implementation of + traceback printing in traceback.py. + """ + + @cpython_only @requires_debug_ranges() -class CPythonTracebackErrorCaretTests(TracebackErrorLocationCaretTests): +class CPythonTracebackErrorCaretTests( + CAPIExceptionFormattingMixin, + TracebackErrorLocationCaretTestBase, + unittest.TestCase, +): """ Same set of tests as above but with Python's internal traceback printing. """ - def get_exception(self, callable): - from _testcapi import exception_print - try: - callable() - self.fail("No exception thrown.") - except Exception as e: - with captured_output("stderr") as tbstderr: - exception_print(e) - return tbstderr.getvalue().splitlines()[:-1] - - callable_line = get_exception.__code__.co_firstlineno + 3 class TracebackFormatTests(unittest.TestCase): @@ -2787,6 +2814,400 @@ def test_comparison(self): self.assertEqual(exc, ALWAYS_EQ) +global_for_suggestions = None + + +class SuggestionFormattingTestBase: + def get_suggestion(self, obj, attr_name=None): + if attr_name is not None: + def callable(): + getattr(obj, attr_name) + else: + callable = obj + + result_lines = self.get_exception( + callable, slice_start=-1, slice_end=None + ) + return result_lines[0] + + def test_getattr_suggestions(self): + class Substitution: + noise = more_noise = a = bc = None + blech = None + + class Elimination: + noise = more_noise = a = bc = None + blch = None + + class Addition: + noise = more_noise = a = bc = None + bluchin = None + + class SubstitutionOverElimination: + blach = None + bluc = None + + class SubstitutionOverAddition: + blach = None + bluchi = None + + class EliminationOverAddition: + blucha = None + bluc = None + + class CaseChangeOverSubstitution: + Luch = None + fluch = None + BLuch = None + + for cls, suggestion in [ + (Addition, "'bluchin'?"), + (Substitution, "'blech'?"), + (Elimination, "'blch'?"), + (Addition, "'bluchin'?"), + (SubstitutionOverElimination, "'blach'?"), + (SubstitutionOverAddition, "'blach'?"), + (EliminationOverAddition, "'bluc'?"), + (CaseChangeOverSubstitution, "'BLuch'?"), + ]: + actual = self.get_suggestion(cls(), 'bluch') + self.assertIn(suggestion, actual) + + def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): + class A: + blech = None + + actual = self.get_suggestion(A(), 'somethingverywrong') + self.assertNotIn("blech", actual) + + def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self): + class MyClass: + vvv = mom = w = id = pytho = None + + for name in ("b", "v", "m", "py"): + with self.subTest(name=name): + actual = self.get_suggestion(MyClass, name) + self.assertNotIn("you mean", actual) + self.assertNotIn("vvv", actual) + self.assertNotIn("mom", actual) + self.assertNotIn("'id'", actual) + self.assertNotIn("'w'", actual) + self.assertNotIn("'pytho'", actual) + + def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): + class A: + blech = None + # A class with a very big __dict__ will not be consider + # for suggestions. + for index in range(2000): + setattr(A, f"index_{index}", None) + + actual = self.get_suggestion(A(), 'bluch') + self.assertNotIn("blech", actual) + + def test_getattr_suggestions_no_args(self): + class A: + blech = None + def __getattr__(self, attr): + raise AttributeError() + + actual = self.get_suggestion(A(), 'bluch') + self.assertIn("blech", actual) + + class A: + blech = None + def __getattr__(self, attr): + raise AttributeError + + actual = self.get_suggestion(A(), 'bluch') + self.assertIn("blech", actual) + + def test_getattr_suggestions_invalid_args(self): + class NonStringifyClass: + __str__ = None + __repr__ = None + + class A: + blech = None + def __getattr__(self, attr): + raise AttributeError(NonStringifyClass()) + + class B: + blech = None + def __getattr__(self, attr): + raise AttributeError("Error", 23) + + class C: + blech = None + def __getattr__(self, attr): + raise AttributeError(23) + + for cls in [A, B, C]: + actual = self.get_suggestion(cls(), 'bluch') + self.assertIn("blech", actual) + + def test_getattr_suggestions_for_same_name(self): + class A: + def __dir__(self): + return ['blech'] + actual = self.get_suggestion(A(), 'blech') + self.assertNotIn("Did you mean", actual) + + def test_attribute_error_with_failing_dict(self): + class T: + bluch = 1 + def __dir__(self): + raise AttributeError("oh no!") + + actual = self.get_suggestion(T(), 'blich') + self.assertNotIn("blech", actual) + self.assertNotIn("oh no!", actual) + + def test_attribute_error_with_bad_name(self): + def raise_attribute_error_with_bad_name(): + raise AttributeError(name=12, obj=23) + + result_lines = self.get_exception( + raise_attribute_error_with_bad_name, slice_start=-1, slice_end=None + ) + self.assertNotIn("?", result_lines[-1]) + + def test_attribute_error_inside_nested_getattr(self): + class A: + bluch = 1 + + class B: + def __getattribute__(self, attr): + a = A() + return a.blich + + actual = self.get_suggestion(B(), 'something') + self.assertIn("Did you mean", actual) + self.assertIn("bluch", actual) + + def test_name_error_suggestions(self): + def Substitution(): + noise = more_noise = a = bc = None + blech = None + print(bluch) + + def Elimination(): + noise = more_noise = a = bc = None + blch = None + print(bluch) + + def Addition(): + noise = more_noise = a = bc = None + bluchin = None + print(bluch) + + def SubstitutionOverElimination(): + blach = None + bluc = None + print(bluch) + + def SubstitutionOverAddition(): + blach = None + bluchi = None + print(bluch) + + def EliminationOverAddition(): + blucha = None + bluc = None + print(bluch) + + for func, suggestion in [(Substitution, "'blech'?"), + (Elimination, "'blch'?"), + (Addition, "'bluchin'?"), + (EliminationOverAddition, "'blucha'?"), + (SubstitutionOverElimination, "'blach'?"), + (SubstitutionOverAddition, "'blach'?")]: + actual = self.get_suggestion(func) + self.assertIn(suggestion, actual) + + def test_name_error_suggestions_from_globals(self): + def func(): + print(global_for_suggestio) + actual = self.get_suggestion(func) + self.assertIn("'global_for_suggestions'?", actual) + + def test_name_error_suggestions_from_builtins(self): + def func(): + print(ZeroDivisionErrrrr) + actual = self.get_suggestion(func) + self.assertIn("'ZeroDivisionError'?", actual) + + def test_name_error_suggestions_do_not_trigger_for_long_names(self): + def func(): + somethingverywronghehehehehehe = None + print(somethingverywronghe) + actual = self.get_suggestion(func) + self.assertNotIn("somethingverywronghehe", actual) + + def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): + + def f_b(): + vvv = mom = w = id = pytho = None + b + + def f_v(): + vvv = mom = w = id = pytho = None + v + + def f_m(): + vvv = mom = w = id = pytho = None + m + + def f_py(): + vvv = mom = w = id = pytho = None + py + + for name, func in (("b", f_b), ("v", f_v), ("m", f_m), ("py", f_py)): + with self.subTest(name=name): + actual = self.get_suggestion(func) + self.assertNotIn("you mean", actual) + self.assertNotIn("vvv", actual) + self.assertNotIn("mom", actual) + self.assertNotIn("'id'", actual) + self.assertNotIn("'w'", actual) + self.assertNotIn("'pytho'", actual) + + def test_name_error_suggestions_do_not_trigger_for_too_many_locals(self): + def func(): + # Mutating locals() is unreliable, so we need to do it by hand + a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = a10 = \ + a11 = a12 = a13 = a14 = a15 = a16 = a17 = a18 = a19 = a20 = \ + a21 = a22 = a23 = a24 = a25 = a26 = a27 = a28 = a29 = a30 = \ + a31 = a32 = a33 = a34 = a35 = a36 = a37 = a38 = a39 = a40 = \ + a41 = a42 = a43 = a44 = a45 = a46 = a47 = a48 = a49 = a50 = \ + a51 = a52 = a53 = a54 = a55 = a56 = a57 = a58 = a59 = a60 = \ + a61 = a62 = a63 = a64 = a65 = a66 = a67 = a68 = a69 = a70 = \ + a71 = a72 = a73 = a74 = a75 = a76 = a77 = a78 = a79 = a80 = \ + a81 = a82 = a83 = a84 = a85 = a86 = a87 = a88 = a89 = a90 = \ + a91 = a92 = a93 = a94 = a95 = a96 = a97 = a98 = a99 = a100 = \ + a101 = a102 = a103 = a104 = a105 = a106 = a107 = a108 = a109 = a110 = \ + a111 = a112 = a113 = a114 = a115 = a116 = a117 = a118 = a119 = a120 = \ + a121 = a122 = a123 = a124 = a125 = a126 = a127 = a128 = a129 = a130 = \ + a131 = a132 = a133 = a134 = a135 = a136 = a137 = a138 = a139 = a140 = \ + a141 = a142 = a143 = a144 = a145 = a146 = a147 = a148 = a149 = a150 = \ + a151 = a152 = a153 = a154 = a155 = a156 = a157 = a158 = a159 = a160 = \ + a161 = a162 = a163 = a164 = a165 = a166 = a167 = a168 = a169 = a170 = \ + a171 = a172 = a173 = a174 = a175 = a176 = a177 = a178 = a179 = a180 = \ + a181 = a182 = a183 = a184 = a185 = a186 = a187 = a188 = a189 = a190 = \ + a191 = a192 = a193 = a194 = a195 = a196 = a197 = a198 = a199 = a200 = \ + a201 = a202 = a203 = a204 = a205 = a206 = a207 = a208 = a209 = a210 = \ + a211 = a212 = a213 = a214 = a215 = a216 = a217 = a218 = a219 = a220 = \ + a221 = a222 = a223 = a224 = a225 = a226 = a227 = a228 = a229 = a230 = \ + a231 = a232 = a233 = a234 = a235 = a236 = a237 = a238 = a239 = a240 = \ + a241 = a242 = a243 = a244 = a245 = a246 = a247 = a248 = a249 = a250 = \ + a251 = a252 = a253 = a254 = a255 = a256 = a257 = a258 = a259 = a260 = \ + a261 = a262 = a263 = a264 = a265 = a266 = a267 = a268 = a269 = a270 = \ + a271 = a272 = a273 = a274 = a275 = a276 = a277 = a278 = a279 = a280 = \ + a281 = a282 = a283 = a284 = a285 = a286 = a287 = a288 = a289 = a290 = \ + a291 = a292 = a293 = a294 = a295 = a296 = a297 = a298 = a299 = a300 = \ + a301 = a302 = a303 = a304 = a305 = a306 = a307 = a308 = a309 = a310 = \ + a311 = a312 = a313 = a314 = a315 = a316 = a317 = a318 = a319 = a320 = \ + a321 = a322 = a323 = a324 = a325 = a326 = a327 = a328 = a329 = a330 = \ + a331 = a332 = a333 = a334 = a335 = a336 = a337 = a338 = a339 = a340 = \ + a341 = a342 = a343 = a344 = a345 = a346 = a347 = a348 = a349 = a350 = \ + a351 = a352 = a353 = a354 = a355 = a356 = a357 = a358 = a359 = a360 = \ + a361 = a362 = a363 = a364 = a365 = a366 = a367 = a368 = a369 = a370 = \ + a371 = a372 = a373 = a374 = a375 = a376 = a377 = a378 = a379 = a380 = \ + a381 = a382 = a383 = a384 = a385 = a386 = a387 = a388 = a389 = a390 = \ + a391 = a392 = a393 = a394 = a395 = a396 = a397 = a398 = a399 = a400 = \ + a401 = a402 = a403 = a404 = a405 = a406 = a407 = a408 = a409 = a410 = \ + a411 = a412 = a413 = a414 = a415 = a416 = a417 = a418 = a419 = a420 = \ + a421 = a422 = a423 = a424 = a425 = a426 = a427 = a428 = a429 = a430 = \ + a431 = a432 = a433 = a434 = a435 = a436 = a437 = a438 = a439 = a440 = \ + a441 = a442 = a443 = a444 = a445 = a446 = a447 = a448 = a449 = a450 = \ + a451 = a452 = a453 = a454 = a455 = a456 = a457 = a458 = a459 = a460 = \ + a461 = a462 = a463 = a464 = a465 = a466 = a467 = a468 = a469 = a470 = \ + a471 = a472 = a473 = a474 = a475 = a476 = a477 = a478 = a479 = a480 = \ + a481 = a482 = a483 = a484 = a485 = a486 = a487 = a488 = a489 = a490 = \ + a491 = a492 = a493 = a494 = a495 = a496 = a497 = a498 = a499 = a500 = \ + a501 = a502 = a503 = a504 = a505 = a506 = a507 = a508 = a509 = a510 = \ + a511 = a512 = a513 = a514 = a515 = a516 = a517 = a518 = a519 = a520 = \ + a521 = a522 = a523 = a524 = a525 = a526 = a527 = a528 = a529 = a530 = \ + a531 = a532 = a533 = a534 = a535 = a536 = a537 = a538 = a539 = a540 = \ + a541 = a542 = a543 = a544 = a545 = a546 = a547 = a548 = a549 = a550 = \ + a551 = a552 = a553 = a554 = a555 = a556 = a557 = a558 = a559 = a560 = \ + a561 = a562 = a563 = a564 = a565 = a566 = a567 = a568 = a569 = a570 = \ + a571 = a572 = a573 = a574 = a575 = a576 = a577 = a578 = a579 = a580 = \ + a581 = a582 = a583 = a584 = a585 = a586 = a587 = a588 = a589 = a590 = \ + a591 = a592 = a593 = a594 = a595 = a596 = a597 = a598 = a599 = a600 = \ + a601 = a602 = a603 = a604 = a605 = a606 = a607 = a608 = a609 = a610 = \ + a611 = a612 = a613 = a614 = a615 = a616 = a617 = a618 = a619 = a620 = \ + a621 = a622 = a623 = a624 = a625 = a626 = a627 = a628 = a629 = a630 = \ + a631 = a632 = a633 = a634 = a635 = a636 = a637 = a638 = a639 = a640 = \ + a641 = a642 = a643 = a644 = a645 = a646 = a647 = a648 = a649 = a650 = \ + a651 = a652 = a653 = a654 = a655 = a656 = a657 = a658 = a659 = a660 = \ + a661 = a662 = a663 = a664 = a665 = a666 = a667 = a668 = a669 = a670 = \ + a671 = a672 = a673 = a674 = a675 = a676 = a677 = a678 = a679 = a680 = \ + a681 = a682 = a683 = a684 = a685 = a686 = a687 = a688 = a689 = a690 = \ + a691 = a692 = a693 = a694 = a695 = a696 = a697 = a698 = a699 = a700 = \ + a701 = a702 = a703 = a704 = a705 = a706 = a707 = a708 = a709 = a710 = \ + a711 = a712 = a713 = a714 = a715 = a716 = a717 = a718 = a719 = a720 = \ + a721 = a722 = a723 = a724 = a725 = a726 = a727 = a728 = a729 = a730 = \ + a731 = a732 = a733 = a734 = a735 = a736 = a737 = a738 = a739 = a740 = \ + a741 = a742 = a743 = a744 = a745 = a746 = a747 = a748 = a749 = a750 = \ + a751 = a752 = a753 = a754 = a755 = a756 = a757 = a758 = a759 = a760 = \ + a761 = a762 = a763 = a764 = a765 = a766 = a767 = a768 = a769 = a770 = \ + a771 = a772 = a773 = a774 = a775 = a776 = a777 = a778 = a779 = a780 = \ + a781 = a782 = a783 = a784 = a785 = a786 = a787 = a788 = a789 = a790 = \ + a791 = a792 = a793 = a794 = a795 = a796 = a797 = a798 = a799 = a800 \ + = None + print(a0) + + actual = self.get_suggestion(func) + self.assertNotRegex(actual, r"NameError.*a1") + + def test_name_error_with_custom_exceptions(self): + def func(): + blech = None + raise NameError() + + actual = self.get_suggestion(func) + self.assertNotIn("blech", actual) + + def func(): + blech = None + raise NameError + + actual = self.get_suggestion(func) + self.assertNotIn("blech", actual) + + def test_unbound_local_error_does_not_match(self): + def func(): + something = 3 + print(somethong) + somethong = 3 + + actual = self.get_suggestion(func) + self.assertNotIn("something", actual) + + +class PurePythonSuggestionFormattingTests( + PurePythonExceptionFormattingMixin, + SuggestionFormattingTestBase, + unittest.TestCase, +): + """ + Same set of tests as above using the pure Python implementation of + traceback printing in traceback.py. + """ + + +@cpython_only +class CPythonSuggestionFormattingTests( + CAPIExceptionFormattingMixin, + SuggestionFormattingTestBase, + unittest.TestCase, +): + """ + Same set of tests as above but with Python's internal traceback printing. + """ + + class MiscTest(unittest.TestCase): def test_all(self): @@ -2800,6 +3221,59 @@ def test_all(self): expected.add(name) self.assertCountEqual(traceback.__all__, expected) + def test_levenshtein_distance(self): + # copied from _testinternalcapi.test_edit_cost + # to also exercise the Python implementation + + def CHECK(a, b, expected): + actual = traceback._levenshtein_distance(a, b, 4044) + self.assertEqual(actual, expected) + + CHECK("", "", 0) + CHECK("", "a", 2) + CHECK("a", "A", 1) + CHECK("Apple", "Aple", 2) + CHECK("Banana", "B@n@n@", 6) + CHECK("Cherry", "Cherry!", 2) + CHECK("---0---", "------", 2) + CHECK("abc", "y", 6) + CHECK("aa", "bb", 4) + CHECK("aaaaa", "AAAAA", 5) + CHECK("wxyz", "wXyZ", 2) + CHECK("wxyz", "wXyZ123", 8) + CHECK("Python", "Java", 12) + CHECK("Java", "C#", 8) + CHECK("AbstractFoobarManager", "abstract_foobar_manager", 3+2*2) + CHECK("CPython", "PyPy", 10) + CHECK("CPython", "pypy", 11) + CHECK("AttributeError", "AttributeErrop", 2) + CHECK("AttributeError", "AttributeErrorTests", 10) + CHECK("ABA", "AAB", 4) + + def test_levenshtein_distance_short_circuit(self): + if not LEVENSHTEIN_DATA_FILE.is_file(): + self.fail( + f"{LEVENSHTEIN_DATA_FILE} is missing." + f" Run `make regen-test-levenshtein`" + ) + + with LEVENSHTEIN_DATA_FILE.open("r") as f: + examples = json.load(f) + for a, b, expected in examples: + res1 = traceback._levenshtein_distance(a, b, 1000) + self.assertEqual(res1, expected, msg=(a, b)) + + for threshold in [expected, expected + 1, expected + 2]: + # big enough thresholds shouldn't change the result + res2 = traceback._levenshtein_distance(a, b, threshold) + self.assertEqual(res2, expected, msg=(a, b, threshold)) + + for threshold in range(expected): + # for small thresholds, the only piece of information + # we receive is "strings not close enough". + res3 = traceback._levenshtein_distance(a, b, threshold) + self.assertGreater(res3, threshold, msg=(a, b, threshold)) + if __name__ == "__main__": unittest.main() diff --git a/Lib/traceback.py b/Lib/traceback.py index b1a5fd0a26d40a..c46ddaf51a00d4 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -707,6 +707,11 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, self.offset = exc_value.offset self.end_offset = exc_value.end_offset self.msg = exc_value.msg + elif exc_type and issubclass(exc_type, (NameError, AttributeError)) and \ + getattr(exc_value, "name", None) is not None: + suggestion = _compute_suggestion_error(exc_value, exc_traceback) + if suggestion: + self._str += f". Did you mean: '{suggestion}'?" if lookup_lines: self._load_lines() self.__suppress_context__ = \ @@ -977,3 +982,125 @@ def print(self, *, file=None, chain=True): file = sys.stderr for line in self.format(chain=chain): print(line, file=file, end="") + + +_MAX_CANDIDATE_ITEMS = 750 +_MAX_STRING_SIZE = 40 +_MOVE_COST = 2 +_CASE_COST = 1 + + +def _substitution_cost(ch_a, ch_b): + if ch_a == ch_b: + return 0 + if ch_a.lower() == ch_b.lower(): + return _CASE_COST + return _MOVE_COST + + +def _compute_suggestion_error(exc_value, tb): + wrong_name = getattr(exc_value, "name", None) + if wrong_name is None or not isinstance(wrong_name, str): + return None + if isinstance(exc_value, AttributeError): + obj = exc_value.obj + try: + d = dir(obj) + except Exception: + return None + else: + assert isinstance(exc_value, NameError) + # find most recent frame + if tb is None: + return None + while tb.tb_next is not None: + tb = tb.tb_next + frame = tb.tb_frame + d = ( + list(frame.f_locals) + + list(frame.f_globals) + + list(frame.f_globals['__builtins__']) + ) + if len(d) > _MAX_CANDIDATE_ITEMS: + return None + wrong_name_len = len(wrong_name) + if wrong_name_len > _MAX_STRING_SIZE: + return None + best_distance = wrong_name_len + suggestion = None + for possible_name in d: + if possible_name == wrong_name: + # A missing attribute is "found". Don't suggest it (see GH-88821). + continue + # No more than 1/3 of the involved characters should need changed. + max_distance = (len(possible_name) + wrong_name_len + 3) * _MOVE_COST // 6 + # Don't take matches we've already beaten. + max_distance = min(max_distance, best_distance - 1) + current_distance = _levenshtein_distance(wrong_name, possible_name, max_distance) + if current_distance > max_distance: + continue + if not suggestion or current_distance < best_distance: + suggestion = possible_name + best_distance = current_distance + return suggestion + + +def _levenshtein_distance(a, b, max_cost): + # A Python implementation of Python/suggestions.c:levenshtein_distance. + + # Both strings are the same + if a == b: + return 0 + + # Trim away common affixes + pre = 0 + while a[pre:] and b[pre:] and a[pre] == b[pre]: + pre += 1 + a = a[pre:] + b = b[pre:] + post = 0 + while a[:post or None] and b[:post or None] and a[post-1] == b[post-1]: + post -= 1 + a = a[:post or None] + b = b[:post or None] + if not a or not b: + return _MOVE_COST * (len(a) + len(b)) + if len(a) > _MAX_STRING_SIZE or len(b) > _MAX_STRING_SIZE: + return max_cost + 1 + + # Prefer shorter buffer + if len(b) < len(a): + a, b = b, a + + # Quick fail when a match is impossible + if (len(b) - len(a)) * _MOVE_COST > max_cost: + return max_cost + 1 + + # Instead of producing the whole traditional len(a)-by-len(b) + # matrix, we can update just one row in place. + # Initialize the buffer row + row = list(range(_MOVE_COST, _MOVE_COST * (len(a) + 1), _MOVE_COST)) + + result = 0 + for bindex in range(len(b)): + bchar = b[bindex] + distance = result = bindex * _MOVE_COST + minimum = sys.maxsize + for index in range(len(a)): + # 1) Previous distance in this row is cost(b[:b_index], a[:index]) + substitute = distance + _substitution_cost(bchar, a[index]) + # 2) cost(b[:b_index], a[:index+1]) from previous row + distance = row[index] + # 3) existing result is cost(b[:b_index+1], a[index]) + + insert_delete = min(result, distance) + _MOVE_COST + result = min(insert_delete, substitute) + + # cost(b[:b_index+1], a[:index+1]) + row[index] = result + if result < minimum: + minimum = result + if minimum > max_cost: + # Everything in this row is too big, so bail early. + return max_cost + 1 + return result diff --git a/Makefile.pre.in b/Makefile.pre.in index 01578306bd5ff5..11118354f15def 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -958,6 +958,11 @@ regen-test-frozenmain: $(BUILDPYTHON) # using Programs/freeze_test_frozenmain.py $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Programs/freeze_test_frozenmain.py Programs/test_frozenmain.h +.PHONY: regen-test-levenshtein +regen-test-levenshtein: + # Regenerate Lib/test/levenshtein_examples.json + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/scripts/generate_levenshtein_examples.py Lib/test/levenshtein_examples.json + .PHONY: regen-re regen-re: $(BUILDPYTHON) # Regenerate Lib/re/_casefix.py @@ -1223,7 +1228,7 @@ regen-limited-abi: all regen-all: regen-opcode regen-opcode-targets regen-typeslots \ regen-token regen-ast regen-keyword regen-sre regen-frozen clinic \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ - regen-global-objects + regen-test-levenshtein regen-global-objects @echo @echo "Note: make regen-stdlib-module-names and make regen-configure should be run manually" diff --git a/Misc/NEWS.d/next/Library/2022-10-04-00-43-43.gh-issue-97008.3rjtt6.rst b/Misc/NEWS.d/next/Library/2022-10-04-00-43-43.gh-issue-97008.3rjtt6.rst new file mode 100644 index 00000000000000..b41f88d07890d1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-04-00-43-43.gh-issue-97008.3rjtt6.rst @@ -0,0 +1,5 @@ +:exc:`NameError` and :exc:`AttributeError` spelling suggestions provided +since :gh:`82711` are now also emitted by the pure Python +:mod:`traceback` module. Tests for those suggestions now exercise both +implementations to ensure they are equivalent. Patch by Carl Friedrich +Bolz-Tereick and Łukasz Langa. diff --git a/Tools/scripts/generate_levenshtein_examples.py b/Tools/scripts/generate_levenshtein_examples.py new file mode 100644 index 00000000000000..5a8360fff7315a --- /dev/null +++ b/Tools/scripts/generate_levenshtein_examples.py @@ -0,0 +1,70 @@ +"""Generate 10,000 unique examples for the Levenshtein short-circuit tests.""" + +import argparse +from functools import cache +import json +import os.path +from random import choices, randrange + + +# This should be in sync with Lib/traceback.py. It's not importing those values +# because this script is being executed by PYTHON_FOR_REGEN and not by the in-tree +# build of Python. +_MOVE_COST = 2 +_CASE_COST = 1 + + +def _substitution_cost(ch_a, ch_b): + if ch_a == ch_b: + return 0 + if ch_a.lower() == ch_b.lower(): + return _CASE_COST + return _MOVE_COST + + +@cache +def levenshtein(a, b): + if not a or not b: + return (len(a) + len(b)) * _MOVE_COST + option1 = levenshtein(a[:-1], b[:-1]) + _substitution_cost(a[-1], b[-1]) + option2 = levenshtein(a[:-1], b) + _MOVE_COST + option3 = levenshtein(a, b[:-1]) + _MOVE_COST + return min(option1, option2, option3) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('output_path', metavar='FILE', type=str) + parser.add_argument('--overwrite', dest='overwrite', action='store_const', + const=True, default=False, + help='overwrite an existing test file') + + args = parser.parse_args() + output_path = os.path.realpath(args.output_path) + if not args.overwrite and os.path.isfile(output_path): + print(f"{output_path} already exists, skipping regeneration.") + print( + "To force, add --overwrite to the invocation of this tool or" + " delete the existing file." + ) + return + + examples = set() + # Create a lot of non-empty examples, which should end up with a Gauss-like + # distribution for even costs (moves) and odd costs (case substitutions). + while len(examples) < 9990: + a = ''.join(choices("abcABC", k=randrange(1, 10))) + b = ''.join(choices("abcABC", k=randrange(1, 10))) + expected = levenshtein(a, b) + examples.add((a, b, expected)) + # Create one empty case each for strings between 0 and 9 in length. + for i in range(10): + b = ''.join(choices("abcABC", k=i)) + expected = levenshtein("", b) + examples.add(("", b, expected)) + with open(output_path, "w") as f: + json.dump(sorted(examples), f, indent=2) + + +if __name__ == "__main__": + main() From 75af114ffa0f83b98d4304f5cb263acc517e76c9 Mon Sep 17 00:00:00 2001 From: Ev2geny Date: Wed, 5 Oct 2022 00:37:33 +0200 Subject: [PATCH 015/151] gh-58451: Add optional delete_on_close parameter to NamedTemporaryFile (GH-97015) --- Doc/library/tempfile.rst | 85 ++++++++++++--- Doc/whatsnew/3.12.rst | 5 + Lib/tempfile.py | 76 ++++++------- Lib/test/test_tempfile.py | 100 +++++++++++++++++- .../2020-09-28-04-56-04.bpo-14243.YECnxv.rst | 1 + 5 files changed, 216 insertions(+), 51 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-09-28-04-56-04.bpo-14243.YECnxv.rst diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index b7e604c1b70acb..b6d4f5dd05bbfc 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -75,20 +75,61 @@ The module defines the following user-callable items: Added *errors* parameter. -.. function:: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, *, errors=None) - - This function operates exactly as :func:`TemporaryFile` does, except that - the file is guaranteed to have a visible name in the file system (on - Unix, the directory entry is not unlinked). That name can be retrieved - from the :attr:`name` attribute of the returned - file-like object. Whether the name can be - used to open the file a second time, while the named temporary file is - still open, varies across platforms (it can be so used on Unix; it cannot - on Windows). If *delete* is true (the default), the file is - deleted as soon as it is closed. - The returned object is always a file-like object whose :attr:`!file` - attribute is the underlying true file object. This file-like object can - be used in a :keyword:`with` statement, just like a normal file. +.. function:: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, *, errors=None, delete_on_close=True) + + This function operates exactly as :func:`TemporaryFile` does, except the + following differences: + + * This function returns a file that is guaranteed to have a visible name in + the file system. + * To manage the named file, it extends the parameters of + :func:`TemporaryFile` with *delete* and *delete_on_close* parameters that + determine whether and how the named file should be automatically deleted. + + The returned object is always a :term:`file-like object` whose :attr:`!file` + attribute is the underlying true file object. This :term:`file-like object` + can be used in a :keyword:`with` statement, just like a normal file. The + name of the temporary file can be retrieved from the :attr:`name` attribute + of the returned file-like object. On Unix, unlike with the + :func:`TemporaryFile`, the directory entry does not get unlinked immediately + after the file creation. + + If *delete* is true (the default) and *delete_on_close* is true (the + default), the file is deleted as soon as it is closed. If *delete* is true + and *delete_on_close* is false, the file is deleted on context manager exit + only, or else when the :term:`file-like object` is finalized. Deletion is not + always guaranteed in this case (see :meth:`object.__del__`). If *delete* is + false, the value of *delete_on_close* is ignored. + + Therefore to use the name of the temporary file to reopen the file after + closing it, either make sure not to delete the file upon closure (set the + *delete* parameter to be false) or, in case the temporary file is created in + a :keyword:`with` statement, set the *delete_on_close* parameter to be false. + The latter approach is recommended as it provides assistance in automatic + cleaning of the temporary file upon the context manager exit. + + Opening the temporary file again by its name while it is still open works as + follows: + + * On POSIX the file can always be opened again. + * On Windows, make sure that at least one of the following conditions are + fulfilled: + + * *delete* is false + * additional open shares delete access (e.g. by calling :func:`os.open` + with the flag ``O_TEMPORARY``) + * *delete* is true but *delete_on_close* is false. Note, that in this + case the additional opens that do not share delete access (e.g. + created via builtin :func:`open`) must be closed before exiting the + context manager, else the :func:`os.unlink` call on context manager + exit will fail with a :exc:`PermissionError`. + + On Windows, if *delete_on_close* is false, and the file is created in a + directory for which the user lacks delete access, then the :func:`os.unlink` + call on exit of the context manager will fail with a :exc:`PermissionError`. + This cannot happen when *delete_on_close* is true because delete access is + requested by the open, which fails immediately if the requested access is not + granted. On POSIX (only), a process that is terminated abruptly with SIGKILL cannot automatically delete any NamedTemporaryFiles it created. @@ -98,6 +139,9 @@ The module defines the following user-callable items: .. versionchanged:: 3.8 Added *errors* parameter. + .. versionchanged:: 3.12 + Added *delete_on_close* parameter. + .. class:: SpooledTemporaryFile(max_size=0, mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, *, errors=None) @@ -346,6 +390,19 @@ Here are some examples of typical usage of the :mod:`tempfile` module:: >>> # file is now closed and removed + # create a temporary file using a context manager + # close the file, use the name to open the file again + >>> with tempfile.TemporaryFile(delete_on_close=False) as fp: + ... fp.write(b'Hello world!') + ... fp.close() + # the file is closed, but not removed + # open the file again by using its name + ... with open(fp.name) as f + ... f.read() + b'Hello world!' + >>> + # file is now removed + # create a temporary directory using the context manager >>> with tempfile.TemporaryDirectory() as tmpdirname: ... print('created temporary directory', tmpdirname) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 39ae2518bbdde1..052507a4873f81 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -148,6 +148,11 @@ unicodedata * The Unicode database has been updated to version 15.0.0. (Contributed by Benjamin Peterson in :gh:`96734`). +tempfile +-------- + +The :class:`tempfile.NamedTemporaryFile` function has a new optional parameter +*delete_on_close* (Contributed by Evgeny Zorin in :gh:`58451`.) Optimizations ============= diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 480c17232e9f09..bb18d60db0d919 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -418,42 +418,42 @@ class _TemporaryFileCloser: underlying file object, without adding a __del__ method to the temporary file.""" - file = None # Set here since __del__ checks it + cleanup_called = False close_called = False - def __init__(self, file, name, delete=True): + def __init__(self, file, name, delete=True, delete_on_close=True): self.file = file self.name = name self.delete = delete + self.delete_on_close = delete_on_close - # NT provides delete-on-close as a primitive, so we don't need - # the wrapper to do anything special. We still use it so that - # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile. - if _os.name != 'nt': - # Cache the unlinker so we don't get spurious errors at - # shutdown when the module-level "os" is None'd out. Note - # that this must be referenced as self.unlink, because the - # name TemporaryFileWrapper may also get None'd out before - # __del__ is called. - - def close(self, unlink=_os.unlink): - if not self.close_called and self.file is not None: - self.close_called = True - try: + def cleanup(self, windows=(_os.name == 'nt'), unlink=_os.unlink): + if not self.cleanup_called: + self.cleanup_called = True + try: + if not self.close_called: + self.close_called = True self.file.close() - finally: - if self.delete: + finally: + # Windows provides delete-on-close as a primitive, in which + # case the file was deleted by self.file.close(). + if self.delete and not (windows and self.delete_on_close): + try: unlink(self.name) + except FileNotFoundError: + pass - # Need to ensure the file is deleted on __del__ - def __del__(self): - self.close() - - else: - def close(self): - if not self.close_called: - self.close_called = True + def close(self): + if not self.close_called: + self.close_called = True + try: self.file.close() + finally: + if self.delete and self.delete_on_close: + self.cleanup() + + def __del__(self): + self.cleanup() class _TemporaryFileWrapper: @@ -464,11 +464,11 @@ class _TemporaryFileWrapper: remove the file when it is no longer needed. """ - def __init__(self, file, name, delete=True): + def __init__(self, file, name, delete=True, delete_on_close=True): self.file = file self.name = name - self.delete = delete - self._closer = _TemporaryFileCloser(file, name, delete) + self._closer = _TemporaryFileCloser(file, name, delete, + delete_on_close) def __getattr__(self, name): # Attribute lookups are delegated to the underlying file @@ -499,7 +499,7 @@ def __enter__(self): # deleted when used in a with statement def __exit__(self, exc, value, tb): result = self.file.__exit__(exc, value, tb) - self.close() + self._closer.cleanup() return result def close(self): @@ -518,10 +518,10 @@ def __iter__(self): for line in self.file: yield line - def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, - dir=None, delete=True, *, errors=None): + dir=None, delete=True, *, errors=None, + delete_on_close=True): """Create and return a temporary file. Arguments: 'prefix', 'suffix', 'dir' -- as for mkstemp. @@ -529,7 +529,10 @@ def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, 'buffering' -- the buffer size argument to io.open (default -1). 'encoding' -- the encoding argument to io.open (default None) 'newline' -- the newline argument to io.open (default None) - 'delete' -- whether the file is deleted on close (default True). + 'delete' -- whether the file is automatically deleted (default True). + 'delete_on_close' -- if 'delete', whether the file is deleted on close + (default True) or otherwise either on context manager exit + (if context manager was used) or on object finalization. . 'errors' -- the errors argument to io.open (default None) The file is created as mkstemp() would do it. @@ -548,7 +551,7 @@ def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, # Setting O_TEMPORARY in the flags causes the OS to delete # the file when it is closed. This is only supported by Windows. - if _os.name == 'nt' and delete: + if _os.name == 'nt' and delete and delete_on_close: flags |= _os.O_TEMPORARY if "b" not in mode: @@ -567,12 +570,13 @@ def opener(*args): raw = getattr(file, 'buffer', file) raw = getattr(raw, 'raw', raw) raw.name = name - return _TemporaryFileWrapper(file, name, delete) + return _TemporaryFileWrapper(file, name, delete, delete_on_close) except: file.close() raise except: - if name is not None and not (_os.name == 'nt' and delete): + if name is not None and not ( + _os.name == 'nt' and delete and delete_on_close): _os.unlink(name) raise diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index ccf7ea072de276..7c2c8de7a2e6fc 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -11,6 +11,7 @@ import stat import types import weakref +import gc from unittest import mock import unittest @@ -1013,6 +1014,102 @@ def use_closed(): pass self.assertRaises(ValueError, use_closed) + def test_context_man_not_del_on_close_if_delete_on_close_false(self): + # Issue gh-58451: tempfile.NamedTemporaryFile is not particulary useful + # on Windows + # A NamedTemporaryFile is NOT deleted when closed if + # delete_on_close=False, but is deleted on context manager exit + dir = tempfile.mkdtemp() + try: + with tempfile.NamedTemporaryFile(dir=dir, + delete=True, + delete_on_close=False) as f: + f.write(b'blat') + f_name = f.name + f.close() + with self.subTest(): + # Testing that file is not deleted on close + self.assertTrue(os.path.exists(f.name), + f"NamedTemporaryFile {f.name!r} is incorrectly " + f"deleted on closure when delete_on_close=False") + + with self.subTest(): + # Testing that file is deleted on context manager exit + self.assertFalse(os.path.exists(f.name), + f"NamedTemporaryFile {f.name!r} exists " + f"after context manager exit") + + finally: + os.rmdir(dir) + + def test_context_man_ok_to_delete_manually(self): + # In the case of delete=True, a NamedTemporaryFile can be manually + # deleted in a with-statement context without causing an error. + dir = tempfile.mkdtemp() + try: + with tempfile.NamedTemporaryFile(dir=dir, + delete=True, + delete_on_close=False) as f: + f.write(b'blat') + f.close() + os.unlink(f.name) + + finally: + os.rmdir(dir) + + def test_context_man_not_del_if_delete_false(self): + # A NamedTemporaryFile is not deleted if delete = False + dir = tempfile.mkdtemp() + f_name = "" + try: + # Test that delete_on_close=True has no effect if delete=False. + with tempfile.NamedTemporaryFile(dir=dir, delete=False, + delete_on_close=True) as f: + f.write(b'blat') + f_name = f.name + self.assertTrue(os.path.exists(f.name), + f"NamedTemporaryFile {f.name!r} exists after close") + finally: + os.unlink(f_name) + os.rmdir(dir) + + def test_del_by_finalizer(self): + # A NamedTemporaryFile is deleted when finalized in the case of + # delete=True, delete_on_close=False, and no with-statement is used. + def my_func(dir): + f = tempfile.NamedTemporaryFile(dir=dir, delete=True, + delete_on_close=False) + tmp_name = f.name + f.write(b'blat') + # Testing extreme case, where the file is not explicitly closed + # f.close() + return tmp_name + # Make sure that the garbage collector has finalized the file object. + gc.collect() + dir = tempfile.mkdtemp() + try: + tmp_name = my_func(dir) + self.assertFalse(os.path.exists(tmp_name), + f"NamedTemporaryFile {tmp_name!r} " + f"exists after finalizer ") + finally: + os.rmdir(dir) + + def test_correct_finalizer_work_if_already_deleted(self): + # There should be no error in the case of delete=True, + # delete_on_close=False, no with-statement is used, and the file is + # deleted manually. + def my_func(dir)->str: + f = tempfile.NamedTemporaryFile(dir=dir, delete=True, + delete_on_close=False) + tmp_name = f.name + f.write(b'blat') + f.close() + os.unlink(tmp_name) + return tmp_name + # Make sure that the garbage collector has finalized the file object. + gc.collect() + def test_bad_mode(self): dir = tempfile.mkdtemp() self.addCleanup(os_helper.rmtree, dir) @@ -1081,7 +1178,8 @@ def test_iobase_interface(self): missing_attrs = iobase_attrs - spooledtempfile_attrs self.assertFalse( missing_attrs, - 'SpooledTemporaryFile missing attributes from IOBase/BufferedIOBase/TextIOBase' + 'SpooledTemporaryFile missing attributes from ' + 'IOBase/BufferedIOBase/TextIOBase' ) def test_del_on_close(self): diff --git a/Misc/NEWS.d/next/Library/2020-09-28-04-56-04.bpo-14243.YECnxv.rst b/Misc/NEWS.d/next/Library/2020-09-28-04-56-04.bpo-14243.YECnxv.rst new file mode 100644 index 00000000000000..267535452ef146 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-28-04-56-04.bpo-14243.YECnxv.rst @@ -0,0 +1 @@ +The :class:`tempfile.NamedTemporaryFile` function has a new optional parameter *delete_on_close* From 937cc6dd923a348f09b967c582d80856ad2aefb4 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 4 Oct 2022 17:50:34 -0500 Subject: [PATCH 016/151] gh-95913: Move py.exe to appropriate What's New section & refine text (#97718) * Move Windows py.exe improvements from Typing section to New Features * Add ref target label and use literal for py.exe * Be clearer/explict about what legacy version arg components reprisent * Apply other minor clarity and textual fixes to py.exe launcher text * Refine phrasing of legacy sentence of py.exe desc Co-authored-by: Ezio Melotti Co-authored-by: Ezio Melotti --- Doc/whatsnew/3.11.rst | 48 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 0d38abfc000597..c7233aab71e6f1 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -207,6 +207,32 @@ See :pep:`678` for more details. PEP written by Zac Hatfield-Dodds.) +.. _whatsnew311-windows-launcher: + +Windows ``py.exe`` launcher improvements +---------------------------------------- + +The copy of the :ref:`launcher` included with Python 3.11 has been significantly +updated. It now supports company/tag syntax as defined in :pep:`514` using the +``-V:/`` argument instead of the limited ``-.``. +This allows launching distributions other than ``PythonCore``, +the one hosted on `python.org `_. + +When using ``-V:`` selectors, either company or tag can be omitted, but all +installs will be searched. For example, ``-V:OtherPython/`` will select the +"best" tag registered for ``OtherPython``, while ``-V:3.11`` or ``-V:/3.11`` +will select the "best" distribution with tag ``3.11``. + +When using the legacy ``-``, ``-.``, +``--`` or ``-.-`` arguments, +all existing behaviour should be preserved from past versions, +and only releases from ``PythonCore`` will be selected. +However, the ``-64`` suffix now implies "not 32-bit" (not necessarily x86-64), +as there are multiple supported 64-bit platforms. +32-bit runtimes are detected by checking the runtime's tag for a ``-32`` suffix. +All releases of Python since 3.5 have included this in their 32-bit builds. + + .. _new-feat-related-type-hints-311: .. _whatsnew311-typing-features: @@ -401,28 +427,6 @@ See `this message from the Steering Council /`` argument instead of the limited ``-x.y`` argument. This -allows launching distributions other than ``PythonCore``, which is the one -obtained from `python.org `_. - -When using ``-V:`` selectors, either company or tag can be omitted, but all -installs will be searched. For example, ``-V:OtherPython/`` will select the -"best" tag registered for ``OtherPython``, while ``-V:3.11`` or ``-V:/3.11`` -will select the "best" distribution with tag ``3.11``. - -When using legacy ``-x``, ``-x.y``, ``-x-ZZ`` or ``-x.y-ZZ`` arguments, all -existing behaviour should be preserved from past versions. Only releases from -``PythonCore`` will be selected. However, the ``-64`` suffix now implies "not -32-bit", as there are multiple supported 64-bit platforms. 32-bit runtimes are -detected by checking its tag for a ``-32`` suffix. All releases of Python -since 3.5 have included this in their 32-bit builds. - - Other Language Changes ====================== From 1fce50558f6dfe06e11115ec7951d6fb0117a424 Mon Sep 17 00:00:00 2001 From: cousteau Date: Tue, 4 Oct 2022 23:54:03 +0100 Subject: [PATCH 017/151] gh-88355: Fix backslashes in AF_PIPE (#96543) Fix backslashes in AF_PIPE (#88355) The correct syntax for AF_PIPE addresses is `\\.\pipe\blahblah`, not `\.\pipe{blahblah}`, but the syntax markup messed up the backslashes. --- Doc/library/multiprocessing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 6f6e7881598b88..dab115acdc207c 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -2629,9 +2629,9 @@ Address Formats filesystem. * An ``'AF_PIPE'`` address is a string of the form - :samp:`r'\\\\.\\pipe\\{PipeName}'`. To use :func:`Client` to connect to a named + :samp:`r'\\\\\\.\\pipe\\\\{PipeName}'`. To use :func:`Client` to connect to a named pipe on a remote computer called *ServerName* one should use an address of the - form :samp:`r'\\\\{ServerName}\\pipe\\{PipeName}'` instead. + form :samp:`r'\\\\\\\\{ServerName}\\pipe\\\\{PipeName}'` instead. Note that any string beginning with two backslashes is assumed by default to be an ``'AF_PIPE'`` address rather than an ``'AF_UNIX'`` address. From bb89f150ab36c96db19183356b96ff32e29acba6 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:10:57 +0100 Subject: [PATCH 018/151] gh-93738: Documentation C syntax (:c:type:`Py_UNICODE*` -> :c:expr:`Py_UNICODE*`) (#97784) :c:type:`Py_UNICODE*` -> :c:expr:`Py_UNICODE*` --- Doc/whatsnew/3.3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 2d78f81798f283..609370bad274b6 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -2267,7 +2267,7 @@ The :c:type:`Py_UNICODE` has been deprecated by :pep:`393` and will be removed in Python 4. All functions using this type are deprecated: Unicode functions and methods using :c:type:`Py_UNICODE` and -:c:type:`Py_UNICODE*` types: +:c:expr:`Py_UNICODE*` types: * :c:macro:`PyUnicode_FromUnicode`: use :c:func:`PyUnicode_FromWideChar` or :c:func:`PyUnicode_FromKindAndData` From 1007409c427092b34912161e64625da75117a4eb Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:11:20 +0100 Subject: [PATCH 019/151] gh-93738: Documentation C syntax (:c:type:`PyUnicodeObject*` -> :c:expr:`PyUnicodeObject*`) (#97783) :c:type:`PyUnicodeObject*` -> :c:expr:`PyUnicodeObject*` --- Doc/c-api/unicode.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 99afebd762a456..f8b6fef3321541 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -728,7 +728,7 @@ conversion function: ParseTuple converter: decode :class:`bytes` objects -- obtained either directly or indirectly through the :class:`os.PathLike` interface -- to :class:`str` using :c:func:`PyUnicode_DecodeFSDefaultAndSize`; :class:`str` - objects are output as-is. *result* must be a :c:type:`PyUnicodeObject*` which + objects are output as-is. *result* must be a :c:expr:`PyUnicodeObject*` which must be released when it is no longer used. .. versionadded:: 3.2 From 86e22432c5b3815c2e10b1ba64f2ab3964e67735 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:11:34 +0100 Subject: [PATCH 020/151] gh-93738: Documentation C syntax (:c:type:`PyBytesObject*` -> :c:expr:`PyBytesObject*`) (#97782) :c:type:`PyBytesObject*` -> :c:expr:`PyBytesObject*` --- Doc/c-api/unicode.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index f8b6fef3321541..ec9c5d089c57fb 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -711,7 +711,7 @@ conversion function: ParseTuple converter: encode :class:`str` objects -- obtained directly or through the :class:`os.PathLike` interface -- to :class:`bytes` using :c:func:`PyUnicode_EncodeFSDefault`; :class:`bytes` objects are output as-is. - *result* must be a :c:type:`PyBytesObject*` which must be released when it is + *result* must be a :c:expr:`PyBytesObject*` which must be released when it is no longer used. .. versionadded:: 3.1 From ff430738eb90329ef79f94f1b0e8db20f82f9089 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:11:54 +0100 Subject: [PATCH 021/151] gh-93738: Documentation C syntax (:c:type:`PyTupleObject*` -> :c:expr:`PyTupleObject*`) (#97780) :c:type:`PyTupleObject*` -> :c:expr:`PyTupleObject*` --- Doc/c-api/typehints.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/typehints.rst b/Doc/c-api/typehints.rst index 8b0b9b651e80e6..88554a346c0dda 100644 --- a/Doc/c-api/typehints.rst +++ b/Doc/c-api/typehints.rst @@ -16,7 +16,7 @@ two types exist -- :ref:`GenericAlias ` and :class:`types.GenericAlias`. The *origin* and *args* arguments set the ``GenericAlias``\ 's ``__origin__`` and ``__args__`` attributes respectively. *origin* should be a :c:type:`PyTypeObject*`, and *args* can be a - :c:type:`PyTupleObject*` or any ``PyObject*``. If *args* passed is + :c:expr:`PyTupleObject*` or any ``PyObject*``. If *args* passed is not a tuple, a 1-tuple is automatically constructed and ``__args__`` is set to ``(args,)``. Minimal checking is done for the arguments, so the function will succeed even From b220c7f16c7756fe2006ca51a648fa3bb9dae850 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:12:22 +0100 Subject: [PATCH 022/151] gh-93738: Documentation C syntax (:c:type:`PyInterpreterState *` -> :c:expr:`PyInterpreterState *`) (#97777) :c:type:`PyInterpreterState *` -> :c:expr:`PyInterpreterState *` --- Doc/c-api/init.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 2a9cf0ea702209..ec3034893b9001 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1023,7 +1023,7 @@ code, or when embedding the Python interpreter: .. c:type:: PyThreadState This data structure represents the state of a single thread. The only public - data member is :attr:`interp` (:c:type:`PyInterpreterState *`), which points to + data member is :attr:`interp` (:c:expr:`PyInterpreterState *`), which points to this thread's interpreter state. From 82b2939ca0d2505459465745630abbb50555fdb2 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:13:03 +0100 Subject: [PATCH 023/151] gh-93738: Documentation C syntax (:c:type:`PyObject` -> :c:expr:`PyObject`) (#97776) :c:type:`PyObject` -> :c:expr:`PyObject` --- Doc/c-api/arg.rst | 10 +++++----- Doc/c-api/call.rst | 8 ++++---- Doc/c-api/dict.rst | 4 ++-- Doc/c-api/exceptions.rst | 4 ++-- Doc/c-api/init.rst | 2 +- Doc/c-api/intro.rst | 4 ++-- Doc/c-api/structures.rst | 20 ++++++++++---------- Doc/c-api/tuple.rst | 2 +- Doc/c-api/typeobj.rst | 2 +- Doc/library/ctypes.rst | 4 ++-- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 2e63e4d4563d4b..c9dcf746ef2f9f 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -129,17 +129,17 @@ which disallows mutable objects such as :class:`bytearray`. ``S`` (:class:`bytes`) [PyBytesObject \*] Requires that the Python object is a :class:`bytes` object, without attempting any conversion. Raises :exc:`TypeError` if the object is not - a bytes object. The C variable may also be declared as :c:type:`PyObject*`. + a bytes object. The C variable may also be declared as :c:expr:`PyObject*`. ``Y`` (:class:`bytearray`) [PyByteArrayObject \*] Requires that the Python object is a :class:`bytearray` object, without attempting any conversion. Raises :exc:`TypeError` if the object is not - a :class:`bytearray` object. The C variable may also be declared as :c:type:`PyObject*`. + a :class:`bytearray` object. The C variable may also be declared as :c:expr:`PyObject*`. ``U`` (:class:`str`) [PyObject \*] Requires that the Python object is a Unicode object, without attempting any conversion. Raises :exc:`TypeError` if the object is not a Unicode - object. The C variable may also be declared as :c:type:`PyObject*`. + object. The C variable may also be declared as :c:expr:`PyObject*`. ``w*`` (read-write :term:`bytes-like object`) [Py_buffer] This format accepts any object which implements the read-write buffer @@ -283,7 +283,7 @@ Other objects ``O!`` (object) [*typeobject*, PyObject \*] Store a Python object in a C object pointer. This is similar to ``O``, but takes two C arguments: the first is the address of a Python type object, the - second is the address of the C variable (of type :c:type:`PyObject*`) into which + second is the address of the C variable (of type :c:expr:`PyObject*`) into which the object pointer is stored. If the Python object does not have the required type, :exc:`TypeError` is raised. @@ -444,7 +444,7 @@ API Functions *args*; it must actually be a tuple. The length of the tuple must be at least *min* and no more than *max*; *min* and *max* may be equal. Additional arguments must be passed to the function, each of which should be a pointer to a - :c:type:`PyObject*` variable; these will be filled in with the values from + :c:expr:`PyObject*` variable; these will be filled in with the values from *args*; they will contain :term:`borrowed references `. The variables which correspond to optional parameters not given by *args* will not be filled in; these should diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst index 11d5c33a2d1560..6fb2e15196103d 100644 --- a/Doc/c-api/call.rst +++ b/Doc/c-api/call.rst @@ -284,7 +284,7 @@ please see individual documentation for details. This is the equivalent of the Python expression: ``callable(*args)``. - Note that if you only pass :c:type:`PyObject *` args, + Note that if you only pass :c:expr:`PyObject *` args, :c:func:`PyObject_CallFunctionObjArgs` is a faster alternative. .. versionchanged:: 3.4 @@ -305,7 +305,7 @@ please see individual documentation for details. This is the equivalent of the Python expression: ``obj.name(arg1, arg2, ...)``. - Note that if you only pass :c:type:`PyObject *` args, + Note that if you only pass :c:expr:`PyObject *` args, :c:func:`PyObject_CallMethodObjArgs` is a faster alternative. .. versionchanged:: 3.4 @@ -315,7 +315,7 @@ please see individual documentation for details. .. c:function:: PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ...) Call a callable Python object *callable*, with a variable number of - :c:type:`PyObject *` arguments. The arguments are provided as a variable number + :c:expr:`PyObject *` arguments. The arguments are provided as a variable number of parameters followed by *NULL*. Return the result of the call on success, or raise an exception and return @@ -329,7 +329,7 @@ please see individual documentation for details. Call a method of the Python object *obj*, where the name of the method is given as a Python string object in *name*. It is called with a variable number of - :c:type:`PyObject *` arguments. The arguments are provided as a variable number + :c:expr:`PyObject *` arguments. The arguments are provided as a variable number of parameters followed by *NULL*. Return the result of the call on success, or raise an exception and return diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index d257c9b5f763d1..67c2026baa1496 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -118,7 +118,7 @@ Dictionary Objects .. c:function:: PyObject* PyDict_GetItemString(PyObject *p, const char *key) This is the same as :c:func:`PyDict_GetItem`, but *key* is specified as a - :c:type:`const char*`, rather than a :c:type:`PyObject*`. + :c:type:`const char*`, rather than a :c:expr:`PyObject*`. Note that exceptions which occur while calling :meth:`__hash__` and :meth:`__eq__` methods and creating a temporary string object @@ -167,7 +167,7 @@ Dictionary Objects prior to the first call to this function to start the iteration; the function returns true for each pair in the dictionary, and false once all pairs have been reported. The parameters *pkey* and *pvalue* should either - point to :c:type:`PyObject*` variables that will be filled in with each key + point to :c:expr:`PyObject*` variables that will be filled in with each key and value, respectively, or may be ``NULL``. Any references returned through them are borrowed. *ppos* should not be altered during iteration. Its value represents offsets within the internal dictionary structure, and diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index df73f23d2de2b7..7221957fe1dbac 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -848,7 +848,7 @@ Standard Exceptions All standard Python exceptions are available as global variables whose names are ``PyExc_`` followed by the Python exception name. These have the type -:c:type:`PyObject*`; they are all class objects. For completeness, here are all +:c:expr:`PyObject*`; they are all class objects. For completeness, here are all the variables: .. index:: @@ -1068,7 +1068,7 @@ Standard Warning Categories All standard Python warning categories are available as global variables whose names are ``PyExc_`` followed by the Python exception name. These have the type -:c:type:`PyObject*`; they are all class objects. For completeness, here are all +:c:expr:`PyObject*`; they are all class objects. For completeness, here are all the variables: .. index:: diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index ec3034893b9001..cb3bfedc97e88a 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1875,7 +1875,7 @@ you need to include :file:`pythread.h` to use thread-local storage. .. note:: None of these API functions handle memory management on behalf of the :c:type:`void*` values. You need to allocate and deallocate them yourself. - If the :c:type:`void*` values happen to be :c:type:`PyObject*`, these + If the :c:type:`void*` values happen to be :c:expr:`PyObject*`, these functions don't do refcount operations on them either. .. _thread-specific-storage-api: diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index 557ccfc052343e..991bc3b09fd886 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -264,13 +264,13 @@ Objects, Types and Reference Counts .. index:: object: type Most Python/C API functions have one or more arguments as well as a return value -of type :c:type:`PyObject*`. This type is a pointer to an opaque data type +of type :c:expr:`PyObject*`. This type is a pointer to an opaque data type representing an arbitrary Python object. Since all Python object types are treated the same way by the Python language in most situations (e.g., assignments, scope rules, and argument passing), it is only fitting that they should be represented by a single C type. Almost all Python objects live on the heap: you never declare an automatic or static variable of type -:c:type:`PyObject`, only pointer variables of type :c:type:`PyObject*` can be +:c:type:`PyObject`, only pointer variables of type :c:expr:`PyObject*` can be declared. The sole exception are the type objects; since these must never be deallocated, they are typically static :c:type:`PyTypeObject` objects. diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index f1eb09bb569168..1cc5c4647120cb 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -27,7 +27,7 @@ the definition of all other Python objects. object. In a normal "release" build, it contains only the object's reference count and a pointer to the corresponding type object. Nothing is actually declared to be a :c:type:`PyObject`, but every pointer - to a Python object can be cast to a :c:type:`PyObject*`. Access to the + to a Python object can be cast to a :c:expr:`PyObject*`. Access to the members must be done by using the macros :c:macro:`Py_REFCNT` and :c:macro:`Py_TYPE`. @@ -184,7 +184,7 @@ Implementing functions and methods .. c:type:: PyCFunction Type of the functions used to implement most Python callables in C. - Functions of this type take two :c:type:`PyObject*` parameters and return + Functions of this type take two :c:expr:`PyObject*` parameters and return one such value. If the return value is ``NULL``, an exception shall have been set. If not ``NULL``, the return value is interpreted as the return value of the function as exposed in Python. The function must return a new @@ -263,10 +263,10 @@ Implementing functions and methods +------------------+---------------+-------------------------------+ The :attr:`ml_meth` is a C function pointer. The functions may be of different -types, but they always return :c:type:`PyObject*`. If the function is not of +types, but they always return :c:expr:`PyObject*`. If the function is not of the :c:type:`PyCFunction`, the compiler will require a cast in the method table. Even though :c:type:`PyCFunction` defines the first parameter as -:c:type:`PyObject*`, it is common that the method implementation uses the +:c:expr:`PyObject*`, it is common that the method implementation uses the specific C type of the *self* object. The :attr:`ml_flags` field is a bitfield which can include the following flags. @@ -278,7 +278,7 @@ There are these calling conventions: .. data:: METH_VARARGS This is the typical calling convention, where the methods have the type - :c:type:`PyCFunction`. The function expects two :c:type:`PyObject*` values. + :c:type:`PyCFunction`. The function expects two :c:expr:`PyObject*` values. The first one is the *self* object for methods; for module functions, it is the module object. The second parameter (often called *args*) is a tuple object representing all arguments. This parameter is typically processed @@ -299,7 +299,7 @@ There are these calling conventions: Fast calling convention supporting only positional arguments. The methods have the type :c:type:`_PyCFunctionFast`. The first parameter is *self*, the second parameter is a C array - of :c:type:`PyObject*` values indicating the arguments and the third + of :c:expr:`PyObject*` values indicating the arguments and the third parameter is the number of arguments (the length of the array). .. versionadded:: 3.7 @@ -315,7 +315,7 @@ There are these calling conventions: with methods of type :c:type:`_PyCFunctionFastWithKeywords`. Keyword arguments are passed the same way as in the :ref:`vectorcall protocol `: - there is an additional fourth :c:type:`PyObject*` parameter + there is an additional fourth :c:expr:`PyObject*` parameter which is a tuple representing the names of the keyword arguments (which are guaranteed to be strings) or possibly ``NULL`` if there are no keywords. The values of the keyword @@ -354,7 +354,7 @@ There are these calling conventions: Methods with a single object argument can be listed with the :const:`METH_O` flag, instead of invoking :c:func:`PyArg_ParseTuple` with a ``"O"`` argument. They have the type :c:type:`PyCFunction`, with the *self* parameter, and a - :c:type:`PyObject*` parameter representing the single argument. + :c:expr:`PyObject*` parameter representing the single argument. These two constants are not used to indicate the calling convention but the @@ -522,7 +522,7 @@ Accessing attributes of extension types | | | getter and setter | +-------------+------------------+-----------------------------------+ - The ``get`` function takes one :c:type:`PyObject*` parameter (the + The ``get`` function takes one :c:expr:`PyObject*` parameter (the instance) and a function pointer (the associated ``closure``):: typedef PyObject *(*getter)(PyObject *, void *); @@ -530,7 +530,7 @@ Accessing attributes of extension types It should return a new reference on success or ``NULL`` with a set exception on failure. - ``set`` functions take two :c:type:`PyObject*` parameters (the instance and + ``set`` functions take two :c:expr:`PyObject*` parameters (the instance and the value to be set) and a function pointer (the associated ``closure``):: typedef int (*setter)(PyObject *, PyObject *, void *); diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 9b85522600d4e7..0bfd4b308d9372 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -161,7 +161,7 @@ type. .. c:type:: PyStructSequence_Field Describes a field of a struct sequence. As a struct sequence is modeled as a - tuple, all fields are typed as :c:type:`PyObject*`. The index in the + tuple, all fields are typed as :c:expr:`PyObject*`. The index in the :attr:`fields` array of the :c:type:`PyStructSequence_Desc` determines which field of the struct sequence is described. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 2439f7c41b5665..32ecc111d9df41 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -1520,7 +1520,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) than zero and contains the offset in the instance structure of the weak reference list head (ignoring the GC header, if present); this offset is used by :c:func:`PyObject_ClearWeakRefs` and the :c:func:`PyWeakref_\*` functions. The - instance structure needs to include a field of type :c:type:`PyObject*` which is + instance structure needs to include a field of type :c:expr:`PyObject*` which is initialized to ``NULL``. Do not confuse this field with :c:member:`~PyTypeObject.tp_weaklist`; that is the list head for diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 9546696e1c3d76..685ff835346f49 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2387,8 +2387,8 @@ These are the fundamental ctypes data types: .. class:: py_object - Represents the C :c:type:`PyObject *` datatype. Calling this without an - argument creates a ``NULL`` :c:type:`PyObject *` pointer. + Represents the C :c:expr:`PyObject *` datatype. Calling this without an + argument creates a ``NULL`` :c:expr:`PyObject *` pointer. The :mod:`ctypes.wintypes` module provides quite some other Windows specific data types, for example :c:type:`HWND`, :c:type:`WPARAM`, or :c:type:`DWORD`. Some From c9733b2d3bb359dd6443d2ed6544c81dc7584e83 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 4 Oct 2022 18:16:37 -0500 Subject: [PATCH 024/151] gh-95913: Copyedit/improve Other Language Changes What's New section (#97719) * Add/refine cross references to items in other lang changes section * Unify context manager exception changes into single non-repetitive item * More clearly describe the intent and consequences of the -P option * Apply minor clarifications & copyedits to rest of section * Tweak the formatting of module references Co-authored-by: Ezio Melotti Co-authored-by: Ezio Melotti --- Doc/whatsnew/3.11.rst | 67 +++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index c7233aab71e6f1..173580d5894192 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -427,52 +427,57 @@ See `this message from the Steering Council `. (See - :issue:`46725` for more details.) - -* Asynchronous comprehensions are now allowed inside comprehensions in - asynchronous functions. Outer comprehensions implicitly become - asynchronous. (Contributed by Serhiy Storchaka in :issue:`33346`.) +* Starred unpacking expressions can now be used in :keyword:`for` statements. + (See :issue:`46725` for more details.) -* A :exc:`TypeError` is now raised instead of an :exc:`AttributeError` in - :meth:`contextlib.ExitStack.enter_context` and - :meth:`contextlib.AsyncExitStack.enter_async_context` for objects which do not - support the :term:`context manager` or :term:`asynchronous context manager` - protocols correspondingly. - (Contributed by Serhiy Storchaka in :issue:`44471`.) +* Asynchronous :ref:`comprehensions ` are now allowed + inside comprehensions in :ref:`asynchronous functions `. + Outer comprehensions implicitly become asynchronous in this case. + (Contributed by Serhiy Storchaka in :issue:`33346`.) * A :exc:`TypeError` is now raised instead of an :exc:`AttributeError` in - :keyword:`with` and :keyword:`async with` statements for objects which do not - support the :term:`context manager` or :term:`asynchronous context manager` - protocols correspondingly. - (Contributed by Serhiy Storchaka in :issue:`12022`.) - -* Added :meth:`object.__getstate__` which provides the default - implementation of the ``__getstate__()`` method. :mod:`Copying ` - and :mod:`pickling ` instances of subclasses of builtin types + :keyword:`with` statements and :meth:`contextlib.ExitStack.enter_context` + for objects that do not support the :term:`context manager` protocol, + and in :keyword:`async with` statements and + :meth:`contextlib.AsyncExitStack.enter_async_context` + for objects not supporting the :term:`asynchronous context manager` protocol. + (Contributed by Serhiy Storchaka in :issue:`12022` and :issue:`44471`.) + +* Added :meth:`object.__getstate__`, which provides the default + implementation of the :meth:`!__getstate__` method. :mod:`copy`\ing + and :mod:`pickle`\ing instances of subclasses of builtin types :class:`bytearray`, :class:`set`, :class:`frozenset`, :class:`collections.OrderedDict`, :class:`collections.deque`, :class:`weakref.WeakSet`, and :class:`datetime.tzinfo` now copies and pickles instance attributes implemented as :term:`slots <__slots__>`. (Contributed by Serhiy Storchaka in :issue:`26579`.) -* Add :option:`-P` command line option and :envvar:`PYTHONSAFEPATH` environment - variable to not prepend a potentially unsafe path to :data:`sys.path` such as - the current directory, the script's directory or an empty string. +* Added a :option:`-P` command line option + and a :envvar:`PYTHONSAFEPATH` environment variable, + which disable the automatic prepending to :data:`sys.path` + of the script's directory when running a script, + or the current directory when using :option:`-c` and :option:`-m`. + This ensures only stdlib and installed modules + are picked up by :keyword:`import`, + and avoids unintentionally or maliciously shadowing modules + with those in a local (and typically user-writable) directory. (Contributed by Victor Stinner in :gh:`57684`.) -* A ``"z"`` option was added to the format specification mini-language that - coerces negative zero to zero after rounding to the format precision. See - :pep:`682` for more details. (Contributed by John Belmonte in :gh:`90153`.) +* A ``"z"`` option was added to the :ref:`formatspec` that + coerces negative to positive zero after rounding to the format precision. + See :pep:`682` for more details. + (Contributed by John Belmonte in :gh:`90153`.) -* Bytes are no longer accepted on :attr:`sys.path`. Support broke sometime - between Python 3.2 and 3.6 with no one noticing until after Python 3.10.0 - was released. Bringing back support would also be problematic due to - interactions between :option:`-b` and :attr:`sys.path_importer_cache` when - there is a mixture of strings and bytes keys. +* Bytes are no longer accepted on :data:`sys.path`. Support broke sometime + between Python 3.2 and 3.6, with no one noticing until after Python 3.10.0 + was released. In addition, bringing back support would be problematic due to + interactions between :option:`-b` and :data:`sys.path_importer_cache` when + there is a mixture of :class:`str` and :class:`bytes` keys. (Contributed by Thomas Grainger in :gh:`91181`.) Other CPython Implementation Changes From 9455377cbca064b90082a8e23c09bbd60557427d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:18:09 +0100 Subject: [PATCH 025/151] gh-93738: Documentation C syntax (:c:data:`view->obj` -> :c:expr:`view->obj`) (#97773) :c:data:`view->obj` -> :c:expr:`view->obj` --- Doc/c-api/typeobj.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 32ecc111d9df41..86c0830e7a9cf1 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2356,13 +2356,13 @@ Buffer Object Structures steps: (1) Check if the request can be met. If not, raise :c:data:`PyExc_BufferError`, - set :c:data:`view->obj` to ``NULL`` and return ``-1``. + set :c:expr:`view->obj` to ``NULL`` and return ``-1``. (2) Fill in the requested fields. (3) Increment an internal counter for the number of exports. - (4) Set :c:data:`view->obj` to *exporter* and increment :c:data:`view->obj`. + (4) Set :c:expr:`view->obj` to *exporter* and increment :c:expr:`view->obj`. (5) Return ``0``. @@ -2370,10 +2370,10 @@ Buffer Object Structures schemes can be used: * Re-export: Each member of the tree acts as the exporting object and - sets :c:data:`view->obj` to a new reference to itself. + sets :c:expr:`view->obj` to a new reference to itself. * Redirect: The buffer request is redirected to the root object of the - tree. Here, :c:data:`view->obj` will be a new reference to the root + tree. Here, :c:expr:`view->obj` will be a new reference to the root object. The individual fields of *view* are described in section @@ -2415,7 +2415,7 @@ Buffer Object Structures *view* argument. - This function MUST NOT decrement :c:data:`view->obj`, since that is + This function MUST NOT decrement :c:expr:`view->obj`, since that is done automatically in :c:func:`PyBuffer_Release` (this scheme is useful for breaking reference cycles). From 3d795a2bfd027e6645b8f2b76f087aa77adb356f Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:26:14 +0100 Subject: [PATCH 026/151] gh-93738: Documentation C syntax (Use `c:struct`) (#97772) Use `c:struct` --- Doc/c-api/import.rst | 6 +++--- Doc/c-api/veryhigh.rst | 4 ++-- Doc/library/ctypes.rst | 4 ++-- Doc/library/socket.rst | 12 ++++++------ Doc/whatsnew/3.11.rst | 2 +- Doc/whatsnew/3.8.rst | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 5e2333a74ce648..0922956c607bc3 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -243,7 +243,7 @@ Importing Modules UTF-8 encoded string instead of a Unicode object. -.. c:type:: struct _frozen +.. c:struct:: _frozen .. index:: single: freeze utility @@ -265,7 +265,7 @@ Importing Modules .. c:var:: const struct _frozen* PyImport_FrozenModules - This pointer is initialized to point to an array of :c:type:`struct _frozen` + This pointer is initialized to point to an array of :c:struct:`_frozen` records, terminated by one whose members are all ``NULL`` or zero. When a frozen module is imported, it is searched in this table. Third-party code could play tricks with this to provide a dynamically created collection of frozen modules. @@ -281,7 +281,7 @@ Importing Modules :c:func:`Py_Initialize`. -.. c:type:: struct _inittab +.. c:struct:: _inittab Structure describing a single entry in the list of built-in modules. Each of these structures gives the name and initialization function for a module built diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index b17818e35ed9cf..e3752bdccb6602 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -82,7 +82,7 @@ the same library that the Python runtime is using. .. c:function:: int PyRun_SimpleString(const char *command) This is a simplified interface to :c:func:`PyRun_SimpleStringFlags` below, - leaving the :c:type:`PyCompilerFlags`\* argument set to ``NULL``. + leaving the :c:struct:`PyCompilerFlags`\* argument set to ``NULL``. .. c:function:: int PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags) @@ -338,7 +338,7 @@ the same library that the Python runtime is using. interpreter loop. -.. c:type:: struct PyCompilerFlags +.. c:struct:: PyCompilerFlags This is the structure used to hold compiler flags. In cases where code is only being compiled, it is passed as ``int flags``, and in cases where code is being diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 685ff835346f49..d4c52a27592a94 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1088,7 +1088,7 @@ An extended example which also demonstrates the use of pointers accesses the Quoting the docs for that value: - This pointer is initialized to point to an array of :c:type:`struct _frozen` + This pointer is initialized to point to an array of :c:struct:`_frozen` records, terminated by one whose members are all ``NULL`` or zero. When a frozen module is imported, it is searched in this table. Third-party code could play tricks with this to provide a dynamically created collection of frozen modules. @@ -1107,7 +1107,7 @@ size, we show only how this table can be read with :mod:`ctypes`:: ... >>> -We have defined the :c:type:`struct _frozen` data type, so we can get the pointer +We have defined the :c:struct:`_frozen` data type, so we can get the pointer to the table:: >>> FrozenTable = POINTER(struct_frozen) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index f97c4f67001633..1a9e5fee77b736 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1059,7 +1059,7 @@ The :mod:`socket` module also offers various network-related services: Convert an IPv4 address from dotted-quad string format (for example, '123.45.67.89') to 32-bit packed binary format, as a bytes object four characters in length. This is useful when conversing with a program that uses the standard C - library and needs objects of type :c:type:`struct in_addr`, which is the C type + library and needs objects of type :c:struct:`in_addr`, which is the C type for the 32-bit packed binary this function returns. :func:`inet_aton` also accepts strings with less than three dots; see the @@ -1078,7 +1078,7 @@ The :mod:`socket` module also offers various network-related services: Convert a 32-bit packed IPv4 address (a :term:`bytes-like object` four bytes in length) to its standard dotted-quad string representation (for example, '123.45.67.89'). This is useful when conversing with a program that uses the - standard C library and needs objects of type :c:type:`struct in_addr`, which + standard C library and needs objects of type :c:struct:`in_addr`, which is the C type for the 32-bit packed binary data this function takes as an argument. @@ -1095,8 +1095,8 @@ The :mod:`socket` module also offers various network-related services: Convert an IP address from its family-specific string format to a packed, binary format. :func:`inet_pton` is useful when a library or network protocol - calls for an object of type :c:type:`struct in_addr` (similar to - :func:`inet_aton`) or :c:type:`struct in6_addr`. + calls for an object of type :c:struct:`in_addr` (similar to + :func:`inet_aton`) or :c:struct:`in6_addr`. Supported values for *address_family* are currently :const:`AF_INET` and :const:`AF_INET6`. If the IP address string *ip_string* is invalid, @@ -1116,8 +1116,8 @@ The :mod:`socket` module also offers various network-related services: bytes) to its standard, family-specific string representation (for example, ``'7.10.0.5'`` or ``'5aef:2b::8'``). :func:`inet_ntop` is useful when a library or network protocol returns an - object of type :c:type:`struct in_addr` (similar to :func:`inet_ntoa`) or - :c:type:`struct in6_addr`. + object of type :c:struct:`in_addr` (similar to :func:`inet_ntoa`) or + :c:struct:`in6_addr`. Supported values for *address_family* are currently :const:`AF_INET` and :const:`AF_INET6`. If the bytes object *packed_ip* is not the correct diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 173580d5894192..f7e63ec8b0f558 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1880,7 +1880,7 @@ Porting to Python 3.11 fields of the result from the exception instance (the ``value`` field). (Contributed by Irit Katriel in :issue:`45711`.) -* :c:type:`_frozen` has a new ``is_package`` field to indicate whether +* :c:struct:`_frozen` has a new ``is_package`` field to indicate whether or not the frozen module is a package. Previously, a negative value in the ``size`` field was the indicator. Now only non-negative values be used for ``size``. diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 1ab741ff95b7d4..95aa6f7a8582ae 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -2015,7 +2015,7 @@ Changes in the Python API Changes in the C API -------------------- -* The :c:type:`PyCompilerFlags` structure got a new *cf_feature_version* +* The :c:struct:`PyCompilerFlags` structure got a new *cf_feature_version* field. It should be initialized to ``PY_MINOR_VERSION``. The field is ignored by default, and is used if and only if ``PyCF_ONLY_AST`` flag is set in *cf_flags*. From cb408c6da6771b907e3747bf5ddd2bea9d7c97c6 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:26:36 +0100 Subject: [PATCH 027/151] gh-93738: Documentation C syntax (:c:type:`TYPE` -> :c:expr:`TYPE`) (#97770) :c:type:`TYPE` -> :c:expr:`TYPE` --- Doc/c-api/memory.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 335ea00cff7cb8..4abbf340c5f420 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -265,14 +265,14 @@ The following type-oriented macros are provided for convenience. Note that .. c:function:: TYPE* PyMem_New(TYPE, size_t n) Same as :c:func:`PyMem_Malloc`, but allocates ``(n * sizeof(TYPE))`` bytes of - memory. Returns a pointer cast to :c:type:`TYPE*`. The memory will not have + memory. Returns a pointer cast to :c:expr:`TYPE*`. The memory will not have been initialized in any way. .. c:function:: TYPE* PyMem_Resize(void *p, TYPE, size_t n) Same as :c:func:`PyMem_Realloc`, but the memory block is resized to ``(n * - sizeof(TYPE))`` bytes. Returns a pointer cast to :c:type:`TYPE*`. On return, + sizeof(TYPE))`` bytes. Returns a pointer cast to :c:expr:`TYPE*`. On return, *p* will be a pointer to the new memory area, or ``NULL`` in the event of failure. From dde456324dc856aa66f51d12501a0b312e877cc5 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:27:29 +0100 Subject: [PATCH 028/151] gh-93738: Documentation C syntax (:c:type:`FILE` -> :c:expr:`FILE`) (#97769) :c:type:`FILE` -> :c:expr:`FILE` --- Doc/c-api/file.rst | 2 +- Doc/c-api/marshal.rst | 8 ++++---- Doc/c-api/veryhigh.rst | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index ed3735aa83608a..145dfe4962ac8f 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -8,7 +8,7 @@ File Objects .. index:: object: file These APIs are a minimal emulation of the Python 2 C API for built-in file -objects, which used to rely on the buffered I/O (:c:type:`FILE*`) support +objects, which used to rely on the buffered I/O (:c:expr:`FILE*`) support from the C standard library. In Python 3, files and streams use the new :mod:`io` module, which defines several layers over the low-level unbuffered I/O of the operating system. The functions described below are diff --git a/Doc/c-api/marshal.rst b/Doc/c-api/marshal.rst index 7bb0dad2b6b6d5..1ba18beb3ea00f 100644 --- a/Doc/c-api/marshal.rst +++ b/Doc/c-api/marshal.rst @@ -43,7 +43,7 @@ The following functions allow marshalled values to be read back in. .. c:function:: long PyMarshal_ReadLongFromFile(FILE *file) - Return a C :c:type:`long` from the data stream in a :c:type:`FILE*` opened + Return a C :c:type:`long` from the data stream in a :c:expr:`FILE*` opened for reading. Only a 32-bit value can be read in using this function, regardless of the native size of :c:type:`long`. @@ -53,7 +53,7 @@ The following functions allow marshalled values to be read back in. .. c:function:: int PyMarshal_ReadShortFromFile(FILE *file) - Return a C :c:type:`short` from the data stream in a :c:type:`FILE*` opened + Return a C :c:type:`short` from the data stream in a :c:expr:`FILE*` opened for reading. Only a 16-bit value can be read in using this function, regardless of the native size of :c:type:`short`. @@ -63,7 +63,7 @@ The following functions allow marshalled values to be read back in. .. c:function:: PyObject* PyMarshal_ReadObjectFromFile(FILE *file) - Return a Python object from the data stream in a :c:type:`FILE*` opened for + Return a Python object from the data stream in a :c:expr:`FILE*` opened for reading. On error, sets the appropriate exception (:exc:`EOFError`, :exc:`ValueError` @@ -72,7 +72,7 @@ The following functions allow marshalled values to be read back in. .. c:function:: PyObject* PyMarshal_ReadLastObjectFromFile(FILE *file) - Return a Python object from the data stream in a :c:type:`FILE*` opened for + Return a Python object from the data stream in a :c:expr:`FILE*` opened for reading. Unlike :c:func:`PyMarshal_ReadObjectFromFile`, this function assumes that no further objects will be read from the file, allowing it to aggressively load file data into memory so that the de-serialization can diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index e3752bdccb6602..513856d8a48d70 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -16,11 +16,11 @@ parameter. The available start symbols are :const:`Py_eval_input`, :const:`Py_file_input`, and :const:`Py_single_input`. These are described following the functions which accept them as parameters. -Note also that several of these functions take :c:type:`FILE*` parameters. One -particular issue which needs to be handled carefully is that the :c:type:`FILE` +Note also that several of these functions take :c:expr:`FILE*` parameters. One +particular issue which needs to be handled carefully is that the :c:expr:`FILE` structure for different C libraries can be different and incompatible. Under Windows (at least), it is possible for dynamically linked extensions to actually -use different libraries, so care should be taken that :c:type:`FILE*` parameters +use different libraries, so care should be taken that :c:expr:`FILE*` parameters are only passed to these functions if it is certain that they were created by the same library that the Python runtime is using. From 3a134eb31a3dc9aacce8756fbc65306fba017d6d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:32:27 +0100 Subject: [PATCH 029/151] gh-93738: Documentation C syntax (:c:type: to :c:expr:, misc. cases) (#97775) * :c:type: to :c:expr: * Update Doc/whatsnew/2.4.rst --- Doc/c-api/file.rst | 2 +- Doc/c-api/structures.rst | 6 +++--- Doc/whatsnew/2.4.rst | 4 ++-- Misc/NEWS.d/3.8.0a4.rst | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index 145dfe4962ac8f..745d892be7ea89 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -65,7 +65,7 @@ the :mod:`io` APIs instead. Overrides the normal behavior of :func:`io.open_code` to pass its parameter through the provided handler. - The handler is a function of type :c:type:`PyObject *(\*)(PyObject *path, + The handler is a function of type :c:expr:`PyObject *(\*)(PyObject *path, void *userData)`, where *path* is guaranteed to be :c:type:`PyUnicodeObject`. The *userData* pointer is passed into the hook function. Since hook diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 1cc5c4647120cb..76803a093353b5 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -103,7 +103,7 @@ the definition of all other Python objects. .. versionchanged:: 3.11 :c:func:`Py_TYPE()` is changed to an inline static function. - The parameter type is no longer :c:type:`const PyObject*`. + The parameter type is no longer :c:expr:`const PyObject*`. .. c:function:: int Py_IS_TYPE(PyObject *o, PyTypeObject *type) @@ -128,7 +128,7 @@ the definition of all other Python objects. Use the :c:func:`Py_SET_REFCNT()` function to set an object reference count. .. versionchanged:: 3.11 - The parameter type is no longer :c:type:`const PyObject*`. + The parameter type is no longer :c:expr:`const PyObject*`. .. versionchanged:: 3.10 :c:func:`Py_REFCNT()` is changed to the inline static function. @@ -149,7 +149,7 @@ the definition of all other Python objects. .. versionchanged:: 3.11 :c:func:`Py_SIZE()` is changed to an inline static function. - The parameter type is no longer :c:type:`const PyVarObject*`. + The parameter type is no longer :c:expr:`const PyVarObject*`. .. c:function:: void Py_SET_SIZE(PyVarObject *o, Py_ssize_t size) diff --git a/Doc/whatsnew/2.4.rst b/Doc/whatsnew/2.4.rst index ddfac1a3f4e63a..61f9eb43243ceb 100644 --- a/Doc/whatsnew/2.4.rst +++ b/Doc/whatsnew/2.4.rst @@ -1453,7 +1453,7 @@ Some of the changes to Python's build process and to the C API are: extension functions: :c:macro:`Py_RETURN_NONE`, :c:macro:`Py_RETURN_TRUE`, and :c:macro:`Py_RETURN_FALSE`. (Contributed by Brett Cannon.) -* Another new macro, :c:macro:`Py_CLEAR(obj)`, decreases the reference count of +* Another new macro, :c:macro:`Py_CLEAR`, decreases the reference count of *obj* and sets *obj* to the null pointer. (Contributed by Jim Fulton.) * A new function, ``PyTuple_Pack(N, obj1, obj2, ..., objN)``, constructs @@ -1464,7 +1464,7 @@ Some of the changes to Python's build process and to the C API are: lookups without masking exceptions raised during the look-up process. (Contributed by Raymond Hettinger.) -* The :c:macro:`Py_IS_NAN(X)` macro returns 1 if its float or double argument +* The :c:expr:`Py_IS_NAN(X)` macro returns 1 if its float or double argument *X* is a NaN. (Contributed by Tim Peters.) * C code can avoid unnecessary locking by using the new diff --git a/Misc/NEWS.d/3.8.0a4.rst b/Misc/NEWS.d/3.8.0a4.rst index a21de196aa9652..9841195210c9e7 100644 --- a/Misc/NEWS.d/3.8.0a4.rst +++ b/Misc/NEWS.d/3.8.0a4.rst @@ -1354,7 +1354,7 @@ the function is called twice. .. nonce: pz-DIR .. section: C API -:c:macro:`PyDoc_VAR(name)` and :c:macro:`PyDoc_STRVAR(name,str)` now create +:c:expr:`PyDoc_VAR(name)` and :c:expr:`PyDoc_STRVAR(name,str)` now create ``static const char name[]`` instead of ``static char name[]``. Patch by Inada Naoki. From 556e8618eb903437993e4bd10e2a85ff6e2d0555 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 4 Oct 2022 19:03:58 -0500 Subject: [PATCH 030/151] gh-95913: Copyedit/improve Implementation Changes What's New section (#97720) * Add and refine reST/Sphinx syntax for implementation changes section * Clarify and refine wording in the Implementation Changes section * Elide unnecessary comma Co-authored-by: Ezio Melotti Co-authored-by: Ezio Melotti --- Doc/whatsnew/3.11.rst | 44 ++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index f7e63ec8b0f558..dafbbb673f0ed6 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -480,17 +480,24 @@ Other Language Changes there is a mixture of :class:`str` and :class:`bytes` keys. (Contributed by Thomas Grainger in :gh:`91181`.) + +.. _whatsnew311-other-implementation-changes: + Other CPython Implementation Changes ==================================== -* Special methods :meth:`complex.__complex__` and :meth:`bytes.__bytes__` are implemented to - support :class:`typing.SupportsComplex` and :class:`typing.SupportsBytes` protocols. +* The special methods :meth:`~object.__complex__` for :class:`complex` + and :meth:`~object.__bytes__` for :class:`bytes` are implemented to support + the :class:`typing.SupportsComplex` and :class:`typing.SupportsBytes` protocols. (Contributed by Mark Dickinson and Dong-hee Na in :issue:`24234`.) -* ``siphash13`` is added as a new internal hashing algorithms. It has similar security - properties as ``siphash24`` but it is slightly faster for long inputs. ``str``, ``bytes``, - and some other types now use it as default algorithm for :func:`hash`. :pep:`552` - hash-based pyc files now use ``siphash13``, too. +* ``siphash13`` is added as a new internal hashing algorithm. + It has similar security properties as ``siphash24``, + but it is slightly faster for long inputs. + :class:`str`, :class:`bytes`, and some other types + now use it as the default algorithm for :func:`hash`. + :pep:`552` :ref:`hash-based .pyc files ` + now use ``siphash13`` too. (Contributed by Inada Naoki in :issue:`29410`.) * When an active exception is re-raised by a :keyword:`raise` statement with no parameters, @@ -499,25 +506,28 @@ Other CPython Implementation Changes reflected in the re-raised exception. (Contributed by Irit Katriel in :issue:`45711`.) -* The interpreter state's representation of handled exceptions (a.k.a exc_info, or - _PyErr_StackItem) now has only the ``exc_value`` field, ``exc_type`` and ``exc_traceback`` - have been removed as their values can be derived from ``exc_value``. +* The interpreter state's representation of handled exceptions + (aka ``exc_info`` or ``_PyErr_StackItem``) + now only has the ``exc_value`` field; ``exc_type`` and ``exc_traceback`` + have been removed, as they can be derived from ``exc_value``. (Contributed by Irit Katriel in :issue:`45711`.) -* A new command line option for the Windows installer ``AppendPath`` has been added. - It behaves similiar to ``PrependPath`` but appends the install and scripts directories - instead of prepending them. +* A new :ref:`command line option `, ``AppendPath``, + has been added for the Windows installer. + It behaves similarly to ``PrependPath``, + but appends the install and scripts directories instead of prepending them. (Contributed by Bastian Neuburger in :issue:`44934`.) -* The :c:member:`PyConfig.module_search_paths_set` field must now be set to 1 for +* The :c:member:`PyConfig.module_search_paths_set` field must now be set to ``1`` for initialization to use :c:member:`PyConfig.module_search_paths` to initialize :data:`sys.path`. Otherwise, initialization will recalculate the path and replace any values added to ``module_search_paths``. -* The output of the :option:`--help` option is changed to fit inside 50 lines and 80 - columns. Information about :ref:`Python environment variables ` - and :option:`-X options <-X>` is available with the new :option:`--help-env` or - :option:`--help-xoptions` flags, and with :option:`--help-all`. +* The output of the :option:`--help` option now fits in 50 lines/80 columns. + Information about :ref:`Python environment variables ` + and :option:`-X` options is now available using the respective + :option:`--help-env` and :option:`--help-xoptions` flags, + and with the new :option:`--help-all`. (Contributed by Éric Araujo in :issue:`46142`.) From 2eeacb058f2ad477f237a48f98c4d9c101b4a7d0 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 5 Oct 2022 03:29:18 +0300 Subject: [PATCH 031/151] gh-97837: Change deprecation warning message in `unittest` (#97838) --- Lib/test/test_unittest/test_async_case.py | 17 ++++++++++++++--- Lib/test/test_unittest/test_case.py | 17 ++++++++++++++--- Lib/unittest/async_case.py | 2 +- Lib/unittest/case.py | 2 +- ...022-10-04-21-21-41.gh-issue-97837.19q-eg.rst | 7 +++++++ 5 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-04-21-21-41.gh-issue-97837.19q-eg.rst diff --git a/Lib/test/test_unittest/test_async_case.py b/Lib/test/test_unittest/test_async_case.py index d7d4dc91316c6c..fab8270ea33abd 100644 --- a/Lib/test/test_unittest/test_async_case.py +++ b/Lib/test/test_unittest/test_async_case.py @@ -277,25 +277,36 @@ async def on_cleanup2(self): self.assertEqual(events, ['asyncSetUp', 'test', 'asyncTearDown', 'cleanup2', 'cleanup1']) def test_deprecation_of_return_val_from_test(self): - # Issue 41322 - deprecate return of value!=None from a test + # Issue 41322 - deprecate return of value that is not None from a test + class Nothing: + def __eq__(self, o): + return o is None class Test(unittest.IsolatedAsyncioTestCase): async def test1(self): return 1 async def test2(self): yield 1 + async def test3(self): + return Nothing() with self.assertWarns(DeprecationWarning) as w: Test('test1').run() - self.assertIn('It is deprecated to return a value!=None', str(w.warning)) + self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) self.assertIn('test1', str(w.warning)) self.assertEqual(w.filename, __file__) with self.assertWarns(DeprecationWarning) as w: Test('test2').run() - self.assertIn('It is deprecated to return a value!=None', str(w.warning)) + self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) self.assertIn('test2', str(w.warning)) self.assertEqual(w.filename, __file__) + with self.assertWarns(DeprecationWarning) as w: + Test('test3').run() + self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) + self.assertIn('test3', str(w.warning)) + self.assertEqual(w.filename, __file__) + def test_cleanups_interleave_order(self): events = [] diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index fae0d1076da948..05d60a8ad3cf94 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -307,25 +307,36 @@ def test(self): Foo('test').run() def test_deprecation_of_return_val_from_test(self): - # Issue 41322 - deprecate return of value!=None from a test + # Issue 41322 - deprecate return of value that is not None from a test + class Nothing: + def __eq__(self, o): + return o is None class Foo(unittest.TestCase): def test1(self): return 1 def test2(self): yield 1 + def test3(self): + return Nothing() with self.assertWarns(DeprecationWarning) as w: Foo('test1').run() - self.assertIn('It is deprecated to return a value!=None', str(w.warning)) + self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) self.assertIn('test1', str(w.warning)) self.assertEqual(w.filename, __file__) with self.assertWarns(DeprecationWarning) as w: Foo('test2').run() - self.assertIn('It is deprecated to return a value!=None', str(w.warning)) + self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) self.assertIn('test2', str(w.warning)) self.assertEqual(w.filename, __file__) + with self.assertWarns(DeprecationWarning) as w: + Foo('test3').run() + self.assertIn('It is deprecated to return a value that is not None', str(w.warning)) + self.assertIn('test3', str(w.warning)) + self.assertEqual(w.filename, __file__) + def _check_call_order__subtests(self, result, events, expected_events): class Foo(Test.LoggingTestCase): def test(self): diff --git a/Lib/unittest/async_case.py b/Lib/unittest/async_case.py index 3457e92e5da298..bd2a471156065b 100644 --- a/Lib/unittest/async_case.py +++ b/Lib/unittest/async_case.py @@ -88,7 +88,7 @@ def _callSetUp(self): def _callTestMethod(self, method): if self._callMaybeAsync(method) is not None: - warnings.warn(f'It is deprecated to return a value!=None from a ' + warnings.warn(f'It is deprecated to return a value that is not None from a ' f'test case ({method})', DeprecationWarning, stacklevel=4) def _callTearDown(self): diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index af8303333d4087..b01f6605e23e39 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -577,7 +577,7 @@ def _callSetUp(self): def _callTestMethod(self, method): if method() is not None: - warnings.warn(f'It is deprecated to return a value!=None from a ' + warnings.warn(f'It is deprecated to return a value that is not None from a ' f'test case ({method})', DeprecationWarning, stacklevel=3) def _callTearDown(self): diff --git a/Misc/NEWS.d/next/Library/2022-10-04-21-21-41.gh-issue-97837.19q-eg.rst b/Misc/NEWS.d/next/Library/2022-10-04-21-21-41.gh-issue-97837.19q-eg.rst new file mode 100644 index 00000000000000..b1350c959e2b69 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-04-21-21-41.gh-issue-97837.19q-eg.rst @@ -0,0 +1,7 @@ +Change deprecate warning message in :mod:`unittest` from + +``It is deprecated to return a value!=None`` + +to + +``It is deprecated to return a value that is not None from a test case`` From 76e702cbf3ebf797b09759e5d45493b9c4e69103 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 4 Oct 2022 17:30:03 -0700 Subject: [PATCH 032/151] GH-97779: Ensure that *all* frame objects are backed by "complete" frames (GH-97845) --- Lib/test/test_code.py | 33 +++++++++++++++++++ ...2-10-04-02-00-10.gh-issue-97779.f3N1hI.rst | 1 + Objects/codeobject.c | 22 +++++++++++-- Objects/frameobject.c | 16 ++++++++- Python/frame.c | 7 ++++ 5 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-04-02-00-10.gh-issue-97779.f3N1hI.rst diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 2fdfdd0d309c5d..4e4d82314a9fb8 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -132,6 +132,7 @@ import unittest import textwrap import weakref +import dis try: import ctypes @@ -682,6 +683,38 @@ def test_lines(self): self.check_lines(misshappen) self.check_lines(bug93662) + @cpython_only + def test_code_new_empty(self): + # If this test fails, it means that the construction of PyCode_NewEmpty + # needs to be modified! Please update this test *and* PyCode_NewEmpty, + # so that they both stay in sync. + def f(): + pass + PY_CODE_LOCATION_INFO_NO_COLUMNS = 13 + f.__code__ = f.__code__.replace( + co_firstlineno=42, + co_code=bytes( + [ + dis.opmap["RESUME"], 0, + dis.opmap["LOAD_ASSERTION_ERROR"], 0, + dis.opmap["RAISE_VARARGS"], 1, + ] + ), + co_linetable=bytes( + [ + (1 << 7) + | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) + | (3 - 1), + 0, + ] + ), + ) + self.assertRaises(AssertionError, f) + self.assertEqual( + list(f.__code__.co_positions()), + 3 * [(42, 42, None, None)], + ) + if check_impl_detail(cpython=True) and ctypes is not None: py = ctypes.pythonapi diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-04-02-00-10.gh-issue-97779.f3N1hI.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-04-02-00-10.gh-issue-97779.f3N1hI.rst new file mode 100644 index 00000000000000..6115218088651b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-04-02-00-10.gh-issue-97779.f3N1hI.rst @@ -0,0 +1 @@ +Ensure that all Python frame objects are backed by "complete" frames. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 7d0d038f489a98..14d1d00684aedf 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -638,12 +638,22 @@ PyCode_New(int argcount, int kwonlyargcount, exceptiontable); } -static const char assert0[6] = { +// NOTE: When modifying the construction of PyCode_NewEmpty, please also change +// test.test_code.CodeLocationTest.test_code_new_empty to keep it in sync! + +static const uint8_t assert0[6] = { RESUME, 0, LOAD_ASSERTION_ERROR, 0, RAISE_VARARGS, 1 }; +static const uint8_t linetable[2] = { + (1 << 7) // New entry. + | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) + | (3 - 1), // Three code units. + 0, // Offset from co_firstlineno. +}; + PyCodeObject * PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) { @@ -651,6 +661,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) PyObject *filename_ob = NULL; PyObject *funcname_ob = NULL; PyObject *code_ob = NULL; + PyObject *linetable_ob = NULL; PyCodeObject *result = NULL; nulltuple = PyTuple_New(0); @@ -665,10 +676,14 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) if (filename_ob == NULL) { goto failed; } - code_ob = PyBytes_FromStringAndSize(assert0, 6); + code_ob = PyBytes_FromStringAndSize((const char *)assert0, 6); if (code_ob == NULL) { goto failed; } + linetable_ob = PyBytes_FromStringAndSize((const char *)linetable, 2); + if (linetable_ob == NULL) { + goto failed; + } #define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty) struct _PyCodeConstructor con = { @@ -677,7 +692,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .qualname = funcname_ob, .code = code_ob, .firstlineno = firstlineno, - .linetable = emptystring, + .linetable = linetable_ob, .consts = nulltuple, .names = nulltuple, .localsplusnames = nulltuple, @@ -692,6 +707,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) Py_XDECREF(funcname_ob); Py_XDECREF(filename_ob); Py_XDECREF(code_ob); + Py_XDECREF(linetable_ob); return result; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 2e377794312612..6a51a946ef35f0 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -588,6 +588,7 @@ first_line_not_before(int *lines, int len, int line) static PyFrameState _PyFrame_GetState(PyFrameObject *frame) { + assert(!_PyFrame_IsIncomplete(frame->f_frame)); if (frame->f_frame->stacktop == 0) { return FRAME_CLEARED; } @@ -1094,6 +1095,9 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals); f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data; f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; + // This frame needs to be "complete", so pretend that the first RESUME ran: + f->f_frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable; + assert(!_PyFrame_IsIncomplete(f->f_frame)); Py_DECREF(func); _PyObject_GC_TRACK(f); return f; @@ -1222,6 +1226,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { int PyFrame_FastToLocalsWithError(PyFrameObject *f) { + assert(!_PyFrame_IsIncomplete(f->f_frame)); if (f == NULL) { PyErr_BadInternalCall(); return -1; @@ -1237,7 +1242,7 @@ void PyFrame_FastToLocals(PyFrameObject *f) { int res; - + assert(!_PyFrame_IsIncomplete(f->f_frame)); assert(!PyErr_Occurred()); res = PyFrame_FastToLocalsWithError(f); @@ -1320,6 +1325,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { + assert(!_PyFrame_IsIncomplete(f->f_frame)); if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) { _PyFrame_LocalsToFast(f->f_frame, clear); f->f_fast_as_locals = 0; @@ -1330,6 +1336,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) int _PyFrame_IsEntryFrame(PyFrameObject *frame) { assert(frame != NULL); + assert(!_PyFrame_IsIncomplete(frame->f_frame)); return frame->f_frame->is_entry; } @@ -1338,6 +1345,7 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); + assert(!_PyFrame_IsIncomplete(frame->f_frame)); PyCodeObject *code = frame->f_frame->f_code; assert(code != NULL); Py_INCREF(code); @@ -1349,6 +1357,7 @@ PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) { assert(frame != NULL); + assert(!_PyFrame_IsIncomplete(frame->f_frame)); PyFrameObject *back = frame->f_back; if (back == NULL) { _PyInterpreterFrame *prev = frame->f_frame->previous; @@ -1366,24 +1375,28 @@ PyFrame_GetBack(PyFrameObject *frame) PyObject* PyFrame_GetLocals(PyFrameObject *frame) { + assert(!_PyFrame_IsIncomplete(frame->f_frame)); return frame_getlocals(frame, NULL); } PyObject* PyFrame_GetGlobals(PyFrameObject *frame) { + assert(!_PyFrame_IsIncomplete(frame->f_frame)); return frame_getglobals(frame, NULL); } PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) { + assert(!_PyFrame_IsIncomplete(frame->f_frame)); return frame_getbuiltins(frame, NULL); } int PyFrame_GetLasti(PyFrameObject *frame) { + assert(!_PyFrame_IsIncomplete(frame->f_frame)); int lasti = _PyInterpreterFrame_LASTI(frame->f_frame); if (lasti < 0) { return -1; @@ -1394,6 +1407,7 @@ PyFrame_GetLasti(PyFrameObject *frame) PyObject * PyFrame_GetGenerator(PyFrameObject *frame) { + assert(!_PyFrame_IsIncomplete(frame->f_frame)); if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) { return NULL; } diff --git a/Python/frame.c b/Python/frame.c index 05a8cffcb8a716..96566de63a78fd 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -70,6 +70,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) frame = (_PyInterpreterFrame *)f->_f_frame_data; f->f_frame = frame; frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; + if (_PyFrame_IsIncomplete(frame)) { + // This may be a newly-created generator or coroutine frame. Since it's + // dead anyways, just pretend that the first RESUME ran: + PyCodeObject *code = frame->f_code; + frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable; + } + assert(!_PyFrame_IsIncomplete(frame)); assert(f->f_back == NULL); _PyInterpreterFrame *prev = frame->previous; while (prev && _PyFrame_IsIncomplete(prev)) { From 0ba4aade7db53909e384ae6461786c70dec6b9e3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 5 Oct 2022 01:34:03 +0100 Subject: [PATCH 033/151] GH-91079: Decouple C stack overflow checks from Python recursion checks. (GH-96510) --- Include/cpython/pystate.h | 16 +++- Include/internal/pycore_ceval.h | 21 +++-- Include/internal/pycore_runtime_init.h | 2 +- Lib/test/support/__init__.py | 5 +- Lib/test/test_ast.py | 6 +- Lib/test/test_call.py | 38 +++++++++ Lib/test/test_collections.py | 2 +- Lib/test/test_compile.py | 3 +- Lib/test/test_dynamic.py | 8 +- Lib/test/test_exceptions.py | 8 +- Lib/test/test_isinstance.py | 12 +-- Lib/test/test_marshal.py | 3 +- ...2-09-05-09-56-32.gh-issue-91079.H4-DdU.rst | 3 + Modules/_testinternalcapi.c | 4 +- Parser/asdl_c.py | 9 +-- Python/Python-ast.c | 9 +-- Python/ast.c | 9 +-- Python/ast_opt.c | 9 +-- Python/ceval.c | 81 +++++++++++++------ Python/pystate.c | 5 +- Python/symtable.c | 9 +-- Python/sysmodule.c | 2 +- 22 files changed, 165 insertions(+), 99 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-09-05-09-56-32.gh-issue-91079.H4-DdU.rst diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index cc3c3eae941933..7722a384cbfa01 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -95,8 +95,10 @@ struct _ts { /* Was this thread state statically allocated? */ int _static; - int recursion_remaining; - int recursion_limit; + int py_recursion_remaining; + int py_recursion_limit; + + int c_recursion_remaining; int recursion_headroom; /* Allow 50 more calls to handle any errors. */ /* 'tracing' keeps track of the execution depth when tracing/profiling. @@ -202,6 +204,16 @@ struct _ts { _PyCFrame root_cframe; }; +/* WASI has limited call stack. Python's recursion limit depends on code + layout, optimization, and WASI runtime. Wasmtime can handle about 700 + recursions, sometimes less. 500 is a more conservative limit. */ +#ifndef C_RECURSION_LIMIT +# ifdef __wasi__ +# define C_RECURSION_LIMIT 500 +# else +# define C_RECURSION_LIMIT 800 +# endif +#endif /* other API */ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 4914948c6ca744..deda070a6dea79 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -12,15 +12,8 @@ extern "C" { struct pyruntimestate; struct _ceval_runtime_state; -/* WASI has limited call stack. Python's recursion limit depends on code - layout, optimization, and WASI runtime. Wasmtime can handle about 700-750 - recursions, sometimes less. 600 is a more conservative limit. */ #ifndef Py_DEFAULT_RECURSION_LIMIT -# ifdef __wasi__ -# define Py_DEFAULT_RECURSION_LIMIT 600 -# else -# define Py_DEFAULT_RECURSION_LIMIT 1000 -# endif +# define Py_DEFAULT_RECURSION_LIMIT 1000 #endif #include "pycore_interp.h" // PyInterpreterState.eval_frame @@ -118,12 +111,12 @@ extern void _PyEval_DeactivateOpCache(void); /* With USE_STACKCHECK macro defined, trigger stack checks in _Py_CheckRecursiveCall() on every 64th call to _Py_EnterRecursiveCall. */ static inline int _Py_MakeRecCheck(PyThreadState *tstate) { - return (tstate->recursion_remaining-- <= 0 - || (tstate->recursion_remaining & 63) == 0); + return (tstate->c_recursion_remaining-- <= 0 + || (tstate->c_recursion_remaining & 63) == 0); } #else static inline int _Py_MakeRecCheck(PyThreadState *tstate) { - return tstate->recursion_remaining-- <= 0; + return tstate->c_recursion_remaining-- <= 0; } #endif @@ -131,6 +124,9 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCall( PyThreadState *tstate, const char *where); +int _Py_CheckRecursiveCallPy( + PyThreadState *tstate); + static inline int _Py_EnterRecursiveCallTstate(PyThreadState *tstate, const char *where) { return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where)); @@ -142,7 +138,7 @@ static inline int _Py_EnterRecursiveCall(const char *where) { } static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) { - tstate->recursion_remaining++; + tstate->c_recursion_remaining++; } static inline void _Py_LeaveRecursiveCall(void) { @@ -157,6 +153,7 @@ extern PyObject* _Py_MakeCoro(PyFunctionObject *func); extern int _Py_HandlePending(PyThreadState *tstate); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 621d5cc864255d..8dd7a3128c6665 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -68,7 +68,7 @@ extern "C" { #define _PyThreadState_INIT \ { \ ._static = 1, \ - .recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \ + .py_recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \ .context_ver = 1, \ } diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 573dce52ca474a..9fdad641232c4f 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -60,7 +60,7 @@ "run_with_tz", "PGO", "missing_compiler_executable", "ALWAYS_EQ", "NEVER_EQ", "LARGEST", "SMALLEST", "LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT", - "Py_DEBUG", + "Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", ] @@ -2352,3 +2352,6 @@ def adjust_int_max_str_digits(max_digits): yield finally: sys.set_int_max_str_digits(current) + +#For recursion tests, easily exceeds default recursion limit +EXCEEDS_RECURSION_LIMIT = 5000 diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 9a7df28e22c4b9..b34644118d2815 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -825,9 +825,9 @@ def next(self): @support.cpython_only def test_ast_recursion_limit(self): - fail_depth = sys.getrecursionlimit() * 3 - crash_depth = sys.getrecursionlimit() * 300 - success_depth = int(fail_depth * 0.75) + fail_depth = support.EXCEEDS_RECURSION_LIMIT + crash_depth = 100_000 + success_depth = 1200 def check_limit(prefix, repeated): expect_ok = prefix + repeated * success_depth diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index c1a386228ff0d0..1f3307f822a5fc 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -864,6 +864,44 @@ def test_multiple_values(self): with self.check_raises_type_error(msg): A().method_two_args("x", "y", x="oops") +@cpython_only +class TestRecursion(unittest.TestCase): + + def test_super_deep(self): + + def recurse(n): + if n: + recurse(n-1) + + def py_recurse(n, m): + if n: + py_recurse(n-1, m) + else: + c_py_recurse(m-1) + + def c_recurse(n): + if n: + _testcapi.pyobject_fastcall(c_recurse, (n-1,)) + + def c_py_recurse(m): + if m: + _testcapi.pyobject_fastcall(py_recurse, (1000, m)) + + depth = sys.getrecursionlimit() + sys.setrecursionlimit(100_000) + try: + recurse(90_000) + with self.assertRaises(RecursionError): + recurse(101_000) + c_recurse(100) + with self.assertRaises(RecursionError): + c_recurse(90_000) + c_py_recurse(90) + with self.assertRaises(RecursionError): + c_py_recurse(100_000) + finally: + sys.setrecursionlimit(depth) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 59b3f2ec7bfcb6..1e398d6c3c7a3f 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -545,7 +545,7 @@ def test_odd_sizes(self): self.assertEqual(Dot(1)._replace(d=999), (999,)) self.assertEqual(Dot(1)._fields, ('d',)) - n = 5000 + n = support.EXCEEDS_RECURSION_LIMIT names = list(set(''.join([choice(string.ascii_letters) for j in range(10)]) for i in range(n))) n = len(names) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 7c55c7148aec60..21dcc1a719cc25 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -111,8 +111,7 @@ def __getitem__(self, key): @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI") def test_extended_arg(self): - # default: 1000 * 2.5 = 2500 repetitions - repeat = int(sys.getrecursionlimit() * 2.5) + repeat = 2000 longexpr = 'x = x or ' + '-x' * repeat g = {} code = ''' diff --git a/Lib/test/test_dynamic.py b/Lib/test/test_dynamic.py index 25544dea14df3a..7e12d428e0fde2 100644 --- a/Lib/test/test_dynamic.py +++ b/Lib/test/test_dynamic.py @@ -140,11 +140,11 @@ class MyGlobals(dict): def __missing__(self, key): return int(key.removeprefix("_number_")) - # 1,000 on most systems - limit = sys.getrecursionlimit() - code = "lambda: " + "+".join(f"_number_{i}" for i in range(limit)) + # Need more than 256 variables to use EXTENDED_ARGS + variables = 400 + code = "lambda: " + "+".join(f"_number_{i}" for i in range(variables)) sum_func = eval(code, MyGlobals()) - expected = sum(range(limit)) + expected = sum(range(variables)) # Warm up the the function for quickening (PEP 659) for _ in range(30): self.assertEqual(sum_func(), expected) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 03a0f8b576f6fc..65a3a8a48a8809 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1372,6 +1372,7 @@ def test_recursion_normalizing_exception(self): code = """if 1: import sys from _testinternalcapi import get_recursion_depth + from test import support class MyException(Exception): pass @@ -1399,13 +1400,8 @@ def gen(): generator = gen() next(generator) recursionlimit = sys.getrecursionlimit() - depth = get_recursion_depth() try: - # Upon the last recursive invocation of recurse(), - # tstate->recursion_depth is equal to (recursion_limit - 1) - # and is equal to recursion_limit when _gen_throw() calls - # PyErr_NormalizeException(). - recurse(setrecursionlimit(depth + 2) - depth) + recurse(support.EXCEEDS_RECURSION_LIMIT) finally: sys.setrecursionlimit(recursionlimit) print('Done.') diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index a0974640bc1146..2fcf6ebbee7e34 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -8,7 +8,7 @@ from test import support - + class TestIsInstanceExceptions(unittest.TestCase): # Test to make sure that an AttributeError when accessing the instance's # class's bases is masked. This was actually a bug in Python 2.2 and @@ -97,7 +97,7 @@ def getclass(self): class D: pass self.assertRaises(RuntimeError, isinstance, c, D) - + # These tests are similar to above, but tickle certain code paths in # issubclass() instead of isinstance() -- really PyObject_IsSubclass() # vs. PyObject_IsInstance(). @@ -147,7 +147,7 @@ def getbases(self): self.assertRaises(TypeError, issubclass, B, C()) - + # meta classes for creating abstract classes and instances class AbstractClass(object): def __init__(self, bases): @@ -179,7 +179,7 @@ class Super: class Child(Super): pass - + class TestIsInstanceIsSubclass(unittest.TestCase): # Tests to ensure that isinstance and issubclass work on abstract # classes and instances. Before the 2.2 release, TypeErrors were @@ -353,10 +353,10 @@ def blowstack(fxn, arg, compare_to): # Make sure that calling isinstance with a deeply nested tuple for its # argument will raise RecursionError eventually. tuple_arg = (compare_to,) - for cnt in range(sys.getrecursionlimit()+5): + for cnt in range(support.EXCEEDS_RECURSION_LIMIT): tuple_arg = (tuple_arg,) fxn(arg, tuple_arg) - + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 882a819ca8090f..fe4f368bed42f4 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -117,7 +117,8 @@ def test_code(self): def test_many_codeobjects(self): # Issue2957: bad recursion count on code objects - count = 5000 # more than MAX_MARSHAL_STACK_DEPTH + # more than MAX_MARSHAL_STACK_DEPTH + count = support.EXCEEDS_RECURSION_LIMIT codes = (ExceptionTestCase.test_exceptions.__code__,) * count marshal.loads(marshal.dumps(codes)) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-05-09-56-32.gh-issue-91079.H4-DdU.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-05-09-56-32.gh-issue-91079.H4-DdU.rst new file mode 100644 index 00000000000000..64606ac74a49bd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-05-09-56-32.gh-issue-91079.H4-DdU.rst @@ -0,0 +1,3 @@ +Separate Python recursion checking from C recursion checking which reduces +the chance of C stack overflow and allows the recursion limit to be +increased safely. diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 02a061b84f85a3..5724bd5f200f4c 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -44,9 +44,7 @@ get_recursion_depth(PyObject *self, PyObject *Py_UNUSED(args)) { PyThreadState *tstate = _PyThreadState_GET(); - /* subtract one to ignore the frame of the get_recursion_depth() call */ - - return PyLong_FromLong(tstate->recursion_limit - tstate->recursion_remaining - 1); + return PyLong_FromLong(tstate->py_recursion_limit - tstate->py_recursion_remaining); } diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 13dd44ca0cdc3f..6bd2e66c804d1f 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1380,7 +1380,6 @@ class PartingShots(StaticVisitor): return NULL; } - int recursion_limit = Py_GetRecursionLimit(); int starting_recursion_depth; /* Be careful here to prevent overflow. */ int COMPILER_STACK_FRAME_SCALE = 3; @@ -1388,11 +1387,9 @@ class PartingShots(StaticVisitor): if (!tstate) { return 0; } - state->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; - int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; - starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; + state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; + int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; state->recursion_depth = starting_recursion_depth; PyObject *result = ast2obj_mod(state, t); diff --git a/Python/Python-ast.c b/Python/Python-ast.c index f485af675ccff7..2571e28bc1690d 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -12315,7 +12315,6 @@ PyObject* PyAST_mod2obj(mod_ty t) return NULL; } - int recursion_limit = Py_GetRecursionLimit(); int starting_recursion_depth; /* Be careful here to prevent overflow. */ int COMPILER_STACK_FRAME_SCALE = 3; @@ -12323,11 +12322,9 @@ PyObject* PyAST_mod2obj(mod_ty t) if (!tstate) { return 0; } - state->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; - int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; - starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; + state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; + int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; state->recursion_depth = starting_recursion_depth; PyObject *result = ast2obj_mod(state, t); diff --git a/Python/ast.c b/Python/ast.c index a0321b58ba8cff..50fc8e01fb3f69 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -975,7 +975,6 @@ _PyAST_Validate(mod_ty mod) int res = -1; struct validator state; PyThreadState *tstate; - int recursion_limit = Py_GetRecursionLimit(); int starting_recursion_depth; /* Setup recursion depth check counters */ @@ -984,12 +983,10 @@ _PyAST_Validate(mod_ty mod) return 0; } /* Be careful here to prevent overflow. */ - int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; - starting_recursion_depth = (recursion_depth< INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; + int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; state.recursion_depth = starting_recursion_depth; - state.recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; + state.recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; switch (mod->kind) { case Module_kind: diff --git a/Python/ast_opt.c b/Python/ast_opt.c index b1d807bcf10ae1..426c5341b56fed 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -1080,7 +1080,6 @@ int _PyAST_Optimize(mod_ty mod, PyArena *arena, _PyASTOptimizeState *state) { PyThreadState *tstate; - int recursion_limit = Py_GetRecursionLimit(); int starting_recursion_depth; /* Setup recursion depth check counters */ @@ -1089,12 +1088,10 @@ _PyAST_Optimize(mod_ty mod, PyArena *arena, _PyASTOptimizeState *state) return 0; } /* Be careful here to prevent overflow. */ - int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; - starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; + int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; state->recursion_depth = starting_recursion_depth; - state->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; + state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; int ret = astfold_mod(mod, arena, state); assert(ret || PyErr_Occurred()); diff --git a/Python/ceval.c b/Python/ceval.c index 82b5422c188ea7..c08c794005d1ab 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -257,9 +257,9 @@ Py_SetRecursionLimit(int new_limit) PyInterpreterState *interp = _PyInterpreterState_GET(); interp->ceval.recursion_limit = new_limit; for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { - int depth = p->recursion_limit - p->recursion_remaining; - p->recursion_limit = new_limit; - p->recursion_remaining = new_limit - depth; + int depth = p->py_recursion_limit - p->py_recursion_remaining; + p->py_recursion_limit = new_limit; + p->py_recursion_remaining = new_limit - depth; } } @@ -268,35 +268,27 @@ Py_SetRecursionLimit(int new_limit) int _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) { - /* Check against global limit first. */ - int depth = tstate->recursion_limit - tstate->recursion_remaining; - if (depth < tstate->interp->ceval.recursion_limit) { - tstate->recursion_limit = tstate->interp->ceval.recursion_limit; - tstate->recursion_remaining = tstate->recursion_limit - depth; - assert(tstate->recursion_remaining > 0); - return 0; - } #ifdef USE_STACKCHECK if (PyOS_CheckStack()) { - ++tstate->recursion_remaining; + ++tstate->c_recursion_remaining; _PyErr_SetString(tstate, PyExc_MemoryError, "Stack overflow"); return -1; } #endif if (tstate->recursion_headroom) { - if (tstate->recursion_remaining < -50) { + if (tstate->c_recursion_remaining < -50) { /* Overflowing while handling an overflow. Give up. */ Py_FatalError("Cannot recover from stack overflow."); } } else { - if (tstate->recursion_remaining <= 0) { + if (tstate->c_recursion_remaining <= 0) { tstate->recursion_headroom++; _PyErr_Format(tstate, PyExc_RecursionError, "maximum recursion depth exceeded%s", where); tstate->recursion_headroom--; - ++tstate->recursion_remaining; + ++tstate->c_recursion_remaining; return -1; } } @@ -983,6 +975,39 @@ pop_frame(PyThreadState *tstate, _PyInterpreterFrame *frame) return prev_frame; } + +int _Py_CheckRecursiveCallPy( + PyThreadState *tstate) +{ + if (tstate->recursion_headroom) { + if (tstate->py_recursion_remaining < -50) { + /* Overflowing while handling an overflow. Give up. */ + Py_FatalError("Cannot recover from Python stack overflow."); + } + } + else { + if (tstate->py_recursion_remaining <= 0) { + tstate->recursion_headroom++; + _PyErr_Format(tstate, PyExc_RecursionError, + "maximum recursion depth exceeded"); + tstate->recursion_headroom--; + return -1; + } + } + return 0; +} + +static inline int _Py_EnterRecursivePy(PyThreadState *tstate) { + return (tstate->py_recursion_remaining-- <= 0) && + _Py_CheckRecursiveCallPy(tstate); +} + + +static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) { + tstate->py_recursion_remaining++; +} + + /* It is only between the KW_NAMES instruction and the following CALL, * that this has any meaning. */ @@ -1037,10 +1062,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int frame->previous = prev_cframe->current_frame; cframe.current_frame = frame; + if (_Py_EnterRecursiveCallTstate(tstate, "")) { + tstate->c_recursion_remaining--; + tstate->py_recursion_remaining--; + goto exit_unwind; + } + /* support for generator.throw() */ if (throwflag) { - if (_Py_EnterRecursiveCallTstate(tstate, "")) { - tstate->recursion_remaining--; + if (_Py_EnterRecursivePy(tstate)) { goto exit_unwind; } TRACE_FUNCTION_THROW_ENTRY(); @@ -1079,8 +1109,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int start_frame: - if (_Py_EnterRecursiveCallTstate(tstate, "")) { - tstate->recursion_remaining--; + if (_Py_EnterRecursivePy(tstate)) { goto exit_unwind; } @@ -1830,12 +1859,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyFrame_SetStackPointer(frame, stack_pointer); TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); - _Py_LeaveRecursiveCallTstate(tstate); + _Py_LeaveRecursiveCallPy(tstate); if (!frame->is_entry) { frame = cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); goto resume_frame; } + _Py_LeaveRecursiveCallTstate(tstate); /* Restore previous cframe and return. */ tstate->cframe = cframe.previous; tstate->cframe->use_tracing = cframe.use_tracing; @@ -2046,6 +2076,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyFrame_SetStackPointer(frame, stack_pointer); TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); + _Py_LeaveRecursiveCallPy(tstate); _Py_LeaveRecursiveCallTstate(tstate); /* Restore previous cframe and return. */ tstate->cframe = cframe.previous; @@ -4800,7 +4831,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; - _Py_LeaveRecursiveCallTstate(tstate); + _Py_LeaveRecursiveCallPy(tstate); if (!frame->is_entry) { _PyInterpreterFrame *prev = frame->previous; _PyThreadState_PopFrame(tstate, frame); @@ -4808,6 +4839,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyFrame_StackPush(frame, (PyObject *)gen); goto resume_frame; } + _Py_LeaveRecursiveCallTstate(tstate); /* Make sure that frame is in a valid state */ frame->stacktop = 0; frame->f_locals = NULL; @@ -5178,12 +5210,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int exit_unwind: assert(_PyErr_Occurred(tstate)); - _Py_LeaveRecursiveCallTstate(tstate); + _Py_LeaveRecursiveCallPy(tstate); if (frame->is_entry) { /* Restore previous cframe and exit */ tstate->cframe = cframe.previous; tstate->cframe->use_tracing = cframe.use_tracing; assert(tstate->cframe->current_frame == frame->previous); + _Py_LeaveRecursiveCallTstate(tstate); return NULL; } frame = cframe.current_frame = pop_frame(tstate, frame); @@ -5755,11 +5788,11 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame) // _PyThreadState_PopFrame, since f_code is already cleared at that point: assert((PyObject **)frame + frame->f_code->co_framesize == tstate->datastack_top); - tstate->recursion_remaining--; + tstate->c_recursion_remaining--; assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); assert(frame->owner == FRAME_OWNED_BY_THREAD); _PyFrame_Clear(frame); - tstate->recursion_remaining++; + tstate->c_recursion_remaining++; _PyThreadState_PopFrame(tstate, frame); } diff --git a/Python/pystate.c b/Python/pystate.c index 892ea68a09be18..04c8624cd35123 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -793,8 +793,9 @@ init_threadstate(PyThreadState *tstate, tstate->native_thread_id = PyThread_get_thread_native_id(); #endif - tstate->recursion_limit = interp->ceval.recursion_limit, - tstate->recursion_remaining = interp->ceval.recursion_limit, + tstate->py_recursion_limit = interp->ceval.recursion_limit, + tstate->py_recursion_remaining = interp->ceval.recursion_limit, + tstate->c_recursion_remaining = C_RECURSION_LIMIT; tstate->exc_info = &tstate->exc_state; diff --git a/Python/symtable.c b/Python/symtable.c index 0b259b08b61f97..342f5a080d3ddc 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -278,7 +278,6 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future) asdl_stmt_seq *seq; int i; PyThreadState *tstate; - int recursion_limit = Py_GetRecursionLimit(); int starting_recursion_depth; if (st == NULL) @@ -298,12 +297,10 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future) return NULL; } /* Be careful here to prevent overflow. */ - int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; - starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; + int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; st->recursion_depth = starting_recursion_depth; - st->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; + st->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; /* Make the initial symbol information gathering pass */ if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 1ecf6a2dd39fd5..2c66415ee3d3f4 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1218,7 +1218,7 @@ sys_setrecursionlimit_impl(PyObject *module, int new_limit) /* Reject too low new limit if the current recursion depth is higher than the new low-water mark. */ - int depth = tstate->recursion_limit - tstate->recursion_remaining; + int depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; if (depth >= new_limit) { _PyErr_Format(tstate, PyExc_RecursionError, "cannot set the recursion limit to %i at " From 86eb98cfdddfa54b4ada3d9eb36f02e40592e5d6 Mon Sep 17 00:00:00 2001 From: Shahriar Heidrich Date: Wed, 5 Oct 2022 02:36:04 +0200 Subject: [PATCH 034/151] gh-97654: Add auto exception chaining example to tutorial (#97703) Add auto exception chaining example to tutorial --- Doc/tutorial/errors.rst | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst index 57919e3bad132c..67bb19556681c0 100644 --- a/Doc/tutorial/errors.rst +++ b/Doc/tutorial/errors.rst @@ -284,8 +284,27 @@ re-raise the exception:: Exception Chaining ================== -The :keyword:`raise` statement allows an optional :keyword:`from` which enables -chaining exceptions. For example:: +If an unhandled exception occurs inside an :keyword:`except` section, it will +have the exception being handled attached to it and included in the error +message:: + + >>> try: + ... open("database.sqlite") + ... except OSError: + ... raise RuntimeError("unable to handle error") + ... + Traceback (most recent call last): + File "", line 2, in + FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite' + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "", line 4, in + RuntimeError: unable to handle error + +To indicate that an exception is a direct consequence of another, the +:keyword:`raise` statement allows an optional :keyword:`from` clause:: # exc must be exception instance or None. raise RuntimeError from exc @@ -311,9 +330,8 @@ This can be useful when you are transforming exceptions. For example:: File "", line 4, in RuntimeError: Failed to open database -Exception chaining happens automatically when an exception is raised inside an -:keyword:`except` or :keyword:`finally` section. This can be -disabled by using ``from None`` idiom: +It also allows disabling automatic exception chaining using the ``from None`` +idiom:: >>> try: ... open('database.sqlite') From 83e1296f55dc4a86a8171d3f984838c5a8e6f462 Mon Sep 17 00:00:00 2001 From: Athos Ribeiro Date: Tue, 4 Oct 2022 21:39:42 -0300 Subject: [PATCH 035/151] Add re.VERBOSE flag documentation example (#97678) The current re.VERBOSE documentation example leaves space for ambiguous interpretation. One may read that spaces within the `(?:` token are spaces inside the non-capturing group (such as `(?: )`). This patch removes the ambiguity by including examples after the statement. --- Doc/library/re.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 1b9a7b63ef5e1b..5b304f717b07fa 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -783,7 +783,8 @@ Flags more readable by allowing you to visually separate logical sections of the pattern and add comments. Whitespace within the pattern is ignored, except when in a character class, or when preceded by an unescaped backslash, - or within tokens like ``*?``, ``(?:`` or ``(?P<...>``. + or within tokens like ``*?``, ``(?:`` or ``(?P<...>``. For example, ``(? :`` + and ``* ?`` are not allowed. When a line contains a ``#`` that is not in a character class and is not preceded by an unescaped backslash, all characters from the leftmost such ``#`` through the end of the line are ignored. From 2efca9cc8d1990f1778b5f52711334502ec20e01 Mon Sep 17 00:00:00 2001 From: andrei kulakov Date: Tue, 4 Oct 2022 20:47:49 -0400 Subject: [PATCH 036/151] gh-97825: fix AttributeError when calling subprocess.check_output(input=None) with encoding or errors args (#97826) * fix AttributeError, add unit test --- Lib/subprocess.py | 3 ++- Lib/test/test_subprocess.py | 6 ++++++ .../Library/2022-10-04-07-55-19.gh-issue-97825.mNdv1l.rst | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-04-07-55-19.gh-issue-97825.mNdv1l.rst diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 7ae8df154b481f..760b93b47ebba6 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -456,7 +456,8 @@ def check_output(*popenargs, timeout=None, **kwargs): if 'input' in kwargs and kwargs['input'] is None: # Explicitly passing input=None was previously equivalent to passing an # empty string. That is maintained here for backwards compatibility. - if kwargs.get('universal_newlines') or kwargs.get('text'): + if kwargs.get('universal_newlines') or kwargs.get('text') or kwargs.get('encoding') \ + or kwargs.get('errors'): empty = '' else: empty = b'' diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index f6854922a5b878..424a4a93b6f972 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -238,6 +238,12 @@ def test_check_output_input_none_universal_newlines(self): input=None, universal_newlines=True) self.assertNotIn('XX', output) + def test_check_output_input_none_encoding_errors(self): + output = subprocess.check_output( + [sys.executable, "-c", "print('foo')"], + input=None, encoding='utf-8', errors='ignore') + self.assertIn('foo', output) + def test_check_output_stdout_arg(self): # check_output() refuses to accept 'stdout' argument with self.assertRaises(ValueError) as c: diff --git a/Misc/NEWS.d/next/Library/2022-10-04-07-55-19.gh-issue-97825.mNdv1l.rst b/Misc/NEWS.d/next/Library/2022-10-04-07-55-19.gh-issue-97825.mNdv1l.rst new file mode 100644 index 00000000000000..4633dce7b663e7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-04-07-55-19.gh-issue-97825.mNdv1l.rst @@ -0,0 +1 @@ +Fixes :exc:`AttributeError` when :meth:`subprocess.check_output` is used with argument ``input=None`` and either of the arguments *encoding* or *errors* are used. From ae65da959e46f018829d056615608019587d24ed Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 01:56:20 +0100 Subject: [PATCH 037/151] gh-93738: Documentation C syntax (:c:type:`PyTypeObject*` -> :c:expr:`PyTypeObject*`) (#97778) Co-authored-by: Ezio Melotti --- Doc/c-api/object.rst | 2 +- Doc/c-api/typehints.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index fb03366056b0d2..5a25a2b6c9d3db 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -310,7 +310,7 @@ Object Protocol is equivalent to the Python expression ``type(o)``. This function increments the reference count of the return value. There's really no reason to use this function instead of the :c:func:`Py_TYPE()` function, which returns a - pointer of type :c:type:`PyTypeObject*`, except when the incremented reference + pointer of type :c:expr:`PyTypeObject*`, except when the incremented reference count is needed. diff --git a/Doc/c-api/typehints.rst b/Doc/c-api/typehints.rst index 88554a346c0dda..4c1957a2a1dbca 100644 --- a/Doc/c-api/typehints.rst +++ b/Doc/c-api/typehints.rst @@ -15,7 +15,7 @@ two types exist -- :ref:`GenericAlias ` and Equivalent to calling the Python class :class:`types.GenericAlias`. The *origin* and *args* arguments set the ``GenericAlias``\ 's ``__origin__`` and ``__args__`` attributes respectively. - *origin* should be a :c:type:`PyTypeObject*`, and *args* can be a + *origin* should be a :c:expr:`PyTypeObject*`, and *args* can be a :c:expr:`PyTupleObject*` or any ``PyObject*``. If *args* passed is not a tuple, a 1-tuple is automatically constructed and ``__args__`` is set to ``(args,)``. From 681e059b0161321bc6dda64b43130921290c87c0 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 4 Oct 2022 23:49:10 -0700 Subject: [PATCH 038/151] GH-96704: Add {Task,Handle}.get_context(), use it in call_exception_handler() (#96756) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- Doc/library/asyncio-eventloop.rst | 16 ++++++++ Doc/library/asyncio-task.rst | 7 ++++ Lib/asyncio/base_events.py | 17 +++++++- Lib/asyncio/events.py | 3 ++ Lib/asyncio/tasks.py | 3 ++ Lib/test/test_asyncio/test_futures2.py | 41 +++++++++++++++++++ Lib/test/test_asyncio/test_tasks.py | 11 +++++ ...2-09-18-04-51-30.gh-issue-96704.DmamRX.rst | 1 + Modules/_asynciomodule.c | 13 ++++++ Modules/clinic/_asynciomodule.c.h | 19 ++++++++- 10 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-09-18-04-51-30.gh-issue-96704.DmamRX.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index c51990eff8deec..6fe95687c151de 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1271,6 +1271,15 @@ Allows customizing how exceptions are handled in the event loop. (see :meth:`call_exception_handler` documentation for details about context). + If the handler is called on behalf of a :class:`~asyncio.Task` or + :class:`~asyncio.Handle`, it is run in the + :class:`contextvars.Context` of that task or callback handle. + + .. versionchanged:: 3.12 + + The handler may be called in the :class:`~contextvars.Context` + of the task or handle where the exception originated. + .. method:: loop.get_exception_handler() Return the current exception handler, or ``None`` if no custom @@ -1474,6 +1483,13 @@ Callback Handles A callback wrapper object returned by :meth:`loop.call_soon`, :meth:`loop.call_soon_threadsafe`. + .. method:: get_context() + + Return the :class:`contextvars.Context` object + associated with the handle. + + .. versionadded:: 3.12 + .. method:: cancel() Cancel the callback. If the callback has already been canceled diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index ade969220ea701..d922f614954fcd 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -1097,6 +1097,13 @@ Task Object .. versionadded:: 3.8 + .. method:: get_context() + + Return the :class:`contextvars.Context` object + associated with the task. + + .. versionadded:: 3.12 + .. method:: get_name() Return the name of the Task. diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 66202f09794db8..c8a2f9f25634ef 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1808,7 +1808,22 @@ def call_exception_handler(self, context): exc_info=True) else: try: - self._exception_handler(self, context) + ctx = None + thing = context.get("task") + if thing is None: + # Even though Futures don't have a context, + # Task is a subclass of Future, + # and sometimes the 'future' key holds a Task. + thing = context.get("future") + if thing is None: + # Handles also have a context. + thing = context.get("handle") + if thing is not None and hasattr(thing, "get_context"): + ctx = thing.get_context() + if ctx is not None and hasattr(ctx, "run"): + ctx.run(self._exception_handler, self, context) + else: + self._exception_handler(self, context) except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 0d26ea545baa5d..a327ba54a323a8 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -61,6 +61,9 @@ def __repr__(self): info = self._repr_info() return '<{}>'.format(' '.join(info)) + def get_context(self): + return self._context + def cancel(self): if not self._cancelled: self._cancelled = True diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index e48da0f2008829..8d6dfcd81b7377 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -139,6 +139,9 @@ def __repr__(self): def get_coro(self): return self._coro + def get_context(self): + return self._context + def get_name(self): return self._name diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py index 71279b69c7929e..9e7a5775a70383 100644 --- a/Lib/test/test_asyncio/test_futures2.py +++ b/Lib/test/test_asyncio/test_futures2.py @@ -1,5 +1,6 @@ # IsolatedAsyncioTestCase based tests import asyncio +import contextvars import traceback import unittest from asyncio import tasks @@ -27,6 +28,46 @@ async def raise_exc(): else: self.fail('TypeError was not raised') + async def test_task_exc_handler_correct_context(self): + # see https://github.com/python/cpython/issues/96704 + name = contextvars.ContextVar('name', default='foo') + exc_handler_called = False + + def exc_handler(*args): + self.assertEqual(name.get(), 'bar') + nonlocal exc_handler_called + exc_handler_called = True + + async def task(): + name.set('bar') + 1/0 + + loop = asyncio.get_running_loop() + loop.set_exception_handler(exc_handler) + self.cls(task()) + await asyncio.sleep(0) + self.assertTrue(exc_handler_called) + + async def test_handle_exc_handler_correct_context(self): + # see https://github.com/python/cpython/issues/96704 + name = contextvars.ContextVar('name', default='foo') + exc_handler_called = False + + def exc_handler(*args): + self.assertEqual(name.get(), 'bar') + nonlocal exc_handler_called + exc_handler_called = True + + def callback(): + name.set('bar') + 1/0 + + loop = asyncio.get_running_loop() + loop.set_exception_handler(exc_handler) + loop.call_soon(callback) + await asyncio.sleep(0) + self.assertTrue(exc_handler_called) + @unittest.skipUnless(hasattr(tasks, '_CTask'), 'requires the C _asyncio module') class CFutureTests(FutureTests, unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 04bdf648313148..2491285206bcd7 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2482,6 +2482,17 @@ def test_get_coro(self): finally: loop.close() + def test_get_context(self): + loop = asyncio.new_event_loop() + coro = coroutine_function() + context = contextvars.copy_context() + try: + task = self.new_task(loop, coro, context=context) + loop.run_until_complete(task) + self.assertIs(task.get_context(), context) + finally: + loop.close() + def add_subclass_tests(cls): BaseTask = cls.Task diff --git a/Misc/NEWS.d/next/Library/2022-09-18-04-51-30.gh-issue-96704.DmamRX.rst b/Misc/NEWS.d/next/Library/2022-09-18-04-51-30.gh-issue-96704.DmamRX.rst new file mode 100644 index 00000000000000..6ac99197e685c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-09-18-04-51-30.gh-issue-96704.DmamRX.rst @@ -0,0 +1 @@ +Pass the correct ``contextvars.Context`` when a ``asyncio`` exception handler is called on behalf of a task or callback handle. This adds a new ``Task`` method, ``get_context``, and also a new ``Handle`` method with the same name. If this method is not found on a task object (perhaps because it is a third-party library that does not yet provide this method), the context prevailing at the time the exception handler is called is used. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 909171150bdd36..efa0d2d6906eab 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2409,6 +2409,18 @@ _asyncio_Task_get_coro_impl(TaskObj *self) return self->task_coro; } +/*[clinic input] +_asyncio.Task.get_context +[clinic start generated code]*/ + +static PyObject * +_asyncio_Task_get_context_impl(TaskObj *self) +/*[clinic end generated code: output=6996f53d3dc01aef input=87c0b209b8fceeeb]*/ +{ + Py_INCREF(self->task_context); + return self->task_context; +} + /*[clinic input] _asyncio.Task.get_name [clinic start generated code]*/ @@ -2536,6 +2548,7 @@ static PyMethodDef TaskType_methods[] = { _ASYNCIO_TASK_GET_NAME_METHODDEF _ASYNCIO_TASK_SET_NAME_METHODDEF _ASYNCIO_TASK_GET_CORO_METHODDEF + _ASYNCIO_TASK_GET_CONTEXT_METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* Sentinel */ }; diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index daf524c3456ca8..ddec54c8d7c2bc 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -772,6 +772,23 @@ _asyncio_Task_get_coro(TaskObj *self, PyObject *Py_UNUSED(ignored)) return _asyncio_Task_get_coro_impl(self); } +PyDoc_STRVAR(_asyncio_Task_get_context__doc__, +"get_context($self, /)\n" +"--\n" +"\n"); + +#define _ASYNCIO_TASK_GET_CONTEXT_METHODDEF \ + {"get_context", (PyCFunction)_asyncio_Task_get_context, METH_NOARGS, _asyncio_Task_get_context__doc__}, + +static PyObject * +_asyncio_Task_get_context_impl(TaskObj *self); + +static PyObject * +_asyncio_Task_get_context(TaskObj *self, PyObject *Py_UNUSED(ignored)) +{ + return _asyncio_Task_get_context_impl(self); +} + PyDoc_STRVAR(_asyncio_Task_get_name__doc__, "get_name($self, /)\n" "--\n" @@ -1172,4 +1189,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs, exit: return return_value; } -/*[clinic end generated code: output=459a7c7f21bbc290 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f117b2246eaf7a55 input=a9049054013a1b77]*/ From 6d4d702fe5843334aafaf524f9bb8ed537c14e86 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 5 Oct 2022 08:52:35 +0100 Subject: [PATCH 039/151] gh-87092: bring compiler code closer to a preprocessing-opt-assembler organisation (GH-97644) --- Lib/test/test_compile.py | 14 ++++++- Python/compile.c | 88 ++++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 21dcc1a719cc25..8bf8470ff16feb 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -670,10 +670,22 @@ def test_merge_code_attrs(self): self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable) + @support.cpython_only + def test_strip_unused_consts(self): + def f(): + "docstring" + if True: + return "used" + else: + return "unused" + + self.assertEqual(f.__code__.co_consts, + ("docstring", True, "used")) + # Stripping unused constants is not a strict requirement for the # Python semantics, it's a more an implementation detail. @support.cpython_only - def test_strip_unused_consts(self): + def test_strip_unused_None(self): # Python 3.10rc1 appended None to co_consts when None is not used # at all. See bpo-45056. def f1(): diff --git a/Python/compile.c b/Python/compile.c index 507fd040a89d7d..2da36d0f6316e0 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -912,6 +912,19 @@ cfg_builder_use_label(cfg_builder *g, jump_target_label lbl) return cfg_builder_maybe_start_new_block(g); } +static inline int +basicblock_append_instructions(basicblock *target, basicblock *source) +{ + for (int i = 0; i < source->b_iused; i++) { + int n = basicblock_next_instr(target); + if (n < 0) { + return -1; + } + target->b_instr[n] = source->b_instr[i]; + } + return 0; +} + static basicblock * copy_basicblock(cfg_builder *g, basicblock *block) { @@ -923,12 +936,8 @@ copy_basicblock(cfg_builder *g, basicblock *block) if (result == NULL) { return NULL; } - for (int i = 0; i < block->b_iused; i++) { - int n = basicblock_next_instr(result); - if (n < 0) { - return NULL; - } - result->b_instr[n] = block->b_instr[i]; + if (basicblock_append_instructions(result, block) < 0) { + return NULL; } return result; } @@ -7080,15 +7089,14 @@ stackdepth(basicblock *entryblock, int code_flags) if (new_depth > maxdepth) { maxdepth = new_depth; } - assert(depth >= 0); /* invalid code or bug in stackdepth() */ if (HAS_TARGET(instr->i_opcode)) { effect = stack_effect(instr->i_opcode, instr->i_oparg, 1); assert(effect != PY_INVALID_STACK_EFFECT); int target_depth = depth + effect; + assert(target_depth >= 0); /* invalid code or bug in stackdepth() */ if (target_depth > maxdepth) { maxdepth = target_depth; } - assert(target_depth >= 0); /* invalid code or bug in stackdepth() */ stackdepth_push(&sp, instr->i_target, target_depth); } depth = new_depth; @@ -7487,6 +7495,9 @@ convert_exception_handlers_to_nops(basicblock *entryblock) { } } } + for (basicblock *b = entryblock; b != NULL; b = b->b_next) { + remove_redundant_nops(b); + } } static inline void @@ -7964,8 +7975,8 @@ scan_block_for_local(int target, basicblock *b, bool unsafe_to_start, #undef MAYBE_PUSH static int -add_checks_for_loads_of_unknown_variables(basicblock *entryblock, - struct compiler *c) +add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock, + struct compiler *c) { basicblock **stack = make_cfg_traversal_stack(entryblock); if (stack == NULL) { @@ -8291,7 +8302,7 @@ dump_basicblock(const basicblock *b) static int -calculate_jump_targets(basicblock *entryblock); +translate_jump_labels_to_targets(basicblock *entryblock); static int optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache); @@ -8628,11 +8639,9 @@ assemble(struct compiler *c, int addNone) } nlocalsplus -= numdropped; - consts = consts_dict_keys_inorder(c->u->u_consts); - if (consts == NULL) { - goto error; - } - if (calculate_jump_targets(g->g_entryblock)) { + /** Preprocessing **/ + /* Map labels to targets and mark exception handlers */ + if (translate_jump_labels_to_targets(g->g_entryblock)) { goto error; } if (mark_except_handlers(g->g_entryblock) < 0) { @@ -8641,18 +8650,31 @@ assemble(struct compiler *c, int addNone) if (label_exception_targets(g->g_entryblock)) { goto error; } + + /** Optimization **/ + consts = consts_dict_keys_inorder(c->u->u_consts); + if (consts == NULL) { + goto error; + } if (optimize_cfg(g, consts, c->c_const_cache)) { goto error; } - if (trim_unused_consts(g->g_entryblock, consts)) { + if (add_checks_for_loads_of_uninitialized_variables(g->g_entryblock, c) < 0) { goto error; } + + /** line numbers (TODO: move this before optimization stage) */ if (duplicate_exits_without_lineno(g) < 0) { goto error; } propagate_line_numbers(g->g_entryblock); guarantee_lineno_for_exits(g->g_entryblock, c->u->u_firstlineno); + if (push_cold_blocks_to_end(g, code_flags) < 0) { + goto error; + } + + /** Assembly **/ int maxdepth = stackdepth(g->g_entryblock, code_flags); if (maxdepth < 0) { goto error; @@ -8661,27 +8683,19 @@ assemble(struct compiler *c, int addNone) convert_exception_handlers_to_nops(g->g_entryblock); - if (push_cold_blocks_to_end(g, code_flags) < 0) { - goto error; - } - for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - remove_redundant_nops(b); - } - /* Order of basic blocks must have been determined by now */ if (normalize_jumps(g) < 0) { goto error; } - if (add_checks_for_loads_of_unknown_variables(g->g_entryblock, c) < 0) { - goto error; - } - assert(no_redundant_jumps(g)); /* Can't modify the bytecode after computing jump offsets. */ assemble_jump_offsets(g->g_entryblock); + if (trim_unused_consts(g->g_entryblock, consts)) { + goto error; + } /* Create assembler */ if (!assemble_init(&a, c->u->u_firstlineno)) @@ -9265,12 +9279,8 @@ inline_small_exit_blocks(basicblock *bb) { basicblock *target = last->i_target; if (basicblock_exits_scope(target) && target->b_iused <= MAX_COPY_SIZE) { last->i_opcode = NOP; - for (int i = 0; i < target->b_iused; i++) { - int index = basicblock_next_instr(bb); - if (index < 0) { - return -1; - } - bb->b_instr[index] = target->b_instr[i]; + if (basicblock_append_instructions(bb, target) < 0) { + return -1; } return 1; } @@ -9456,7 +9466,7 @@ propagate_line_numbers(basicblock *entryblock) { /* Calculate the actual jump target from the target_label */ static int -calculate_jump_targets(basicblock *entryblock) +translate_jump_labels_to_targets(basicblock *entryblock) { int max_label = -1; for (basicblock *b = entryblock; b != NULL; b = b->b_next) { @@ -9599,12 +9609,14 @@ is_exit_without_lineno(basicblock *b) { static int duplicate_exits_without_lineno(cfg_builder *g) { + assert(no_empty_basic_blocks(g)); /* Copy all exit blocks without line number that are targets of a jump. */ basicblock *entryblock = g->g_entryblock; for (basicblock *b = entryblock; b != NULL; b = b->b_next) { struct instr *last = basicblock_last_instr(b); - if (last != NULL && is_jump(last)) { + assert(last != NULL); + if (is_jump(last)) { basicblock *target = last->i_target; if (is_exit_without_lineno(target) && target->b_predecessors > 1) { basicblock *new_target = copy_basicblock(g, target); @@ -9621,8 +9633,6 @@ duplicate_exits_without_lineno(cfg_builder *g) } } - assert(no_empty_basic_blocks(g)); - /* Any remaining reachable exit blocks without line number can only be reached by * fall through, and thus can only have a single predecessor */ for (basicblock *b = entryblock; b != NULL; b = b->b_next) { @@ -9775,7 +9785,7 @@ _PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts) if (const_cache == NULL) { goto error; } - if (calculate_jump_targets(g.g_entryblock)) { + if (translate_jump_labels_to_targets(g.g_entryblock)) { goto error; } if (optimize_cfg(&g, consts, const_cache) < 0) { From 45b9d051e92b97c853b4bd8f40f3ea60051a1c2a Mon Sep 17 00:00:00 2001 From: Jia Junjie <62194633+jiajunjie@users.noreply.github.com> Date: Wed, 5 Oct 2022 16:47:54 +0800 Subject: [PATCH 040/151] gh-97661: Improve accuracy of sqlite3.Cursor.fetchone docs (#97662) Co-authored-by: C.A.M. Gerlach --- Doc/library/sqlite3.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index e2774a502403e7..26a085877ec112 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -1425,7 +1425,9 @@ Cursor objects .. method:: fetchone() - Return the next row of a query result set as a :class:`tuple`. + If :attr:`~Connection.row_factory` is ``None``, + return the next row query result set as a :class:`tuple`. + Else, pass it to the row factory and return its result. Return ``None`` if no more data is available. From c34bef63a82aa3f2259fcae5d7aa106e13dfba8b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Oct 2022 12:48:59 +0300 Subject: [PATCH 041/151] gh-74696: Pass root_dir to custom archivers which support it (GH-94251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric --- Doc/library/shutil.rst | 14 +++++- Doc/whatsnew/3.12.rst | 9 ++++ Lib/shutil.py | 20 ++++---- Lib/test/test_shutil.py | 49 ++++++++++++++++--- ...2-06-25-09-12-23.gh-issue-74696.fxC9ua.rst | 2 + 5 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-06-25-09-12-23.gh-issue-74696.fxC9ua.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 8f1668f76b9027..b33dbe21b1fa19 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -575,9 +575,10 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. .. note:: This function is not thread-safe when custom archivers registered - with :func:`register_archive_format` are used. In this case it + with :func:`register_archive_format` do not support the *root_dir* + argument. In this case it temporarily changes the current working directory of the process - to perform archiving. + to *root_dir* to perform archiving. .. versionchanged:: 3.8 The modern pax (POSIX.1-2001) format is now used instead of @@ -614,12 +615,21 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Further arguments are passed as keyword arguments: *owner*, *group*, *dry_run* and *logger* (as passed in :func:`make_archive`). + If *function* has the custom attribute ``function.supports_root_dir`` set to ``True``, + the *root_dir* argument is passed as a keyword argument. + Otherwise the current working directory of the process is temporarily + changed to *root_dir* before calling *function*. + In this case :func:`make_archive` is not thread-safe. + If given, *extra_args* is a sequence of ``(name, value)`` pairs that will be used as extra keywords arguments when the archiver callable is used. *description* is used by :func:`get_archive_formats` which returns the list of archivers. Defaults to an empty string. + .. versionchanged:: 3.12 + Added support for functions supporting the *root_dir* argument. + .. function:: unregister_archive_format(name) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 052507a4873f81..62ec2de2e78c99 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -127,6 +127,15 @@ os for a process with :func:`os.pidfd_open` in non-blocking mode. (Contributed by Kumar Aditya in :gh:`93312`.) +shutil +------ + +* :func:`shutil.make_archive` now passes the *root_dir* argument to custom + archivers which support it. + In this case it no longer temporarily changes the current working directory + of the process to *root_dir* to perform archiving. + (Contributed by Serhiy Storchaka in :gh:`74696`.) + sqlite3 ------- diff --git a/Lib/shutil.py b/Lib/shutil.py index b49437cd1f3e87..ac1dd530528c0a 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -1023,28 +1023,30 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, zip_filename = os.path.abspath(zip_filename) return zip_filename +_make_tarball.supports_root_dir = True +_make_zipfile.supports_root_dir = True + # Maps the name of the archive format to a tuple containing: # * the archiving function # * extra keyword arguments # * description -# * does it support the root_dir argument? _ARCHIVE_FORMATS = { 'tar': (_make_tarball, [('compress', None)], - "uncompressed tar file", True), + "uncompressed tar file"), } if _ZLIB_SUPPORTED: _ARCHIVE_FORMATS['gztar'] = (_make_tarball, [('compress', 'gzip')], - "gzip'ed tar-file", True) - _ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file", True) + "gzip'ed tar-file") + _ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file") if _BZ2_SUPPORTED: _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], - "bzip2'ed tar-file", True) + "bzip2'ed tar-file") if _LZMA_SUPPORTED: _ARCHIVE_FORMATS['xztar'] = (_make_tarball, [('compress', 'xz')], - "xz'ed tar-file", True) + "xz'ed tar-file") def get_archive_formats(): """Returns a list of supported formats for archiving and unarchiving. @@ -1075,7 +1077,7 @@ def register_archive_format(name, function, extra_args=None, description=''): if not isinstance(element, (tuple, list)) or len(element) !=2: raise TypeError('extra_args elements are : (arg_name, value)') - _ARCHIVE_FORMATS[name] = (function, extra_args, description, False) + _ARCHIVE_FORMATS[name] = (function, extra_args, description) def unregister_archive_format(name): del _ARCHIVE_FORMATS[name] @@ -1114,10 +1116,10 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, if base_dir is None: base_dir = os.curdir - support_root_dir = format_info[3] + supports_root_dir = getattr(func, 'supports_root_dir', False) save_cwd = None if root_dir is not None: - if support_root_dir: + if supports_root_dir: # Support path-like base_name here for backwards-compatibility. base_name = os.fspath(base_name) kwargs['root_dir'] = root_dir diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index a2c4ab508195b3..6789fe4cc72e3a 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1568,28 +1568,65 @@ def test_tarfile_root_owner(self): finally: archive.close() + def test_make_archive_cwd_default(self): + current_dir = os.getcwd() + def archiver(base_name, base_dir, **kw): + self.assertNotIn('root_dir', kw) + self.assertEqual(base_name, 'basename') + self.assertEqual(os.getcwd(), current_dir) + raise RuntimeError() + + register_archive_format('xxx', archiver, [], 'xxx file') + try: + with no_chdir: + with self.assertRaises(RuntimeError): + make_archive('basename', 'xxx') + self.assertEqual(os.getcwd(), current_dir) + finally: + unregister_archive_format('xxx') + def test_make_archive_cwd(self): current_dir = os.getcwd() root_dir = self.mkdtemp() - def _breaks(*args, **kw): + def archiver(base_name, base_dir, **kw): + self.assertNotIn('root_dir', kw) + self.assertEqual(base_name, os.path.join(current_dir, 'basename')) + self.assertEqual(os.getcwd(), root_dir) raise RuntimeError() dirs = [] def _chdir(path): dirs.append(path) orig_chdir(path) - register_archive_format('xxx', _breaks, [], 'xxx file') + register_archive_format('xxx', archiver, [], 'xxx file') try: with support.swap_attr(os, 'chdir', _chdir) as orig_chdir: - try: - make_archive('xxx', 'xxx', root_dir=root_dir) - except Exception: - pass + with self.assertRaises(RuntimeError): + make_archive('basename', 'xxx', root_dir=root_dir) self.assertEqual(os.getcwd(), current_dir) self.assertEqual(dirs, [root_dir, current_dir]) finally: unregister_archive_format('xxx') + def test_make_archive_cwd_supports_root_dir(self): + current_dir = os.getcwd() + root_dir = self.mkdtemp() + def archiver(base_name, base_dir, **kw): + self.assertEqual(base_name, 'basename') + self.assertEqual(kw['root_dir'], root_dir) + self.assertEqual(os.getcwd(), current_dir) + raise RuntimeError() + archiver.supports_root_dir = True + + register_archive_format('xxx', archiver, [], 'xxx file') + try: + with no_chdir: + with self.assertRaises(RuntimeError): + make_archive('basename', 'xxx', root_dir=root_dir) + self.assertEqual(os.getcwd(), current_dir) + finally: + unregister_archive_format('xxx') + def test_make_tarfile_in_curdir(self): # Issue #21280 root_dir = self.mkdtemp() diff --git a/Misc/NEWS.d/next/Library/2022-06-25-09-12-23.gh-issue-74696.fxC9ua.rst b/Misc/NEWS.d/next/Library/2022-06-25-09-12-23.gh-issue-74696.fxC9ua.rst new file mode 100644 index 00000000000000..48beaff59a16a8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-25-09-12-23.gh-issue-74696.fxC9ua.rst @@ -0,0 +1,2 @@ +:func:`shutil.make_archive` now passes the *root_dir* argument to custom +archivers which support it. From 18523e03fdfc5736292e9640ba84d1f89750e086 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Oct 2022 12:51:58 +0300 Subject: [PATCH 042/151] gh-97758: Fix a crash in getpath_joinpath() called without arguments (GH-97759) --- Modules/getpath.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/getpath.c b/Modules/getpath.c index be704adbde9468..ceacf36d8968a1 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -261,7 +261,7 @@ getpath_joinpath(PyObject *Py_UNUSED(self), PyObject *args) } Py_ssize_t n = PyTuple_GET_SIZE(args); if (n == 0) { - return PyUnicode_FromString(NULL); + return PyUnicode_FromStringAndSize(NULL, 0); } /* Convert all parts to wchar and accumulate max final length */ wchar_t **parts = (wchar_t **)PyMem_Malloc(n * sizeof(wchar_t *)); From a47df46921119538000cae443cf2a99f24339cb6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Oct 2022 14:21:16 +0300 Subject: [PATCH 043/151] gh-95196: Disable incorrect pickling of the C implemented classmethod descriptors (GH-96383) --- Lib/test/pickletester.py | 18 ++++++++++++++++++ ...22-08-29-13-06-58.gh-issue-95196.eGRR4b.rst | 1 + Objects/descrobject.c | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-08-29-13-06-58.gh-issue-95196.eGRR4b.rst diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 21419e11c87497..499f80a15f3422 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -2776,6 +2776,15 @@ def pie(self): unpickled = self.loads(self.dumps(method, proto)) self.assertEqual(method(obj), unpickled(obj)) + descriptors = ( + PyMethodsTest.__dict__['cheese'], # static method descriptor + PyMethodsTest.__dict__['wine'], # class method descriptor + ) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + for descr in descriptors: + with self.subTest(proto=proto, descr=descr): + self.assertRaises(TypeError, self.dumps, descr, proto) + def test_c_methods(self): global Subclass class Subclass(tuple): @@ -2811,6 +2820,15 @@ class Nested(str): unpickled = self.loads(self.dumps(method, proto)) self.assertEqual(method(*args), unpickled(*args)) + descriptors = ( + bytearray.__dict__['maketrans'], # built-in static method descriptor + dict.__dict__['fromkeys'], # built-in class method descriptor + ) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + for descr in descriptors: + with self.subTest(proto=proto, descr=descr): + self.assertRaises(TypeError, self.dumps, descr, proto) + def test_compat_pickle(self): tests = [ (range(1, 7), '__builtin__', 'xrange'), diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-08-29-13-06-58.gh-issue-95196.eGRR4b.rst b/Misc/NEWS.d/next/Core and Builtins/2022-08-29-13-06-58.gh-issue-95196.eGRR4b.rst new file mode 100644 index 00000000000000..37534fa1752550 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-08-29-13-06-58.gh-issue-95196.eGRR4b.rst @@ -0,0 +1 @@ +Disable incorrect pickling of the C implemented classmethod descriptors. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 82570e085143ed..a2974f91aaaec3 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -776,7 +776,7 @@ PyTypeObject PyClassMethodDescr_Type = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - descr_methods, /* tp_methods */ + 0, /* tp_methods */ descr_members, /* tp_members */ method_getset, /* tp_getset */ 0, /* tp_base */ From 2d20cf543a7e0a1a5713e7fae03da19336d9b47b Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Wed, 5 Oct 2022 18:31:43 +0400 Subject: [PATCH 044/151] gh-93357: Port test cases to IsolatedAsyncioTestCase, part 2 (#97896) This fixes the buildbots. --- Lib/test/test_asyncio/test_streams.py | 44 +++++++++------------------ 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index d1f8aef4bb9cbd..61d5e984dfbfbb 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -941,34 +941,32 @@ def test_LimitOverrunError_pickleable(self): self.assertEqual(str(e), str(e2)) self.assertEqual(e.consumed, e2.consumed) +class NewStreamTests2(unittest.IsolatedAsyncioTestCase): async def test_wait_closed_on_close(self): - async with test_utils.run_test_server() as httpd: - rd, wr = self.loop.run_until_complete( - asyncio.open_connection(*httpd.address)) + with test_utils.run_test_server() as httpd: + rd, wr = await asyncio.open_connection(*httpd.address) wr.write(b'GET / HTTP/1.0\r\n\r\n') data = await rd.readline() self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') - await rd.read() + data = await rd.read() self.assertTrue(data.endswith(b'\r\n\r\nTest message')) self.assertFalse(wr.is_closing()) wr.close() self.assertTrue(wr.is_closing()) await wr.wait_closed() - def test_wait_closed_on_close_with_unread_data(self): + async def test_wait_closed_on_close_with_unread_data(self): with test_utils.run_test_server() as httpd: - rd, wr = self.loop.run_until_complete( - asyncio.open_connection(*httpd.address)) + rd, wr = await asyncio.open_connection(*httpd.address) wr.write(b'GET / HTTP/1.0\r\n\r\n') - f = rd.readline() - data = self.loop.run_until_complete(f) + data = await rd.readline() self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') wr.close() - self.loop.run_until_complete(wr.wait_closed()) + await wr.wait_closed() - def test_async_writer_api(self): + async def test_async_writer_api(self): async def inner(httpd): rd, wr = await asyncio.open_connection(*httpd.address) @@ -980,15 +978,10 @@ async def inner(httpd): wr.close() await wr.wait_closed() - messages = [] - self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - with test_utils.run_test_server() as httpd: - self.loop.run_until_complete(inner(httpd)) - - self.assertEqual(messages, []) + await inner(httpd) - def test_async_writer_api_exception_after_close(self): + async def test_async_writer_api_exception_after_close(self): async def inner(httpd): rd, wr = await asyncio.open_connection(*httpd.address) @@ -1002,24 +995,17 @@ async def inner(httpd): wr.write(b'data') await wr.drain() - messages = [] - self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - with test_utils.run_test_server() as httpd: - self.loop.run_until_complete(inner(httpd)) - - self.assertEqual(messages, []) + await inner(httpd) async def test_eof_feed_when_closing_writer(self): # See http://bugs.python.org/issue35065 - async with test_utils.run_test_server() as httpd: + with test_utils.run_test_server() as httpd: rd, wr = await asyncio.open_connection(*httpd.address) wr.close() - f = wr.wait_closed() - self.loop.run_until_complete(f) + await wr.wait_closed() self.assertTrue(rd.at_eof()) - f = rd.read() - data = self.loop.run_until_complete(f) + data = await rd.read() self.assertEqual(data, b'') From d1e99a7bca044c3cd17fe54cc59efc1626b7d4be Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 16:22:28 +0100 Subject: [PATCH 045/151] gh-93738: Documentation C syntax (Function glob patterns -> literal markup) (#97774) --- Doc/c-api/arg.rst | 8 ++++---- Doc/c-api/exceptions.rst | 4 ++-- Doc/c-api/init.rst | 6 +++--- Doc/c-api/module.rst | 4 ++-- Doc/c-api/typeobj.rst | 6 +++--- Doc/extending/extending.rst | 6 +++--- Doc/whatsnew/2.5.rst | 6 +++--- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index c9dcf746ef2f9f..702c0869116ac7 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -298,7 +298,7 @@ Other objects status = converter(object, address); where *object* is the Python object to be converted and *address* is the - :c:type:`void*` argument that was passed to the :c:func:`PyArg_Parse\*` function. + :c:type:`void*` argument that was passed to the ``PyArg_Parse*`` function. The returned *status* should be ``1`` for a successful conversion and ``0`` if the conversion has failed. When the conversion fails, the *converter* function should raise an exception and leave the content of *address* unmodified. @@ -372,9 +372,9 @@ what is specified for the corresponding format unit in that case. For the conversion to succeed, the *arg* object must match the format and the format must be exhausted. On success, the -:c:func:`PyArg_Parse\*` functions return true, otherwise they return +``PyArg_Parse*`` functions return true, otherwise they return false and raise an appropriate exception. When the -:c:func:`PyArg_Parse\*` functions fail due to conversion failure in one +``PyArg_Parse*`` functions fail due to conversion failure in one of the format units, the variables at the addresses corresponding to that and the following format units are left untouched. @@ -481,7 +481,7 @@ Building values .. c:function:: PyObject* Py_BuildValue(const char *format, ...) Create a new value based on a format string similar to those accepted by the - :c:func:`PyArg_Parse\*` family of functions and a sequence of values. Returns + ``PyArg_Parse*`` family of functions and a sequence of values. Returns the value or ``NULL`` in the case of an error; an exception will be raised if ``NULL`` is returned. diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 7221957fe1dbac..087e0a61d12d59 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -14,7 +14,7 @@ there is a global indicator (per thread) of the last error that occurred. Most C API functions don't clear this on success, but will set it to indicate the cause of the error on failure. Most C API functions also return an error indicator, usually ``NULL`` if they are supposed to return a pointer, or ``-1`` -if they return an integer (exception: the :c:func:`PyArg_\*` functions +if they return an integer (exception: the ``PyArg_*`` functions return ``1`` for success and ``0`` for failure). Concretely, the error indicator consists of three object pointers: the @@ -370,7 +370,7 @@ Querying the error indicator .. c:function:: PyObject* PyErr_Occurred() Test whether the error indicator is set. If set, return the exception *type* - (the first argument to the last call to one of the :c:func:`PyErr_Set\*` + (the first argument to the last call to one of the ``PyErr_Set*`` functions or to :c:func:`PyErr_Restore`). If not set, return ``NULL``. You do not own a reference to the return value, so you do not need to :c:func:`Py_DECREF` it. diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index cb3bfedc97e88a..efa58381d270ff 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -956,11 +956,11 @@ from a C thread is:: /* Release the thread. No Python API allowed beyond this point. */ PyGILState_Release(gstate); -Note that the :c:func:`PyGILState_\*` functions assume there is only one global +Note that the ``PyGILState_*`` functions assume there is only one global interpreter (created automatically by :c:func:`Py_Initialize`). Python supports the creation of additional interpreters (using :c:func:`Py_NewInterpreter`), but mixing multiple interpreters and the -:c:func:`PyGILState_\*` API is unsupported. +``PyGILState_*`` API is unsupported. .. _fork-and-threads: @@ -1587,7 +1587,7 @@ operations executed by such objects may affect the wrong (sub-)interpreter's dictionary of loaded modules. It is equally important to avoid sharing objects from which the above are reachable. -Also note that combining this functionality with :c:func:`PyGILState_\*` APIs +Also note that combining this functionality with ``PyGILState_*`` APIs is delicate, because these APIs assume a bijection between Python thread states and OS-level threads, an assumption broken by the presence of sub-interpreters. It is highly recommended that you don't switch sub-interpreters between a pair diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 94c8d9f981713f..e2ba157b32c7d9 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -64,8 +64,8 @@ Module Objects If *module* is not a module object (or a subtype of a module object), :exc:`SystemError` is raised and ``NULL`` is returned. - It is recommended extensions use other :c:func:`PyModule_\*` and - :c:func:`PyObject_\*` functions rather than directly manipulate a module's + It is recommended extensions use other ``PyModule_*`` and + ``PyObject_*`` functions rather than directly manipulate a module's :attr:`~object.__dict__`. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 86c0830e7a9cf1..8ccdece3efc54c 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -7,8 +7,8 @@ Type Objects Perhaps one of the most important structures of the Python object system is the structure that defines a new type: the :c:type:`PyTypeObject` structure. Type -objects can be handled using any of the :c:func:`PyObject_\*` or -:c:func:`PyType_\*` functions, but do not offer much that's interesting to most +objects can be handled using any of the ``PyObject_*`` or +``PyType_*`` functions, but do not offer much that's interesting to most Python applications. These objects are fundamental to how objects behave, so they are very important to the interpreter itself and to any extension module that implements new types. @@ -1519,7 +1519,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) If the instances of this type are weakly referenceable, this field is greater than zero and contains the offset in the instance structure of the weak reference list head (ignoring the GC header, if present); this offset is used by - :c:func:`PyObject_ClearWeakRefs` and the :c:func:`PyWeakref_\*` functions. The + :c:func:`PyObject_ClearWeakRefs` and the ``PyWeakref_*`` functions. The instance structure needs to include a field of type :c:expr:`PyObject*` which is initialized to ``NULL``. diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 2e3362b834e6fb..0ef899f4c997b1 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -157,16 +157,16 @@ since you should be able to tell from the return value. When a function *f* that calls another function *g* detects that the latter fails, *f* should itself return an error value (usually ``NULL`` or ``-1``). It -should *not* call one of the :c:func:`PyErr_\*` functions --- one has already +should *not* call one of the ``PyErr_*`` functions --- one has already been called by *g*. *f*'s caller is then supposed to also return an error -indication to *its* caller, again *without* calling :c:func:`PyErr_\*`, and so on +indication to *its* caller, again *without* calling ``PyErr_*``, and so on --- the most detailed cause of the error was already reported by the function that first detected it. Once the error reaches the Python interpreter's main loop, this aborts the currently executing Python code and tries to find an exception handler specified by the Python programmer. (There are situations where a module can actually give a more detailed error -message by calling another :c:func:`PyErr_\*` function, and in such cases it is +message by calling another ``PyErr_*`` function, and in such cases it is fine to do so. As a general rule, however, this is not necessary, and can cause information about the cause of the error to be lost: most operations can fail for a variety of reasons.) diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index 6c216826fee047..dfa8f7e93f8177 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -2270,9 +2270,9 @@ code: earlier section :ref:`pep-353` for a discussion of this change. * C API: The obmalloc changes mean that you must be careful to not mix usage - of the :c:func:`PyMem_\*` and :c:func:`PyObject_\*` families of functions. Memory - allocated with one family's :c:func:`\*_Malloc` must be freed with the - corresponding family's :c:func:`\*_Free` function. + of the ``PyMem_*`` and ``PyObject_*`` families of functions. Memory + allocated with one family's ``*_Malloc`` must be freed with the + corresponding family's ``*_Free`` function. .. ====================================================================== From 129a24f6ab09d9f041e1ec189c56d2eac9cd563b Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Wed, 5 Oct 2022 22:45:31 +0530 Subject: [PATCH 046/151] gh-88050: Fix asyncio subprocess to kill process cleanly when process is blocked (#32073) --- Lib/asyncio/base_subprocess.py | 13 +++++----- Lib/test/test_asyncio/test_subprocess.py | 25 +++++++++++++++++++ ...2-07-08-08-39-35.gh-issue-88050.0aOC_m.rst | 1 + 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py index 14d50519228814..c2ca4a2792f666 100644 --- a/Lib/asyncio/base_subprocess.py +++ b/Lib/asyncio/base_subprocess.py @@ -215,14 +215,10 @@ def _process_exited(self, returncode): # object. On Python 3.6, it is required to avoid a ResourceWarning. self._proc.returncode = returncode self._call(self._protocol.process_exited) + for p in self._pipes.values(): + p.pipe.close() self._try_finish() - # wake up futures waiting for wait() - for waiter in self._exit_waiters: - if not waiter.cancelled(): - waiter.set_result(returncode) - self._exit_waiters = None - async def _wait(self): """Wait until the process exit and return the process return code. @@ -247,6 +243,11 @@ def _call_connection_lost(self, exc): try: self._protocol.connection_lost(exc) finally: + # wake up futures waiting for wait() + for waiter in self._exit_waiters: + if not waiter.cancelled(): + waiter.set_result(self._returncode) + self._exit_waiters = None self._loop = None self._proc = None self._protocol = None diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 961c463f8dc11b..9bc60b9dc2ae2e 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -1,4 +1,5 @@ import os +import shutil import signal import sys import unittest @@ -182,6 +183,30 @@ def test_kill(self): else: self.assertEqual(-signal.SIGKILL, returncode) + def test_kill_issue43884(self): + blocking_shell_command = f'{sys.executable} -c "import time; time.sleep(100000000)"' + creationflags = 0 + if sys.platform == 'win32': + from subprocess import CREATE_NEW_PROCESS_GROUP + # On windows create a new process group so that killing process + # kills the process and all its children. + creationflags = CREATE_NEW_PROCESS_GROUP + proc = self.loop.run_until_complete( + asyncio.create_subprocess_shell(blocking_shell_command, stdout=asyncio.subprocess.PIPE, + creationflags=creationflags) + ) + self.loop.run_until_complete(asyncio.sleep(1)) + if sys.platform == 'win32': + proc.send_signal(signal.CTRL_BREAK_EVENT) + # On windows it is an alias of terminate which sets the return code + proc.kill() + returncode = self.loop.run_until_complete(proc.wait()) + if sys.platform == 'win32': + self.assertIsInstance(returncode, int) + # expect 1 but sometimes get 0 + else: + self.assertEqual(-signal.SIGKILL, returncode) + def test_terminate(self): args = PROGRAM_BLOCKED proc = self.loop.run_until_complete( diff --git a/Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst b/Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst new file mode 100644 index 00000000000000..43c0765d940d3a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst @@ -0,0 +1 @@ +Fix :mod:`asyncio` subprocess transport to kill process cleanly when process is blocked and avoid ``RuntimeError`` when loop is closed. Patch by Kumar Aditya. From 4017dd4f8d7c49ee5c6cd2729b8c1b0a8dcaa0d2 Mon Sep 17 00:00:00 2001 From: 180909 <734461790@qq.com> Date: Thu, 6 Oct 2022 01:52:59 +0800 Subject: [PATCH 047/151] GH-95172 Make the same version `versionadded` oneline (#95172) * Make the same version versionadded oneline * Format versionadded for enum.rst * Format versionadded A single line versionadded was reading better. Co-authored-by: Senthil Kumaran --- Doc/library/enum.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 01743b002ba96c..a30a7a4ca34300 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -134,8 +134,7 @@ Module Contents .. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto`` -.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``, ``property`` -.. versionadded:: 3.11 ``member``, ``nonmember`` +.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``, ``property``, ``member``, ``nonmember`` --------------- From 6eee387094cca33467963cdb5b28491d3c8284fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Oct 2022 10:56:42 -0700 Subject: [PATCH 048/151] build(deps): bump actions/stale from 5 to 6 (#97701) Bumps [actions/stale](https://github.com/actions/stale) from 5 to 6. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v5...v6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index f422707afb9e5c..1c6f1641c885c6 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -15,7 +15,7 @@ jobs: steps: - name: "Check PRs" - uses: actions/stale@v5 + uses: actions/stale@v6 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity.' From f200192c6d83899df8c1618181203534681bcdcc Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 5 Oct 2022 19:57:52 +0200 Subject: [PATCH 049/151] gh-91539: improve performance of get_proxies_environment (#91566) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve performance of get_proxies_environment when there are many environment variables * 📜🤖 Added by blurb_it. * fix case of short env name * fix formatting * fix whitespace * whitespace * Update Lib/urllib/request.py Co-authored-by: Carl Meyer * Update Lib/urllib/request.py Co-authored-by: Carl Meyer * Update Lib/urllib/request.py Co-authored-by: Carl Meyer * Update Lib/urllib/request.py Co-authored-by: Carl Meyer * whitespace * Update Misc/NEWS.d/next/Library/2022-04-15-11-29-38.gh-issue-91539.7WgVuA.rst Co-authored-by: Carl Meyer * Update Lib/urllib/request.py Co-authored-by: Carl Meyer Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Carl Meyer --- Lib/urllib/request.py | 26 ++++++++++++------- ...2-04-15-11-29-38.gh-issue-91539.7WgVuA.rst | 1 + 2 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-04-15-11-29-38.gh-issue-91539.7WgVuA.rst diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 1761e951e62466..e2d5b8c5c59a3c 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -2508,28 +2508,34 @@ def getproxies_environment(): this seems to be the standard convention. If you need a different way, you can pass a proxies dictionary to the [Fancy]URLopener constructor. - """ - proxies = {} # in order to prefer lowercase variables, process environment in # two passes: first matches any, second pass matches lowercase only - for name, value in os.environ.items(): - name = name.lower() - if value and name[-6:] == '_proxy': - proxies[name[:-6]] = value + + # select only environment variables which end in (after making lowercase) _proxy + proxies = {} + environment = [] + for name in os.environ.keys(): + # fast screen underscore position before more expensive case-folding + if len(name) > 5 and name[-6] == "_" and name[-5:].lower() == "proxy": + value = os.environ[name] + proxy_name = name[:-6].lower() + environment.append((name, value, proxy_name)) + if value: + proxies[proxy_name] = value # CVE-2016-1000110 - If we are running as CGI script, forget HTTP_PROXY # (non-all-lowercase) as it may be set from the web server by a "Proxy:" # header from the client # If "proxy" is lowercase, it will still be used thanks to the next block if 'REQUEST_METHOD' in os.environ: proxies.pop('http', None) - for name, value in os.environ.items(): + for name, value, proxy_name in environment: + # not case-folded, checking here for lower-case env vars only if name[-6:] == '_proxy': - name = name.lower() if value: - proxies[name[:-6]] = value + proxies[proxy_name] = value else: - proxies.pop(name[:-6], None) + proxies.pop(proxy_name, None) return proxies def proxy_bypass_environment(host, proxies=None): diff --git a/Misc/NEWS.d/next/Library/2022-04-15-11-29-38.gh-issue-91539.7WgVuA.rst b/Misc/NEWS.d/next/Library/2022-04-15-11-29-38.gh-issue-91539.7WgVuA.rst new file mode 100644 index 00000000000000..16d61f1b91102d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-15-11-29-38.gh-issue-91539.7WgVuA.rst @@ -0,0 +1 @@ +Improve performance of ``urllib.request.getproxies_environment`` when there are many environment variables From 5da6c6b7bdfcf334ba4f4b15c96ba0d6062b419a Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Oct 2022 19:01:14 +0100 Subject: [PATCH 050/151] gh-93738: Documentation C syntax (:c:type: -> :c:expr:) (#97768) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :c:type:`` -> :c:expr:`` Co-authored-by: Łukasz Langa --- Doc/c-api/arg.rst | 72 +++++++++++----------- Doc/c-api/capsule.rst | 2 +- Doc/c-api/complex.rst | 4 +- Doc/c-api/conversion.rst | 4 +- Doc/c-api/dict.rst | 4 +- Doc/c-api/file.rst | 2 +- Doc/c-api/float.rst | 16 ++--- Doc/c-api/init.rst | 28 ++++----- Doc/c-api/intro.rst | 4 +- Doc/c-api/long.rst | 42 ++++++------- Doc/c-api/marshal.rst | 12 ++-- Doc/c-api/memory.rst | 12 ++-- Doc/c-api/sys.rst | 6 +- Doc/c-api/unicode.rst | 24 ++++---- Doc/extending/extending.rst | 8 +-- Doc/extending/newtypes.rst | 6 +- Doc/library/ctypes.rst | 120 ++++++++++++++++++------------------ Doc/library/posix.rst | 4 +- Doc/library/socket.rst | 2 +- Doc/library/stdtypes.rst | 2 +- Doc/library/struct.rst | 42 ++++++------- Doc/reference/datamodel.rst | 2 +- Doc/whatsnew/2.2.rst | 2 +- Doc/whatsnew/2.3.rst | 4 +- Doc/whatsnew/2.4.rst | 8 +-- Doc/whatsnew/2.5.rst | 20 +++--- Doc/whatsnew/2.6.rst | 2 +- Doc/whatsnew/2.7.rst | 4 +- Doc/whatsnew/3.3.rst | 2 +- Doc/whatsnew/3.7.rst | 14 ++--- Doc/whatsnew/3.9.rst | 2 +- Misc/NEWS.d/3.5.0a1.rst | 2 +- Misc/NEWS.d/3.9.0a1.rst | 2 +- Misc/NEWS.d/3.9.0b1.rst | 2 +- 34 files changed, 241 insertions(+), 241 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 702c0869116ac7..c5be453c153308 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -152,10 +152,10 @@ which disallows mutable objects such as :class:`bytearray`. It only works for encoded data without embedded NUL bytes. This format requires two arguments. The first is only used as input, and - must be a :c:type:`const char*` which points to the name of an encoding as a + must be a :c:expr:`const char*` which points to the name of an encoding as a NUL-terminated string, or ``NULL``, in which case ``'utf-8'`` encoding is used. An exception is raised if the named encoding is not known to Python. The - second argument must be a :c:type:`char**`; the value of the pointer it + second argument must be a :c:expr:`char**`; the value of the pointer it references will be set to a buffer with the contents of the argument text. The text will be encoded in the encoding specified by the first argument. @@ -175,10 +175,10 @@ which disallows mutable objects such as :class:`bytearray`. characters. It requires three arguments. The first is only used as input, and must be a - :c:type:`const char*` which points to the name of an encoding as a + :c:expr:`const char*` which points to the name of an encoding as a NUL-terminated string, or ``NULL``, in which case ``'utf-8'`` encoding is used. An exception is raised if the named encoding is not known to Python. The - second argument must be a :c:type:`char**`; the value of the pointer it + second argument must be a :c:expr:`char**`; the value of the pointer it references will be set to a buffer with the contents of the argument text. The text will be encoded in the encoding specified by the first argument. The third argument must be a pointer to an integer; the referenced integer @@ -215,38 +215,38 @@ Numbers ``b`` (:class:`int`) [unsigned char] Convert a nonnegative Python integer to an unsigned tiny int, stored in a C - :c:type:`unsigned char`. + :c:expr:`unsigned char`. ``B`` (:class:`int`) [unsigned char] Convert a Python integer to a tiny int without overflow checking, stored in a C - :c:type:`unsigned char`. + :c:expr:`unsigned char`. ``h`` (:class:`int`) [short int] - Convert a Python integer to a C :c:type:`short int`. + Convert a Python integer to a C :c:expr:`short int`. ``H`` (:class:`int`) [unsigned short int] - Convert a Python integer to a C :c:type:`unsigned short int`, without overflow + Convert a Python integer to a C :c:expr:`unsigned short int`, without overflow checking. ``i`` (:class:`int`) [int] - Convert a Python integer to a plain C :c:type:`int`. + Convert a Python integer to a plain C :c:expr:`int`. ``I`` (:class:`int`) [unsigned int] - Convert a Python integer to a C :c:type:`unsigned int`, without overflow + Convert a Python integer to a C :c:expr:`unsigned int`, without overflow checking. ``l`` (:class:`int`) [long int] - Convert a Python integer to a C :c:type:`long int`. + Convert a Python integer to a C :c:expr:`long int`. ``k`` (:class:`int`) [unsigned long] - Convert a Python integer to a C :c:type:`unsigned long` without + Convert a Python integer to a C :c:expr:`unsigned long` without overflow checking. ``L`` (:class:`int`) [long long] - Convert a Python integer to a C :c:type:`long long`. + Convert a Python integer to a C :c:expr:`long long`. ``K`` (:class:`int`) [unsigned long long] - Convert a Python integer to a C :c:type:`unsigned long long` + Convert a Python integer to a C :c:expr:`unsigned long long` without overflow checking. ``n`` (:class:`int`) [:c:type:`Py_ssize_t`] @@ -254,20 +254,20 @@ Numbers ``c`` (:class:`bytes` or :class:`bytearray` of length 1) [char] Convert a Python byte, represented as a :class:`bytes` or - :class:`bytearray` object of length 1, to a C :c:type:`char`. + :class:`bytearray` object of length 1, to a C :c:expr:`char`. .. versionchanged:: 3.3 Allow :class:`bytearray` objects. ``C`` (:class:`str` of length 1) [int] Convert a Python character, represented as a :class:`str` object of - length 1, to a C :c:type:`int`. + length 1, to a C :c:expr:`int`. ``f`` (:class:`float`) [float] - Convert a Python floating point number to a C :c:type:`float`. + Convert a Python floating point number to a C :c:expr:`float`. ``d`` (:class:`float`) [double] - Convert a Python floating point number to a C :c:type:`double`. + Convert a Python floating point number to a C :c:expr:`double`. ``D`` (:class:`complex`) [Py_complex] Convert a Python complex number to a C :c:type:`Py_complex` structure. @@ -292,13 +292,13 @@ Other objects ``O&`` (object) [*converter*, *anything*] Convert a Python object to a C variable through a *converter* function. This takes two arguments: the first is a function, the second is the address of a C - variable (of arbitrary type), converted to :c:type:`void *`. The *converter* + variable (of arbitrary type), converted to :c:expr:`void *`. The *converter* function in turn is called as follows:: status = converter(object, address); where *object* is the Python object to be converted and *address* is the - :c:type:`void*` argument that was passed to the ``PyArg_Parse*`` function. + :c:expr:`void*` argument that was passed to the ``PyArg_Parse*`` function. The returned *status* should be ``1`` for a successful conversion and ``0`` if the conversion has failed. When the conversion fails, the *converter* function should raise an exception and leave the content of *address* unmodified. @@ -531,7 +531,7 @@ Building values Same as ``s#``. ``u`` (:class:`str`) [const wchar_t \*] - Convert a null-terminated :c:type:`wchar_t` buffer of Unicode (UTF-16 or UCS-4) + Convert a null-terminated :c:expr:`wchar_t` buffer of Unicode (UTF-16 or UCS-4) data to a Python Unicode object. If the Unicode buffer pointer is ``NULL``, ``None`` is returned. @@ -547,51 +547,51 @@ Building values Same as ``s#``. ``i`` (:class:`int`) [int] - Convert a plain C :c:type:`int` to a Python integer object. + Convert a plain C :c:expr:`int` to a Python integer object. ``b`` (:class:`int`) [char] - Convert a plain C :c:type:`char` to a Python integer object. + Convert a plain C :c:expr:`char` to a Python integer object. ``h`` (:class:`int`) [short int] - Convert a plain C :c:type:`short int` to a Python integer object. + Convert a plain C :c:expr:`short int` to a Python integer object. ``l`` (:class:`int`) [long int] - Convert a C :c:type:`long int` to a Python integer object. + Convert a C :c:expr:`long int` to a Python integer object. ``B`` (:class:`int`) [unsigned char] - Convert a C :c:type:`unsigned char` to a Python integer object. + Convert a C :c:expr:`unsigned char` to a Python integer object. ``H`` (:class:`int`) [unsigned short int] - Convert a C :c:type:`unsigned short int` to a Python integer object. + Convert a C :c:expr:`unsigned short int` to a Python integer object. ``I`` (:class:`int`) [unsigned int] - Convert a C :c:type:`unsigned int` to a Python integer object. + Convert a C :c:expr:`unsigned int` to a Python integer object. ``k`` (:class:`int`) [unsigned long] - Convert a C :c:type:`unsigned long` to a Python integer object. + Convert a C :c:expr:`unsigned long` to a Python integer object. ``L`` (:class:`int`) [long long] - Convert a C :c:type:`long long` to a Python integer object. + Convert a C :c:expr:`long long` to a Python integer object. ``K`` (:class:`int`) [unsigned long long] - Convert a C :c:type:`unsigned long long` to a Python integer object. + Convert a C :c:expr:`unsigned long long` to a Python integer object. ``n`` (:class:`int`) [:c:type:`Py_ssize_t`] Convert a C :c:type:`Py_ssize_t` to a Python integer. ``c`` (:class:`bytes` of length 1) [char] - Convert a C :c:type:`int` representing a byte to a Python :class:`bytes` object of + Convert a C :c:expr:`int` representing a byte to a Python :class:`bytes` object of length 1. ``C`` (:class:`str` of length 1) [int] - Convert a C :c:type:`int` representing a character to Python :class:`str` + Convert a C :c:expr:`int` representing a character to Python :class:`str` object of length 1. ``d`` (:class:`float`) [double] - Convert a C :c:type:`double` to a Python floating point number. + Convert a C :c:expr:`double` to a Python floating point number. ``f`` (:class:`float`) [float] - Convert a C :c:type:`float` to a Python floating point number. + Convert a C :c:expr:`float` to a Python floating point number. ``D`` (:class:`complex`) [Py_complex \*] Convert a C :c:type:`Py_complex` structure to a Python complex number. @@ -614,7 +614,7 @@ Building values ``O&`` (object) [*converter*, *anything*] Convert *anything* to a Python object through a *converter* function. The - function is called with *anything* (which should be compatible with :c:type:`void*`) + function is called with *anything* (which should be compatible with :c:expr:`void*`) as its argument and should return a "new" Python object, or ``NULL`` if an error occurred. diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst index 2c4cfc48f9a27a..1c8f432505ef68 100644 --- a/Doc/c-api/capsule.rst +++ b/Doc/c-api/capsule.rst @@ -15,7 +15,7 @@ Refer to :ref:`using-capsules` for more information on using these objects. .. c:type:: PyCapsule This subtype of :c:type:`PyObject` represents an opaque value, useful for C - extension modules who need to pass an opaque value (as a :c:type:`void*` + extension modules who need to pass an opaque value (as a :c:expr:`void*` pointer) through Python code to other C code. It is often used to make a C function pointer defined in one module available to other modules, so the regular import mechanism can be used to access C APIs defined in dynamically diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index c25894681bca35..9228ce85200023 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -115,12 +115,12 @@ Complex Numbers as Python Objects .. c:function:: double PyComplex_RealAsDouble(PyObject *op) - Return the real part of *op* as a C :c:type:`double`. + Return the real part of *op* as a C :c:expr:`double`. .. c:function:: double PyComplex_ImagAsDouble(PyObject *op) - Return the imaginary part of *op* as a C :c:type:`double`. + Return the imaginary part of *op* as a C :c:expr:`double`. .. c:function:: Py_complex PyComplex_AsCComplex(PyObject *op) diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index 7b4cc1cacdd4ab..9b9c4ffa4d0343 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -49,7 +49,7 @@ The following functions provide locale-independent string to number conversions. .. c:function:: double PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception) - Convert a string ``s`` to a :c:type:`double`, raising a Python + Convert a string ``s`` to a :c:expr:`double`, raising a Python exception on failure. The set of accepted strings corresponds to the set of strings accepted by Python's :func:`float` constructor, except that ``s`` must not have leading or trailing whitespace. @@ -83,7 +83,7 @@ The following functions provide locale-independent string to number conversions. .. c:function:: char* PyOS_double_to_string(double val, char format_code, int precision, int flags, int *ptype) - Convert a :c:type:`double` *val* to a string using supplied + Convert a :c:expr:`double` *val* to a string using supplied *format_code*, *precision*, and *flags*. *format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``, diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 67c2026baa1496..819168d48707c1 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -73,7 +73,7 @@ Dictionary Objects .. index:: single: PyUnicode_FromString() Insert *val* into the dictionary *p* using *key* as a key. *key* should - be a :c:type:`const char*`. The key object is created using + be a :c:expr:`const char*`. The key object is created using ``PyUnicode_FromString(key)``. Return ``0`` on success or ``-1`` on failure. This function *does not* steal a reference to *val*. @@ -118,7 +118,7 @@ Dictionary Objects .. c:function:: PyObject* PyDict_GetItemString(PyObject *p, const char *key) This is the same as :c:func:`PyDict_GetItem`, but *key* is specified as a - :c:type:`const char*`, rather than a :c:expr:`PyObject*`. + :c:expr:`const char*`, rather than a :c:expr:`PyObject*`. Note that exceptions which occur while calling :meth:`__hash__` and :meth:`__eq__` methods and creating a temporary string object diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index 745d892be7ea89..58ed58e5466859 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -38,7 +38,7 @@ the :mod:`io` APIs instead. .. c:function:: int PyObject_AsFileDescriptor(PyObject *p) - Return the file descriptor associated with *p* as an :c:type:`int`. If the + Return the file descriptor associated with *p* as an :c:expr:`int`. If the object is an integer, its value is returned. If not, the object's :meth:`~io.IOBase.fileno` method is called if it exists; the method must return an integer, which is returned as the file descriptor diff --git a/Doc/c-api/float.rst b/Doc/c-api/float.rst index b306caf74b7c81..023b12c20b7c83 100644 --- a/Doc/c-api/float.rst +++ b/Doc/c-api/float.rst @@ -44,7 +44,7 @@ Floating Point Objects .. c:function:: double PyFloat_AsDouble(PyObject *pyfloat) - Return a C :c:type:`double` representation of the contents of *pyfloat*. If + Return a C :c:expr:`double` representation of the contents of *pyfloat*. If *pyfloat* is not a Python floating point object but has a :meth:`__float__` method, this method will first be called to convert *pyfloat* into a float. If ``__float__()`` is not defined then it falls back to :meth:`__index__`. @@ -57,7 +57,7 @@ Floating Point Objects .. c:function:: double PyFloat_AS_DOUBLE(PyObject *pyfloat) - Return a C :c:type:`double` representation of the contents of *pyfloat*, but + Return a C :c:expr:`double` representation of the contents of *pyfloat*, but without error checking. @@ -70,12 +70,12 @@ Floating Point Objects .. c:function:: double PyFloat_GetMax() - Return the maximum representable finite float *DBL_MAX* as C :c:type:`double`. + Return the maximum representable finite float *DBL_MAX* as C :c:expr:`double`. .. c:function:: double PyFloat_GetMin() - Return the minimum normalized positive float *DBL_MIN* as C :c:type:`double`. + Return the minimum normalized positive float *DBL_MIN* as C :c:expr:`double`. Pack and Unpack functions @@ -83,8 +83,8 @@ Pack and Unpack functions The pack and unpack functions provide an efficient platform-independent way to store floating-point values as byte strings. The Pack routines produce a bytes -string from a C :c:type:`double`, and the Unpack routines produce a C -:c:type:`double` from such a bytes string. The suffix (2, 4 or 8) specifies the +string from a C :c:expr:`double`, and the Unpack routines produce a C +:c:expr:`double` from such a bytes string. The suffix (2, 4 or 8) specifies the number of bytes in the bytes string. On platforms that appear to use IEEE 754 formats these functions work by @@ -107,7 +107,7 @@ Pack functions -------------- The pack routines write 2, 4 or 8 bytes, starting at *p*. *le* is an -:c:type:`int` argument, non-zero if you want the bytes string in little-endian +:c:expr:`int` argument, non-zero if you want the bytes string in little-endian format (exponent last, at ``p+1``, ``p+3``, or ``p+6`` ``p+7``), zero if you want big-endian format (exponent first, at *p*). The :c:data:`PY_BIG_ENDIAN` constant can be used to use the native endian: it is equal to ``1`` on big @@ -138,7 +138,7 @@ Unpack functions ---------------- The unpack routines read 2, 4 or 8 bytes, starting at *p*. *le* is an -:c:type:`int` argument, non-zero if the bytes string is in little-endian format +:c:expr:`int` argument, non-zero if the bytes string is in little-endian format (exponent last, at ``p+1``, ``p+3`` or ``p+6`` and ``p+7``), zero if big-endian (exponent first, at *p*). The :c:data:`PY_BIG_ENDIAN` constant can be used to use the native endian: it is equal to ``1`` on big endian processor, or ``0`` diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index efa58381d270ff..513ef93a384202 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -485,7 +485,7 @@ Process-wide parameters interpreter will change the contents of this storage. Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:type:`wchar_*` string. + :c:expr:`wchar_*` string. .. deprecated:: 3.11 @@ -636,7 +636,7 @@ Process-wide parameters if required after calling :c:func:`Py_Initialize`. Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:type:`wchar_*` string. + :c:expr:`wchar_*` string. The path argument is copied internally, so the caller may free it after the call completes. @@ -751,7 +751,7 @@ Process-wide parameters directory (``"."``). Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:type:`wchar_*` string. + :c:expr:`wchar_*` string. See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` members of the :ref:`Python Initialization Configuration `. @@ -787,7 +787,7 @@ Process-wide parameters :option:`-I`. Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:type:`wchar_*` string. + :c:expr:`wchar_*` string. See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` members of the :ref:`Python Initialization Configuration `. @@ -813,7 +813,7 @@ Process-wide parameters this storage. Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:type:`wchar_*` string. + :c:expr:`wchar_*` string. .. deprecated:: 3.11 @@ -1400,8 +1400,8 @@ All of the following functions must be called after :c:func:`Py_Initialize`. exception (if any) for the thread is cleared. This raises no exceptions. .. versionchanged:: 3.7 - The type of the *id* parameter changed from :c:type:`long` to - :c:type:`unsigned long`. + The type of the *id* parameter changed from :c:expr:`long` to + :c:expr:`unsigned long`. .. c:function:: void PyEval_AcquireThread(PyThreadState *tstate) @@ -1863,7 +1863,7 @@ The Python interpreter provides low-level support for thread-local storage (TLS) which wraps the underlying native TLS implementation to support the Python-level thread local storage API (:class:`threading.local`). The CPython C level APIs are similar to those offered by pthreads and Windows: -use a thread key and functions to associate a :c:type:`void*` value per +use a thread key and functions to associate a :c:expr:`void*` value per thread. The GIL does *not* need to be held when calling these functions; they supply @@ -1874,8 +1874,8 @@ you need to include :file:`pythread.h` to use thread-local storage. .. note:: None of these API functions handle memory management on behalf of the - :c:type:`void*` values. You need to allocate and deallocate them yourself. - If the :c:type:`void*` values happen to be :c:expr:`PyObject*`, these + :c:expr:`void*` values. You need to allocate and deallocate them yourself. + If the :c:expr:`void*` values happen to be :c:expr:`PyObject*`, these functions don't do refcount operations on them either. .. _thread-specific-storage-api: @@ -1885,7 +1885,7 @@ Thread Specific Storage (TSS) API TSS API is introduced to supersede the use of the existing TLS API within the CPython interpreter. This API uses a new type :c:type:`Py_tss_t` instead of -:c:type:`int` to represent thread keys. +:c:expr:`int` to represent thread keys. .. versionadded:: 3.7 @@ -1971,14 +1971,14 @@ undefined if the given :c:type:`Py_tss_t` has not been initialized by .. c:function:: int PyThread_tss_set(Py_tss_t *key, void *value) - Return a zero value to indicate successfully associating a :c:type:`void*` + Return a zero value to indicate successfully associating a :c:expr:`void*` value with a TSS key in the current thread. Each thread has a distinct - mapping of the key to a :c:type:`void*` value. + mapping of the key to a :c:expr:`void*` value. .. c:function:: void* PyThread_tss_get(Py_tss_t *key) - Return the :c:type:`void*` value associated with a TSS key in the current + Return the :c:expr:`void*` value associated with a TSS key in the current thread. This returns ``NULL`` if no value is associated with the key in the current thread. diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index 991bc3b09fd886..85eb24a495b640 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -530,8 +530,8 @@ Types ----- There are few other data types that play a significant role in the Python/C -API; most are simple C types such as :c:type:`int`, :c:type:`long`, -:c:type:`double` and :c:type:`char*`. A few structure types are used to +API; most are simple C types such as :c:expr:`int`, :c:expr:`long`, +:c:expr:`double` and :c:expr:`char*`. A few structure types are used to describe static tables used to list the functions exported by a module or the data attributes of a new object type, and another is used to describe the value of a complex number. These will be discussed together with the functions that diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 56a7c069de908e..4f6f865db8be13 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -47,7 +47,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: PyObject* PyLong_FromUnsignedLong(unsigned long v) - Return a new :c:type:`PyLongObject` object from a C :c:type:`unsigned long`, or + Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long`, or ``NULL`` on failure. @@ -65,13 +65,13 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: PyObject* PyLong_FromLongLong(long long v) - Return a new :c:type:`PyLongObject` object from a C :c:type:`long long`, or ``NULL`` + Return a new :c:type:`PyLongObject` object from a C :c:expr:`long long`, or ``NULL`` on failure. .. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v) - Return a new :c:type:`PyLongObject` object from a C :c:type:`unsigned long long`, + Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long long`, or ``NULL`` on failure. @@ -116,12 +116,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. single: LONG_MAX single: OverflowError (built-in exception) - Return a C :c:type:`long` representation of *obj*. If *obj* is not an + Return a C :c:expr:`long` representation of *obj*. If *obj* is not an instance of :c:type:`PyLongObject`, first call its :meth:`__index__` method (if present) to convert it to a :c:type:`PyLongObject`. Raise :exc:`OverflowError` if the value of *obj* is out of range for a - :c:type:`long`. + :c:expr:`long`. Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. @@ -134,7 +134,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: long PyLong_AsLongAndOverflow(PyObject *obj, int *overflow) - Return a C :c:type:`long` representation of *obj*. If *obj* is not an + Return a C :c:expr:`long` representation of *obj*. If *obj* is not an instance of :c:type:`PyLongObject`, first call its :meth:`__index__` method (if present) to convert it to a :c:type:`PyLongObject`. @@ -157,12 +157,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. index:: single: OverflowError (built-in exception) - Return a C :c:type:`long long` representation of *obj*. If *obj* is not an + Return a C :c:expr:`long long` representation of *obj*. If *obj* is not an instance of :c:type:`PyLongObject`, first call its :meth:`__index__` method (if present) to convert it to a :c:type:`PyLongObject`. Raise :exc:`OverflowError` if the value of *obj* is out of range for a - :c:type:`long long`. + :c:expr:`long long`. Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. @@ -175,7 +175,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: long long PyLong_AsLongLongAndOverflow(PyObject *obj, int *overflow) - Return a C :c:type:`long long` representation of *obj*. If *obj* is not an + Return a C :c:expr:`long long` representation of *obj*. If *obj* is not an instance of :c:type:`PyLongObject`, first call its :meth:`__index__` method (if present) to convert it to a :c:type:`PyLongObject`. @@ -216,11 +216,11 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. single: ULONG_MAX single: OverflowError (built-in exception) - Return a C :c:type:`unsigned long` representation of *pylong*. *pylong* + Return a C :c:expr:`unsigned long` representation of *pylong*. *pylong* must be an instance of :c:type:`PyLongObject`. Raise :exc:`OverflowError` if the value of *pylong* is out of range for a - :c:type:`unsigned long`. + :c:expr:`unsigned long`. Returns ``(unsigned long)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. @@ -247,11 +247,11 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. index:: single: OverflowError (built-in exception) - Return a C :c:type:`unsigned long long` representation of *pylong*. *pylong* + Return a C :c:expr:`unsigned long long` representation of *pylong*. *pylong* must be an instance of :c:type:`PyLongObject`. Raise :exc:`OverflowError` if the value of *pylong* is out of range for an - :c:type:`unsigned long long`. + :c:expr:`unsigned long long`. Returns ``(unsigned long long)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. @@ -262,11 +262,11 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: unsigned long PyLong_AsUnsignedLongMask(PyObject *obj) - Return a C :c:type:`unsigned long` representation of *obj*. If *obj* is not + Return a C :c:expr:`unsigned long` representation of *obj*. If *obj* is not an instance of :c:type:`PyLongObject`, first call its :meth:`__index__` method (if present) to convert it to a :c:type:`PyLongObject`. - If the value of *obj* is out of range for an :c:type:`unsigned long`, + If the value of *obj* is out of range for an :c:expr:`unsigned long`, return the reduction of that value modulo ``ULONG_MAX + 1``. Returns ``(unsigned long)-1`` on error. Use :c:func:`PyErr_Occurred` to @@ -281,12 +281,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: unsigned long long PyLong_AsUnsignedLongLongMask(PyObject *obj) - Return a C :c:type:`unsigned long long` representation of *obj*. If *obj* + Return a C :c:expr:`unsigned long long` representation of *obj*. If *obj* is not an instance of :c:type:`PyLongObject`, first call its :meth:`__index__` method (if present) to convert it to a :c:type:`PyLongObject`. - If the value of *obj* is out of range for an :c:type:`unsigned long long`, + If the value of *obj* is out of range for an :c:expr:`unsigned long long`, return the reduction of that value modulo ``ULLONG_MAX + 1``. Returns ``(unsigned long long)-1`` on error. Use :c:func:`PyErr_Occurred` @@ -301,20 +301,20 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: double PyLong_AsDouble(PyObject *pylong) - Return a C :c:type:`double` representation of *pylong*. *pylong* must be + Return a C :c:expr:`double` representation of *pylong*. *pylong* must be an instance of :c:type:`PyLongObject`. Raise :exc:`OverflowError` if the value of *pylong* is out of range for a - :c:type:`double`. + :c:expr:`double`. Returns ``-1.0`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: void* PyLong_AsVoidPtr(PyObject *pylong) - Convert a Python integer *pylong* to a C :c:type:`void` pointer. + Convert a Python integer *pylong* to a C :c:expr:`void` pointer. If *pylong* cannot be converted, an :exc:`OverflowError` will be raised. This - is only assured to produce a usable :c:type:`void` pointer for values created + is only assured to produce a usable :c:expr:`void` pointer for values created with :c:func:`PyLong_FromVoidPtr`. Returns ``NULL`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. diff --git a/Doc/c-api/marshal.rst b/Doc/c-api/marshal.rst index 1ba18beb3ea00f..8e25968c6909fd 100644 --- a/Doc/c-api/marshal.rst +++ b/Doc/c-api/marshal.rst @@ -21,9 +21,9 @@ unmarshalling. Version 2 uses a binary format for floating point numbers. .. c:function:: void PyMarshal_WriteLongToFile(long value, FILE *file, int version) - Marshal a :c:type:`long` integer, *value*, to *file*. This will only write + Marshal a :c:expr:`long` integer, *value*, to *file*. This will only write the least-significant 32 bits of *value*; regardless of the size of the - native :c:type:`long` type. *version* indicates the file format. + native :c:expr:`long` type. *version* indicates the file format. .. c:function:: void PyMarshal_WriteObjectToFile(PyObject *value, FILE *file, int version) @@ -43,9 +43,9 @@ The following functions allow marshalled values to be read back in. .. c:function:: long PyMarshal_ReadLongFromFile(FILE *file) - Return a C :c:type:`long` from the data stream in a :c:expr:`FILE*` opened + Return a C :c:expr:`long` from the data stream in a :c:expr:`FILE*` opened for reading. Only a 32-bit value can be read in using this function, - regardless of the native size of :c:type:`long`. + regardless of the native size of :c:expr:`long`. On error, sets the appropriate exception (:exc:`EOFError`) and returns ``-1``. @@ -53,9 +53,9 @@ The following functions allow marshalled values to be read back in. .. c:function:: int PyMarshal_ReadShortFromFile(FILE *file) - Return a C :c:type:`short` from the data stream in a :c:expr:`FILE*` opened + Return a C :c:expr:`short` from the data stream in a :c:expr:`FILE*` opened for reading. Only a 16-bit value can be read in using this function, - regardless of the native size of :c:type:`short`. + regardless of the native size of :c:expr:`short`. On error, sets the appropriate exception (:exc:`EOFError`) and returns ``-1``. diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index 4abbf340c5f420..f726cd48663b1c 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -141,7 +141,7 @@ zero bytes. .. c:function:: void* PyMem_RawMalloc(size_t n) - Allocates *n* bytes and returns a pointer of type :c:type:`void*` to the + Allocates *n* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. Requesting zero bytes returns a distinct non-``NULL`` pointer if possible, as @@ -152,7 +152,7 @@ zero bytes. .. c:function:: void* PyMem_RawCalloc(size_t nelem, size_t elsize) Allocates *nelem* elements each whose size in bytes is *elsize* and returns - a pointer of type :c:type:`void*` to the allocated memory, or ``NULL`` if the + a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. Requesting zero elements or elements of size zero bytes returns a distinct @@ -212,7 +212,7 @@ The :ref:`default memory allocator ` uses the .. c:function:: void* PyMem_Malloc(size_t n) - Allocates *n* bytes and returns a pointer of type :c:type:`void*` to the + Allocates *n* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. Requesting zero bytes returns a distinct non-``NULL`` pointer if possible, as @@ -223,7 +223,7 @@ The :ref:`default memory allocator ` uses the .. c:function:: void* PyMem_Calloc(size_t nelem, size_t elsize) Allocates *nelem* elements each whose size in bytes is *elsize* and returns - a pointer of type :c:type:`void*` to the allocated memory, or ``NULL`` if the + a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. Requesting zero elements or elements of size zero bytes returns a distinct @@ -320,7 +320,7 @@ The :ref:`default object allocator ` uses the .. c:function:: void* PyObject_Malloc(size_t n) - Allocates *n* bytes and returns a pointer of type :c:type:`void*` to the + Allocates *n* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. Requesting zero bytes returns a distinct non-``NULL`` pointer if possible, as @@ -331,7 +331,7 @@ The :ref:`default object allocator ` uses the .. c:function:: void* PyObject_Calloc(size_t nelem, size_t elsize) Allocates *nelem* elements each whose size in bytes is *elsize* and returns - a pointer of type :c:type:`void*` to the allocated memory, or ``NULL`` if the + a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. Requesting zero elements or elements of size zero bytes returns a distinct diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 11d5e0e03ec098..6fc8a3aff95686 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -107,7 +107,7 @@ Operating System Utilities Return the current signal handler for signal *i*. This is a thin wrapper around either :c:func:`sigaction` or :c:func:`signal`. Do not call those functions - directly! :c:type:`PyOS_sighandler_t` is a typedef alias for :c:type:`void + directly! :c:type:`PyOS_sighandler_t` is a typedef alias for :c:expr:`void (\*)(int)`. @@ -116,7 +116,7 @@ Operating System Utilities Set the signal handler for signal *i* to be *h*; return the old signal handler. This is a thin wrapper around either :c:func:`sigaction` or :c:func:`signal`. Do not call those functions directly! :c:type:`PyOS_sighandler_t` is a typedef - alias for :c:type:`void (\*)(int)`. + alias for :c:expr:`void (\*)(int)`. .. c:function:: wchar_t* Py_DecodeLocale(const char* arg, size_t *size) @@ -379,7 +379,7 @@ accessible to C code. They all work with the current interpreter thread's silently abort the operation by raising an error subclassed from :class:`Exception` (other errors will not be silenced). - The hook function is of type :c:type:`int (*)(const char *event, PyObject + The hook function is of type :c:expr:`int (*)(const char *event, PyObject *args, void *userData)`, where *args* is guaranteed to be a :c:type:`PyTupleObject`. The hook function is always called with the GIL held by the Python interpreter that raised the event. diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index ec9c5d089c57fb..f062f14e9a7561 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -44,7 +44,7 @@ Python: .. c:type:: Py_UNICODE - This is a typedef of :c:type:`wchar_t`, which is a 16-bit type or 32-bit type + This is a typedef of :c:expr:`wchar_t`, which is a 16-bit type or 32-bit type depending on the platform. .. versionchanged:: 3.3 @@ -788,11 +788,11 @@ conversion function: wchar_t Support """"""""""""""" -:c:type:`wchar_t` support for platforms which support it: +:c:expr:`wchar_t` support for platforms which support it: .. c:function:: PyObject* PyUnicode_FromWideChar(const wchar_t *w, Py_ssize_t size) - Create a Unicode object from the :c:type:`wchar_t` buffer *w* of the given *size*. + Create a Unicode object from the :c:expr:`wchar_t` buffer *w* of the given *size*. Passing ``-1`` as the *size* indicates that the function must itself compute the length, using wcslen. Return ``NULL`` on failure. @@ -800,13 +800,13 @@ wchar_t Support .. c:function:: Py_ssize_t PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, Py_ssize_t size) - Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most - *size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing - null termination character). Return the number of :c:type:`wchar_t` characters - copied or ``-1`` in case of an error. Note that the resulting :c:type:`wchar_t*` + Copy the Unicode object contents into the :c:expr:`wchar_t` buffer *w*. At most + *size* :c:expr:`wchar_t` characters are copied (excluding a possibly trailing + null termination character). Return the number of :c:expr:`wchar_t` characters + copied or ``-1`` in case of an error. Note that the resulting :c:expr:`wchar_t*` string may or may not be null-terminated. It is the responsibility of the caller - to make sure that the :c:type:`wchar_t*` string is null-terminated in case this is - required by the application. Also, note that the :c:type:`wchar_t*` string + to make sure that the :c:expr:`wchar_t*` string is null-terminated in case this is + required by the application. Also, note that the :c:expr:`wchar_t*` string might contain null characters, which would cause the string to be truncated when used with most C functions. @@ -816,9 +816,9 @@ wchar_t Support Convert the Unicode object to a wide character string. The output string always ends with a null character. If *size* is not ``NULL``, write the number of wide characters (excluding the trailing null termination character) into - *\*size*. Note that the resulting :c:type:`wchar_t` string might contain + *\*size*. Note that the resulting :c:expr:`wchar_t` string might contain null characters, which would cause the string to be truncated when used with - most C functions. If *size* is ``NULL`` and the :c:type:`wchar_t*` string + most C functions. If *size* is ``NULL`` and the :c:expr:`wchar_t*` string contains null characters a :exc:`ValueError` is raised. Returns a buffer allocated by :c:func:`PyMem_New` (use @@ -829,7 +829,7 @@ wchar_t Support .. versionadded:: 3.2 .. versionchanged:: 3.7 - Raises a :exc:`ValueError` if *size* is ``NULL`` and the :c:type:`wchar_t*` + Raises a :exc:`ValueError` if *size* is ``NULL`` and the :c:expr:`wchar_t*` string contains null characters. diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 0ef899f4c997b1..d9bf4fd6c7ae0e 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -298,7 +298,7 @@ In this case, it will return an integer object. (Yes, even integers are objects on the heap in Python!) If you have a C function that returns no useful argument (a function returning -:c:type:`void`), the corresponding Python function must return ``None``. You +:c:expr:`void`), the corresponding Python function must return ``None``. You need this idiom to do so (which is implemented by the :c:macro:`Py_RETURN_NONE` macro):: @@ -1171,7 +1171,7 @@ other extension modules must be exported in a different way. Python provides a special mechanism to pass C-level information (pointers) from one extension module to another one: Capsules. A Capsule is a Python data type -which stores a pointer (:c:type:`void \*`). Capsules can only be created and +which stores a pointer (:c:expr:`void \*`). Capsules can only be created and accessed via their C API, but they can be passed around like any other Python object. In particular, they can be assigned to a name in an extension module's namespace. Other extension modules can then import this module, retrieve the @@ -1185,7 +1185,7 @@ different ways between the module providing the code and the client modules. Whichever method you choose, it's important to name your Capsules properly. The function :c:func:`PyCapsule_New` takes a name parameter -(:c:type:`const char \*`); you're permitted to pass in a ``NULL`` name, but +(:c:expr:`const char \*`); you're permitted to pass in a ``NULL`` name, but we strongly encourage you to specify a name. Properly named Capsules provide a degree of runtime type-safety; there is no feasible way to tell one unnamed Capsule from another. @@ -1203,7 +1203,7 @@ of certainty that the Capsule they load contains the correct C API. The following example demonstrates an approach that puts most of the burden on the writer of the exporting module, which is appropriate for commonly used library modules. It stores all C API pointers (just one in the example!) in an -array of :c:type:`void` pointers which becomes the value of a Capsule. The header +array of :c:expr:`void` pointers which becomes the value of a Capsule. The header file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API. diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index b797dc2817c83e..a076eae534b91e 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -207,7 +207,7 @@ a special case, for which the new value passed to the handler is ``NULL``. Python supports two pairs of attribute handlers; a type that supports attributes only needs to implement the functions for one pair. The difference is that one -pair takes the name of the attribute as a :c:type:`char\*`, while the other +pair takes the name of the attribute as a :c:expr:`char\*`, while the other accepts a :c:type:`PyObject\*`. Each type can use whichever pair makes more sense for the implementation's convenience. :: @@ -339,8 +339,8 @@ of ``NULL`` is required. Type-specific Attribute Management ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For simplicity, only the :c:type:`char\*` version will be demonstrated here; the -type of the name parameter is the only difference between the :c:type:`char\*` +For simplicity, only the :c:expr:`char\*` version will be demonstrated here; the +type of the name parameter is the only difference between the :c:expr:`char\*` and :c:type:`PyObject\*` flavors of the interface. This example effectively does the same thing as the generic example above, but does not use the generic support added in Python 2.2. It explains how the handler functions are diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index d4c52a27592a94..4b6b26c991fd38 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -196,9 +196,9 @@ calls). ``None``, integers, bytes objects and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls. ``None`` is passed as a C ``NULL`` pointer, bytes objects and strings are passed -as pointer to the memory block that contains their data (:c:type:`char *` or -:c:type:`wchar_t *`). Python integers are passed as the platforms default C -:c:type:`int` type, their value is masked to fit into the C type. +as pointer to the memory block that contains their data (:c:expr:`char *` or +:c:expr:`wchar_t *`). Python integers are passed as the platforms default C +:c:expr:`int` type, their value is masked to fit into the C type. Before we move on calling functions with other parameter types, we have to learn more about :mod:`ctypes` data types. @@ -214,51 +214,51 @@ Fundamental data types +----------------------+------------------------------------------+----------------------------+ | ctypes type | C type | Python type | +======================+==========================================+============================+ -| :class:`c_bool` | :c:type:`_Bool` | bool (1) | +| :class:`c_bool` | :c:expr:`_Bool` | bool (1) | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_char` | :c:type:`char` | 1-character bytes object | +| :class:`c_char` | :c:expr:`char` | 1-character bytes object | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_wchar` | :c:type:`wchar_t` | 1-character string | +| :class:`c_wchar` | :c:expr:`wchar_t` | 1-character string | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_byte` | :c:type:`char` | int | +| :class:`c_byte` | :c:expr:`char` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_ubyte` | :c:type:`unsigned char` | int | +| :class:`c_ubyte` | :c:expr:`unsigned char` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_short` | :c:type:`short` | int | +| :class:`c_short` | :c:expr:`short` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_ushort` | :c:type:`unsigned short` | int | +| :class:`c_ushort` | :c:expr:`unsigned short` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_int` | :c:type:`int` | int | +| :class:`c_int` | :c:expr:`int` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_uint` | :c:type:`unsigned int` | int | +| :class:`c_uint` | :c:expr:`unsigned int` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_long` | :c:type:`long` | int | +| :class:`c_long` | :c:expr:`long` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_ulong` | :c:type:`unsigned long` | int | +| :class:`c_ulong` | :c:expr:`unsigned long` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_longlong` | :c:type:`__int64` or :c:type:`long long` | int | +| :class:`c_longlong` | :c:expr:`__int64` or :c:expr:`long long` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_ulonglong` | :c:type:`unsigned __int64` or | int | -| | :c:type:`unsigned long long` | | +| :class:`c_ulonglong` | :c:expr:`unsigned __int64` or | int | +| | :c:expr:`unsigned long long` | | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_size_t` | :c:type:`size_t` | int | +| :class:`c_size_t` | :c:expr:`size_t` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_ssize_t` | :c:type:`ssize_t` or | int | -| | :c:type:`Py_ssize_t` | | +| :class:`c_ssize_t` | :c:expr:`ssize_t` or | int | +| | :c:expr:`Py_ssize_t` | | +----------------------+------------------------------------------+----------------------------+ | :class:`c_time_t` | :c:type:`time_t` | int | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_float` | :c:type:`float` | float | +| :class:`c_float` | :c:expr:`float` | float | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_double` | :c:type:`double` | float | +| :class:`c_double` | :c:expr:`double` | float | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_longdouble`| :c:type:`long double` | float | +| :class:`c_longdouble`| :c:expr:`long double` | float | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_char_p` | :c:type:`char *` (NUL terminated) | bytes object or ``None`` | +| :class:`c_char_p` | :c:expr:`char *` (NUL terminated) | bytes object or ``None`` | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_wchar_p` | :c:type:`wchar_t *` (NUL terminated) | string or ``None`` | +| :class:`c_wchar_p` | :c:expr:`wchar_t *` (NUL terminated) | string or ``None`` | +----------------------+------------------------------------------+----------------------------+ -| :class:`c_void_p` | :c:type:`void *` | int or ``None`` | +| :class:`c_void_p` | :c:expr:`void *` | int or ``None`` | +----------------------+------------------------------------------+----------------------------+ (1) @@ -333,7 +333,7 @@ property:: The :func:`create_string_buffer` function replaces the old :func:`c_buffer` function (which is still available as an alias). To create a mutable memory -block containing unicode characters of the C type :c:type:`wchar_t`, use the +block containing unicode characters of the C type :c:expr:`wchar_t`, use the :func:`create_unicode_buffer` function. @@ -444,7 +444,7 @@ integer, string, bytes, a :mod:`ctypes` instance, or an object with an Return types ^^^^^^^^^^^^ -By default functions are assumed to return the C :c:type:`int` type. Other +By default functions are assumed to return the C :c:expr:`int` type. Other return types can be specified by setting the :attr:`restype` attribute of the function object. @@ -1337,7 +1337,7 @@ way is to instantiate one of the following classes: Instances of this class represent loaded shared libraries. Functions in these libraries use the standard C calling convention, and are assumed to return - :c:type:`int`. + :c:expr:`int`. On Windows creating a :class:`CDLL` instance may fail even if the DLL name exists. When a dependent DLL of the loaded DLL is not found, a @@ -1372,7 +1372,7 @@ way is to instantiate one of the following classes: Windows only: Instances of this class represent loaded shared libraries, functions in these libraries use the ``stdcall`` calling convention, and are - assumed to return :c:type:`int` by default. + assumed to return :c:expr:`int` by default. The Python :term:`global interpreter lock` is released before calling any function exported by these libraries, and reacquired afterwards. @@ -1528,7 +1528,7 @@ object is available: An instance of :class:`PyDLL` that exposes Python C API functions as attributes. Note that all these functions are assumed to return C - :c:type:`int`, which is of course not always the truth, so you have to assign + :c:expr:`int`, which is of course not always the truth, so you have to assign the correct :attr:`restype` attribute to use these functions. .. audit-event:: ctypes.dlopen name ctypes.LibraryLoader @@ -1574,10 +1574,10 @@ They are instances of a private class: .. attribute:: restype Assign a ctypes type to specify the result type of the foreign function. - Use ``None`` for :c:type:`void`, a function not returning anything. + Use ``None`` for :c:expr:`void`, a function not returning anything. It is possible to assign a callable Python object that is not a ctypes - type, in this case the function is assumed to return a C :c:type:`int`, and + type, in this case the function is assumed to return a C :c:expr:`int`, and the callable will be called with this integer, allowing further processing or error checking. Using this is deprecated, for more flexible post processing or error checking use a ctypes data type as @@ -2190,21 +2190,21 @@ These are the fundamental ctypes data types: .. class:: c_byte - Represents the C :c:type:`signed char` datatype, and interprets the value as + Represents the C :c:expr:`signed char` datatype, and interprets the value as small integer. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_char - Represents the C :c:type:`char` datatype, and interprets the value as a single + Represents the C :c:expr:`char` datatype, and interprets the value as a single character. The constructor accepts an optional string initializer, the length of the string must be exactly one character. .. class:: c_char_p - Represents the C :c:type:`char *` datatype when it points to a zero-terminated + Represents the C :c:expr:`char *` datatype when it points to a zero-terminated string. For a general character pointer that may also point to binary data, ``POINTER(c_char)`` must be used. The constructor accepts an integer address, or a bytes object. @@ -2212,68 +2212,68 @@ These are the fundamental ctypes data types: .. class:: c_double - Represents the C :c:type:`double` datatype. The constructor accepts an + Represents the C :c:expr:`double` datatype. The constructor accepts an optional float initializer. .. class:: c_longdouble - Represents the C :c:type:`long double` datatype. The constructor accepts an + Represents the C :c:expr:`long double` datatype. The constructor accepts an optional float initializer. On platforms where ``sizeof(long double) == sizeof(double)`` it is an alias to :class:`c_double`. .. class:: c_float - Represents the C :c:type:`float` datatype. The constructor accepts an + Represents the C :c:expr:`float` datatype. The constructor accepts an optional float initializer. .. class:: c_int - Represents the C :c:type:`signed int` datatype. The constructor accepts an + Represents the C :c:expr:`signed int` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. On platforms where ``sizeof(int) == sizeof(long)`` it is an alias to :class:`c_long`. .. class:: c_int8 - Represents the C 8-bit :c:type:`signed int` datatype. Usually an alias for + Represents the C 8-bit :c:expr:`signed int` datatype. Usually an alias for :class:`c_byte`. .. class:: c_int16 - Represents the C 16-bit :c:type:`signed int` datatype. Usually an alias for + Represents the C 16-bit :c:expr:`signed int` datatype. Usually an alias for :class:`c_short`. .. class:: c_int32 - Represents the C 32-bit :c:type:`signed int` datatype. Usually an alias for + Represents the C 32-bit :c:expr:`signed int` datatype. Usually an alias for :class:`c_int`. .. class:: c_int64 - Represents the C 64-bit :c:type:`signed int` datatype. Usually an alias for + Represents the C 64-bit :c:expr:`signed int` datatype. Usually an alias for :class:`c_longlong`. .. class:: c_long - Represents the C :c:type:`signed long` datatype. The constructor accepts an + Represents the C :c:expr:`signed long` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_longlong - Represents the C :c:type:`signed long long` datatype. The constructor accepts + Represents the C :c:expr:`signed long long` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_short - Represents the C :c:type:`signed short` datatype. The constructor accepts an + Represents the C :c:expr:`signed short` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. @@ -2298,83 +2298,83 @@ These are the fundamental ctypes data types: .. class:: c_ubyte - Represents the C :c:type:`unsigned char` datatype, it interprets the value as + Represents the C :c:expr:`unsigned char` datatype, it interprets the value as small integer. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_uint - Represents the C :c:type:`unsigned int` datatype. The constructor accepts an + Represents the C :c:expr:`unsigned int` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. On platforms where ``sizeof(int) == sizeof(long)`` it is an alias for :class:`c_ulong`. .. class:: c_uint8 - Represents the C 8-bit :c:type:`unsigned int` datatype. Usually an alias for + Represents the C 8-bit :c:expr:`unsigned int` datatype. Usually an alias for :class:`c_ubyte`. .. class:: c_uint16 - Represents the C 16-bit :c:type:`unsigned int` datatype. Usually an alias for + Represents the C 16-bit :c:expr:`unsigned int` datatype. Usually an alias for :class:`c_ushort`. .. class:: c_uint32 - Represents the C 32-bit :c:type:`unsigned int` datatype. Usually an alias for + Represents the C 32-bit :c:expr:`unsigned int` datatype. Usually an alias for :class:`c_uint`. .. class:: c_uint64 - Represents the C 64-bit :c:type:`unsigned int` datatype. Usually an alias for + Represents the C 64-bit :c:expr:`unsigned int` datatype. Usually an alias for :class:`c_ulonglong`. .. class:: c_ulong - Represents the C :c:type:`unsigned long` datatype. The constructor accepts an + Represents the C :c:expr:`unsigned long` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_ulonglong - Represents the C :c:type:`unsigned long long` datatype. The constructor + Represents the C :c:expr:`unsigned long long` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_ushort - Represents the C :c:type:`unsigned short` datatype. The constructor accepts + Represents the C :c:expr:`unsigned short` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. .. class:: c_void_p - Represents the C :c:type:`void *` type. The value is represented as integer. + Represents the C :c:expr:`void *` type. The value is represented as integer. The constructor accepts an optional integer initializer. .. class:: c_wchar - Represents the C :c:type:`wchar_t` datatype, and interprets the value as a + Represents the C :c:expr:`wchar_t` datatype, and interprets the value as a single character unicode string. The constructor accepts an optional string initializer, the length of the string must be exactly one character. .. class:: c_wchar_p - Represents the C :c:type:`wchar_t *` datatype, which must be a pointer to a + Represents the C :c:expr:`wchar_t *` datatype, which must be a pointer to a zero-terminated wide character string. The constructor accepts an integer address, or a string. .. class:: c_bool - Represent the C :c:type:`bool` datatype (more accurately, :c:type:`_Bool` from + Represent the C :c:expr:`bool` datatype (more accurately, :c:expr:`_Bool` from C99). Its value can be ``True`` or ``False``, and the constructor accepts any object that has a truth value. diff --git a/Doc/library/posix.rst b/Doc/library/posix.rst index 90be191aa2f8d7..ec04b0dcfc162f 100644 --- a/Doc/library/posix.rst +++ b/Doc/library/posix.rst @@ -39,12 +39,12 @@ Large File Support Several operating systems (including AIX and Solaris) provide support for files that are larger than 2 GiB from a C programming model where -:c:type:`int` and :c:type:`long` are 32-bit values. This is typically accomplished +:c:expr:`int` and :c:expr:`long` are 32-bit values. This is typically accomplished by defining the relevant size and offset types as 64-bit values. Such files are sometimes referred to as :dfn:`large files`. Large file support is enabled in Python when the size of an :c:type:`off_t` is -larger than a :c:type:`long` and the :c:type:`long long` is at least as large +larger than a :c:expr:`long` and the :c:expr:`long long` is at least as large as an :c:type:`off_t`. It may be necessary to configure and compile Python with certain compiler flags to enable this mode. For example, with Solaris 2.6 and 2.7 you need to do diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 1a9e5fee77b736..ee0c68e3a70779 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1612,7 +1612,7 @@ to sockets. ancillary data, items of the form ``(socket.SOL_SOCKET, socket.SCM_RIGHTS, fds)``, where *fds* is a :class:`bytes` object representing the new file descriptors as a binary array of the - native C :c:type:`int` type. If :meth:`recvmsg` raises an + native C :c:expr:`int` type. If :meth:`recvmsg` raises an exception after the system call returns, it will first attempt to close any file descriptors received via this mechanism. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ad4b90bf21d801..2952c50787ad5d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -215,7 +215,7 @@ Numeric Types --- :class:`int`, :class:`float`, :class:`complex` There are three distinct numeric types: :dfn:`integers`, :dfn:`floating point numbers`, and :dfn:`complex numbers`. In addition, Booleans are a subtype of integers. Integers have unlimited precision. Floating point -numbers are usually implemented using :c:type:`double` in C; information +numbers are usually implemented using :c:expr:`double` in C; information about the precision and internal representation of floating point numbers for the machine on which your program is running is available in :data:`sys.float_info`. Complex numbers have a real and imaginary diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index c1888d4a94fe0c..d12a5732fa4a0d 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -196,46 +196,46 @@ platform-dependent. +========+==========================+====================+================+============+ | ``x`` | pad byte | no value | | | +--------+--------------------------+--------------------+----------------+------------+ -| ``c`` | :c:type:`char` | bytes of length 1 | 1 | | +| ``c`` | :c:expr:`char` | bytes of length 1 | 1 | | +--------+--------------------------+--------------------+----------------+------------+ -| ``b`` | :c:type:`signed char` | integer | 1 | \(1), \(2) | +| ``b`` | :c:expr:`signed char` | integer | 1 | \(1), \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``B`` | :c:type:`unsigned char` | integer | 1 | \(2) | +| ``B`` | :c:expr:`unsigned char` | integer | 1 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``?`` | :c:type:`_Bool` | bool | 1 | \(1) | +| ``?`` | :c:expr:`_Bool` | bool | 1 | \(1) | +--------+--------------------------+--------------------+----------------+------------+ -| ``h`` | :c:type:`short` | integer | 2 | \(2) | +| ``h`` | :c:expr:`short` | integer | 2 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``H`` | :c:type:`unsigned short` | integer | 2 | \(2) | +| ``H`` | :c:expr:`unsigned short` | integer | 2 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``i`` | :c:type:`int` | integer | 4 | \(2) | +| ``i`` | :c:expr:`int` | integer | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``I`` | :c:type:`unsigned int` | integer | 4 | \(2) | +| ``I`` | :c:expr:`unsigned int` | integer | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``l`` | :c:type:`long` | integer | 4 | \(2) | +| ``l`` | :c:expr:`long` | integer | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``L`` | :c:type:`unsigned long` | integer | 4 | \(2) | +| ``L`` | :c:expr:`unsigned long` | integer | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``q`` | :c:type:`long long` | integer | 8 | \(2) | +| ``q`` | :c:expr:`long long` | integer | 8 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``Q`` | :c:type:`unsigned long | integer | 8 | \(2) | +| ``Q`` | :c:expr:`unsigned long | integer | 8 | \(2) | | | long` | | | | +--------+--------------------------+--------------------+----------------+------------+ -| ``n`` | :c:type:`ssize_t` | integer | | \(3) | +| ``n`` | :c:expr:`ssize_t` | integer | | \(3) | +--------+--------------------------+--------------------+----------------+------------+ -| ``N`` | :c:type:`size_t` | integer | | \(3) | +| ``N`` | :c:expr:`size_t` | integer | | \(3) | +--------+--------------------------+--------------------+----------------+------------+ | ``e`` | \(6) | float | 2 | \(4) | +--------+--------------------------+--------------------+----------------+------------+ -| ``f`` | :c:type:`float` | float | 4 | \(4) | +| ``f`` | :c:expr:`float` | float | 4 | \(4) | +--------+--------------------------+--------------------+----------------+------------+ -| ``d`` | :c:type:`double` | float | 8 | \(4) | +| ``d`` | :c:expr:`double` | float | 8 | \(4) | +--------+--------------------------+--------------------+----------------+------------+ -| ``s`` | :c:type:`char[]` | bytes | | | +| ``s`` | :c:expr:`char[]` | bytes | | | +--------+--------------------------+--------------------+----------------+------------+ -| ``p`` | :c:type:`char[]` | bytes | | | +| ``p`` | :c:expr:`char[]` | bytes | | | +--------+--------------------------+--------------------+----------------+------------+ -| ``P`` | :c:type:`void \*` | integer | | \(5) | +| ``P`` | :c:expr:`void \*` | integer | | \(5) | +--------+--------------------------+--------------------+----------------+------------+ .. versionchanged:: 3.3 @@ -250,8 +250,8 @@ Notes: (1) .. index:: single: ? (question mark); in struct format strings - The ``'?'`` conversion code corresponds to the :c:type:`_Bool` type defined by - C99. If this type is not available, it is simulated using a :c:type:`char`. In + The ``'?'`` conversion code corresponds to the :c:expr:`_Bool` type defined by + C99. If this type is not available, it is simulated using a :c:expr:`char`. In standard mode, it is always represented by one byte. (2) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index c93269ab04b64f..9dacd66ee564fd 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -316,7 +316,7 @@ Sequences A string is a sequence of values that represent Unicode code points. All the code points in the range ``U+0000 - U+10FFFF`` can be - represented in a string. Python doesn't have a :c:type:`char` type; + represented in a string. Python doesn't have a :c:expr:`char` type; instead, every code point in the string is represented as a string object with length ``1``. The built-in function :func:`ord` converts a code point from its string form to an integer in the diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index 9355c1badaa215..bfb2aacbc07747 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -983,7 +983,7 @@ New and Improved Modules Jun-ichiro "itojun" Hagino.) * Two new format characters were added to the :mod:`struct` module for 64-bit - integers on platforms that support the C :c:type:`long long` type. ``q`` is for + integers on platforms that support the C :c:expr:`long long` type. ``q`` is for a signed 64-bit integer, and ``Q`` is for an unsigned one. The value is returned in Python's long integer type. (Contributed by Tim Peters.) diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index 27a0756cbb849d..c6e2003e92f1b3 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -1905,8 +1905,8 @@ Changes to Python's build process and to the C API include: "")`` instead, but this will be slower than using :const:`METH_NOARGS`. * :c:func:`PyArg_ParseTuple` accepts new format characters for various sizes of - unsigned integers: ``B`` for :c:type:`unsigned char`, ``H`` for :c:type:`unsigned - short int`, ``I`` for :c:type:`unsigned int`, and ``K`` for :c:type:`unsigned + unsigned integers: ``B`` for :c:expr:`unsigned char`, ``H`` for :c:expr:`unsigned + short int`, ``I`` for :c:expr:`unsigned int`, and ``K`` for :c:expr:`unsigned long long`. * A new function, ``PyObject_DelItemString(mapping, char *key)`` was added diff --git a/Doc/whatsnew/2.4.rst b/Doc/whatsnew/2.4.rst index 61f9eb43243ceb..63e819876ce310 100644 --- a/Doc/whatsnew/2.4.rst +++ b/Doc/whatsnew/2.4.rst @@ -472,7 +472,7 @@ PEP 327: Decimal Data Type ========================== Python has always supported floating-point (FP) numbers, based on the underlying -C :c:type:`double` type, as a data type. However, while most programming +C :c:expr:`double` type, as a data type. However, while most programming languages provide a floating-point type, many people (even programmers) are unaware that floating-point numbers don't represent certain decimal fractions accurately. The new :class:`Decimal` type can represent these fractions @@ -501,7 +501,7 @@ mantissa is multiplied by 4 (2 to the power of the exponent 2); 1.25 \* 4 equals 5. Modern systems usually provide floating-point support that conforms to a -standard called IEEE 754. C's :c:type:`double` type is usually implemented as a +standard called IEEE 754. C's :c:expr:`double` type is usually implemented as a 64-bit IEEE 754 number, which uses 52 bits of space for the mantissa. This means that numbers can only be specified to 52 bits of precision. If you're trying to represent numbers whose expansion repeats endlessly, the expansion is @@ -750,10 +750,10 @@ The solution described in the PEP is to add three new functions to the Python API that perform ASCII-only conversions, ignoring the locale setting: * ``PyOS_ascii_strtod(str, ptr)`` and ``PyOS_ascii_atof(str, ptr)`` - both convert a string to a C :c:type:`double`. + both convert a string to a C :c:expr:`double`. * ``PyOS_ascii_formatd(buffer, buf_len, format, d)`` converts a - :c:type:`double` to an ASCII string. + :c:expr:`double` to an ASCII string. The code for these functions came from the GLib library (https://developer.gnome.org/glib/stable/), whose developers kindly diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index dfa8f7e93f8177..0aca2fe697ccdb 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -872,18 +872,18 @@ PEP 353: Using ssize_t as the index type ======================================== A wide-ranging change to Python's C API, using a new :c:type:`Py_ssize_t` type -definition instead of :c:type:`int`, will permit the interpreter to handle more +definition instead of :c:expr:`int`, will permit the interpreter to handle more data on 64-bit platforms. This change doesn't affect Python's capacity on 32-bit platforms. -Various pieces of the Python interpreter used C's :c:type:`int` type to store +Various pieces of the Python interpreter used C's :c:expr:`int` type to store sizes or counts; for example, the number of items in a list or tuple were stored -in an :c:type:`int`. The C compilers for most 64-bit platforms still define -:c:type:`int` as a 32-bit type, so that meant that lists could only hold up to +in an :c:expr:`int`. The C compilers for most 64-bit platforms still define +:c:expr:`int` as a 32-bit type, so that meant that lists could only hold up to ``2**31 - 1`` = 2147483647 items. (There are actually a few different programming models that 64-bit C compilers can use -- see https://unix.org/version2/whatsnew/lp64_wp.html for a discussion -- but the -most commonly available model leaves :c:type:`int` as 32 bits.) +most commonly available model leaves :c:expr:`int` as 32 bits.) A limit of 2147483647 items doesn't really matter on a 32-bit platform because you'll run out of memory before hitting the length limit. Each list item @@ -895,7 +895,7 @@ It's possible to address that much memory on a 64-bit platform, however. The pointers for a list that size would only require 16 GiB of space, so it's not unreasonable that Python programmers might construct lists that large. Therefore, the Python interpreter had to be changed to use some type other than -:c:type:`int`, and this will be a 64-bit type on 64-bit platforms. The change +:c:expr:`int`, and this will be a 64-bit type on 64-bit platforms. The change will cause incompatibilities on 64-bit machines, so it was deemed worth making the transition now, while the number of 64-bit users is still relatively small. (In 5 or 10 years, we may *all* be on 64-bit machines, and the transition would @@ -909,7 +909,7 @@ may therefore need to have some variables changed to :c:type:`Py_ssize_t`. The :c:func:`PyArg_ParseTuple` and :c:func:`Py_BuildValue` functions have a new conversion code, ``n``, for :c:type:`Py_ssize_t`. :c:func:`PyArg_ParseTuple`'s -``s#`` and ``t#`` still output :c:type:`int` by default, but you can define the +``s#`` and ``t#`` still output :c:expr:`int` by default, but you can define the macro :c:macro:`PY_SSIZE_T_CLEAN` before including :file:`Python.h` to make them return :c:type:`Py_ssize_t`. @@ -1695,7 +1695,7 @@ attributes of the :class:`CDLL` object. :: result = libc.printf("Line of output\n") Type constructors for the various C types are provided: :func:`c_int`, -:func:`c_float`, :func:`c_double`, :func:`c_char_p` (equivalent to :c:type:`char +:func:`c_float`, :func:`c_double`, :func:`c_char_p` (equivalent to :c:expr:`char \*`), and so forth. Unlike Python's types, the C versions are all mutable; you can assign to their :attr:`value` attribute to change the wrapped value. Python integers and strings will be automatically converted to the corresponding C @@ -2093,7 +2093,7 @@ Changes to Python's build process and to the C API include: * The largest change to the C API came from :pep:`353`, which modifies the interpreter to use a :c:type:`Py_ssize_t` type definition instead of - :c:type:`int`. See the earlier section :ref:`pep-353` for a discussion of this + :c:expr:`int`. See the earlier section :ref:`pep-353` for a discussion of this change. * The design of the bytecode compiler has changed a great deal, no longer @@ -2264,7 +2264,7 @@ code: Setting :attr:`rpc_paths` to ``None`` or an empty tuple disables this path checking. -* C API: Many functions now use :c:type:`Py_ssize_t` instead of :c:type:`int` to +* C API: Many functions now use :c:type:`Py_ssize_t` instead of :c:expr:`int` to allow processing more data on 64-bit machines. Extension code may need to make the same change to avoid warnings and to support 64-bit machines. See the earlier section :ref:`pep-353` for a discussion of this change. diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 5a3c103f29a789..731ce6aac6919d 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -2389,7 +2389,7 @@ changes, or look through the Subversion logs for all the details. has been updated from version 2.3.2 in Python 2.5 to version 2.4.1. -* The :mod:`struct` module now supports the C99 :c:type:`_Bool` type, +* The :mod:`struct` module now supports the C99 :c:expr:`_Bool` type, using the format character ``'?'``. (Contributed by David Remahl.) diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 01f140dac8ae9c..e8f701d254cd28 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2144,7 +2144,7 @@ Changes to Python's build process and to the C API include: * New functions: :c:func:`PyLong_AsLongAndOverflow` and :c:func:`PyLong_AsLongLongAndOverflow` approximates a Python long - integer as a C :c:type:`long` or :c:type:`long long`. + integer as a C :c:expr:`long` or :c:expr:`long long`. If the number is too large to fit into the output type, an *overflow* flag is set and returned to the caller. (Contributed by Case Van Horsen; :issue:`7528` and :issue:`7767`.) @@ -2202,7 +2202,7 @@ Changes to Python's build process and to the C API include: * New format codes: the :c:func:`PyFormat_FromString`, :c:func:`PyFormat_FromStringV`, and :c:func:`PyErr_Format` functions now accept ``%lld`` and ``%llu`` format codes for displaying - C's :c:type:`long long` types. + C's :c:expr:`long long` types. (Contributed by Mark Dickinson; :issue:`7228`.) * The complicated interaction between threads and process forking has diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 609370bad274b6..fef1a8ac4c0101 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -932,7 +932,7 @@ it can now be used as a class decorator (:issue:`10868`). array ----- -The :mod:`array` module supports the :c:type:`long long` type using ``q`` and +The :mod:`array` module supports the :c:expr:`long long` type using ``q`` and ``Q`` type codes. (Contributed by Oren Tirosh and Hirokazu Yamamoto in :issue:`1172711`.) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 908f26823c12e6..f06cf29c713f99 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -290,21 +290,21 @@ PEP 539: New C API for Thread-Local Storage While Python provides a C API for thread-local storage support; the existing :ref:`Thread Local Storage (TLS) API ` has used -:c:type:`int` to represent TLS keys across all platforms. This has not +:c:expr:`int` to represent TLS keys across all platforms. This has not generally been a problem for officially support platforms, but that is neither POSIX-compliant, nor portable in any practical sense. :pep:`539` changes this by providing a new :ref:`Thread Specific Storage (TSS) API ` to CPython which supersedes use of the existing TLS API within the CPython interpreter, while deprecating the existing -API. The TSS API uses a new type :c:type:`Py_tss_t` instead of :c:type:`int` +API. The TSS API uses a new type :c:type:`Py_tss_t` instead of :c:expr:`int` to represent TSS keys--an opaque type the definition of which may depend on the underlying TLS implementation. Therefore, this will allow to build CPython on platforms where the native TLS key is defined in a way that cannot be safely -cast to :c:type:`int`. +cast to :c:expr:`int`. Note that on platforms where the native TLS key is defined in a way that cannot -be safely cast to :c:type:`int`, all functions of the existing TLS API will be +be safely cast to :c:expr:`int`, all functions of the existing TLS API will be no-op and immediately return failure. This indicates clearly that the old API is not supported on platforms where it cannot be used reliably, and that no effort will be made to add such support. @@ -1708,12 +1708,12 @@ Contributed by Paul Ganssle in :issue:`10381`. The type of results of :c:func:`PyThread_start_new_thread` and :c:func:`PyThread_get_thread_ident`, and the *id* parameter of -:c:func:`PyThreadState_SetAsyncExc` changed from :c:type:`long` to -:c:type:`unsigned long`. +:c:func:`PyThreadState_SetAsyncExc` changed from :c:expr:`long` to +:c:expr:`unsigned long`. (Contributed by Serhiy Storchaka in :issue:`6532`.) :c:func:`PyUnicode_AsWideCharString` now raises a :exc:`ValueError` if the -second argument is ``NULL`` and the :c:type:`wchar_t*` string contains null +second argument is ``NULL`` and the :c:expr:`wchar_t*` string contains null characters. (Contributed by Serhiy Storchaka in :issue:`30708`.) Changes to the startup sequence and the management of dynamic memory diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 6deaede4953bdc..ff01a65772991f 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -773,7 +773,7 @@ Optimizations Stinner in :issue:`38061`.) * :c:func:`PyLong_FromDouble` is now up to 1.87x faster for values that - fit into :c:type:`long`. + fit into :c:expr:`long`. (Contributed by Sergey Fedoseev in :issue:`37986`.) * A number of Python builtins (:class:`range`, :class:`tuple`, :class:`set`, diff --git a/Misc/NEWS.d/3.5.0a1.rst b/Misc/NEWS.d/3.5.0a1.rst index 97bdef6c93213f..96e59206cb1291 100644 --- a/Misc/NEWS.d/3.5.0a1.rst +++ b/Misc/NEWS.d/3.5.0a1.rst @@ -3034,7 +3034,7 @@ by Phil Elson. .. nonce: LK_5S1 .. section: Library -os.read() now uses a :c:func:`Py_ssize_t` type instead of :c:type:`int` for +os.read() now uses a :c:func:`Py_ssize_t` type instead of :c:expr:`int` for the size to support reading more than 2 GB at once. On Windows, the size is truncated to INT_MAX. As any call to os.read(), the OS may read less bytes than the number of requested bytes. diff --git a/Misc/NEWS.d/3.9.0a1.rst b/Misc/NEWS.d/3.9.0a1.rst index eace8755a0d171..633620583838df 100644 --- a/Misc/NEWS.d/3.9.0a1.rst +++ b/Misc/NEWS.d/3.9.0a1.rst @@ -1510,7 +1510,7 @@ asynchronous magic methods on a MagicMock now return an AsyncMock. .. section: Library Update the *length* parameter of :func:`os.pread` to accept -:c:type:`Py_ssize_t` instead of :c:type:`int`. +:c:type:`Py_ssize_t` instead of :c:expr:`int`. .. diff --git a/Misc/NEWS.d/3.9.0b1.rst b/Misc/NEWS.d/3.9.0b1.rst index 529be0eba586ac..a7f52f81a5cd3a 100644 --- a/Misc/NEWS.d/3.9.0b1.rst +++ b/Misc/NEWS.d/3.9.0b1.rst @@ -191,7 +191,7 @@ internal subinterpreters module. .. section: Core and Builtins Improve performance of :c:func:`PyLong_FromDouble` for values that fit into -:c:type:`long`. +:c:expr:`long`. .. From 9a24191ef31f7619a103abb5f9c478d319f033ba Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 5 Oct 2022 20:16:45 +0200 Subject: [PATCH 051/151] I changed my surname early this year (#96671) * I recently changed my name * Update ACKS --- Doc/howto/argparse.rst | 2 +- Misc/ACKS | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index 3075b0142d16d6..f3ad117a3d3bc6 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -2,7 +2,7 @@ Argparse Tutorial ***************** -:author: Tshepang Lekhonkhobe +:author: Tshepang Mbambo .. _argparse-tutorial: diff --git a/Misc/ACKS b/Misc/ACKS index 6a14b546f691f2..ec5e326847b8ca 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1049,7 +1049,7 @@ Robert Lehmann Petri Lehtinen Luke Kenneth Casson Leighton John Leitch -Tshepang Lekhonkhobe +Tshepang Mbambo Marc-André Lemburg Mateusz Lenik John Lenton From 0aca8cacdbb9064ba94342e2493b0e86e1125c6f Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 5 Oct 2022 11:42:26 -0700 Subject: [PATCH 052/151] gh-97850: Remove all known instances of module_repr() (#97876) Remove all known instances of module_repr() --- Doc/whatsnew/3.12.rst | 5 +++++ Lib/importlib/_bootstrap.py | 22 ------------------- Lib/test/test_importlib/frozen/test_loader.py | 9 +------- Lib/test/test_importlib/test_abc.py | 3 --- Lib/test/test_module.py | 2 -- ...2-10-04-17-02-18.gh-issue-97850.E3QTRA.rst | 1 + 6 files changed, 7 insertions(+), 35 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-04-17-02-18.gh-issue-97850.E3QTRA.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 62ec2de2e78c99..2e9515d036e736 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -426,6 +426,11 @@ Removed Validation. (Contributed by Victor Stinner in :gh:`94199`.) +* Many previously deprecated cleanups in :mod:`importlib` have now been + completed: + + * References to, and support for ``module_repr()`` has been eradicated. + Porting to Python 3.12 ====================== diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 67989c500f21c0..5d3c9fe3fbd2fe 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -728,17 +728,6 @@ class BuiltinImporter: _ORIGIN = "built-in" - @staticmethod - def module_repr(module): - """Return repr for the module. - - The method is deprecated. The import machinery does the job itself. - - """ - _warnings.warn("BuiltinImporter.module_repr() is deprecated and " - "slated for removal in Python 3.12", DeprecationWarning) - return f'' - @classmethod def find_spec(cls, fullname, path=None, target=None): if path is not None: @@ -808,17 +797,6 @@ class FrozenImporter: _ORIGIN = "frozen" - @staticmethod - def module_repr(m): - """Return repr for the module. - - The method is deprecated. The import machinery does the job itself. - - """ - _warnings.warn("FrozenImporter.module_repr() is deprecated and " - "slated for removal in Python 3.12", DeprecationWarning) - return ''.format(m.__name__, FrozenImporter._ORIGIN) - @classmethod def _fix_up_module(cls, module): spec = module.__spec__ diff --git a/Lib/test/test_importlib/frozen/test_loader.py b/Lib/test/test_importlib/frozen/test_loader.py index 32f951cb1aca28..da1569e3d0681e 100644 --- a/Lib/test/test_importlib/frozen/test_loader.py +++ b/Lib/test/test_importlib/frozen/test_loader.py @@ -103,7 +103,7 @@ def test_lacking_parent(self): expected=value)) self.assertEqual(output, 'Hello world!\n') - def test_module_repr_indirect(self): + def test_module_repr_indirect_through_spec(self): name = '__hello__' module, output = self.exec_module(name) self.assertEqual(repr(module), @@ -190,13 +190,6 @@ def test_module_reuse(self): self.assertEqual(stdout.getvalue(), 'Hello world!\nHello world!\n') - def test_module_repr(self): - with fresh('__hello__', oldapi=True): - module = self.machinery.FrozenImporter.load_module('__hello__') - repr_str = self.machinery.FrozenImporter.module_repr(module) - self.assertEqual(repr_str, - "") - # No way to trigger an error in a frozen module. test_state_after_failure = None diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index c214209350a0c8..8641b6cc683052 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -687,9 +687,6 @@ def get_data(self, path): def get_filename(self, fullname): return self.path - def module_repr(self, module): - return '' - SPLIT_SOL = make_abc_subclasses(SourceOnlyLoader, 'SourceLoader') diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py index 6c83d76c8e3c68..70e4efea69359a 100644 --- a/Lib/test/test_module.py +++ b/Lib/test/test_module.py @@ -239,7 +239,6 @@ def test_module_repr_with_full_loader(self): repr(m), ")>") def test_module_repr_with_bare_loader_and_filename(self): - # Because the loader has no module_repr(), use the file name. m = ModuleType('foo') # Yes, a class not an instance. m.__loader__ = BareLoader @@ -247,7 +246,6 @@ def test_module_repr_with_bare_loader_and_filename(self): self.assertEqual(repr(m), "") def test_module_repr_with_full_loader_and_filename(self): - # Even though the module has an __file__, use __loader__.module_repr() m = ModuleType('foo') # Yes, a class not an instance. m.__loader__ = FullLoader diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-04-17-02-18.gh-issue-97850.E3QTRA.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-04-17-02-18.gh-issue-97850.E3QTRA.rst new file mode 100644 index 00000000000000..f880d9663842ff --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-04-17-02-18.gh-issue-97850.E3QTRA.rst @@ -0,0 +1 @@ +Long deprecated, ``module_repr()`` should now be completely eradicated. From 0ee0e4d9ede8403973115570ba603d1c9f3ee3dc Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 5 Oct 2022 22:08:07 +0200 Subject: [PATCH 053/151] docs(typing): add "see PEP 675" to LiteralString (#97926) --- Doc/library/typing.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 786f579a07d02c..f63d61eb1ea38d 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -631,6 +631,8 @@ These can be used as types in annotations and do not support ``[]``. that generate type checker errors could be vulnerable to an SQL injection attack. + See :pep:`675` for more details. + .. versionadded:: 3.11 .. data:: Never From 351eda54145b220329ed967a4133634276610725 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 5 Oct 2022 15:00:45 -0700 Subject: [PATCH 054/151] gh-65961: Raise `DeprecationWarning` when `__package__` differs from `__spec__.parent` (#97879) Also remove `importlib.util.set_package()` which was already slated for removal. Co-authored-by: Eric Snow --- Doc/library/importlib.rst | 9 --- Doc/reference/import.rst | 30 +++++++-- Doc/whatsnew/3.12.rst | 12 ++++ Lib/importlib/_bootstrap.py | 2 +- Lib/importlib/util.py | 20 ------ .../import_/test___package__.py | 4 +- Lib/test/test_importlib/test_util.py | 63 ------------------- ...2-10-05-00-37-27.gh-issue-65961.z0Ys0y.rst | 5 ++ Python/import.c | 2 +- 9 files changed, 45 insertions(+), 102 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-05-00-37-27.gh-issue-65961.z0Ys0y.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 0fd765f5985f7c..a7c067c06e8ec2 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1378,15 +1378,6 @@ an :term:`importer`. .. deprecated:: 3.4 The import machinery takes care of this automatically. -.. decorator:: set_package - - A :term:`decorator` for :meth:`importlib.abc.Loader.load_module` to set the - :attr:`__package__` attribute on the returned module. If :attr:`__package__` - is set and has a value other than ``None`` it will not be changed. - - .. deprecated:: 3.4 - The import machinery takes care of this automatically. - .. function:: spec_from_loader(name, loader, *, origin=None, is_package=None) A factory function for creating a :class:`~importlib.machinery.ModuleSpec` diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 507f2b3763cae4..58f4ef897bdb7d 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -358,7 +358,6 @@ of what happens during the loading portion of import:: sys.modules[spec.name] = module elif not hasattr(spec.loader, 'exec_module'): module = spec.loader.load_module(spec.name) - # Set __loader__ and __package__ if missing. else: sys.modules[spec.name] = module try: @@ -539,6 +538,10 @@ The import machinery fills in these attributes on each module object during loading, based on the module's spec, before the loader executes the module. +It is **strongly** recommended that you rely on :attr:`__spec__` and +its attributes instead of any of the other individual attributes +listed below. + .. attribute:: __name__ The ``__name__`` attribute must be set to the fully qualified name of @@ -552,9 +555,12 @@ the module. for introspection, but can be used for additional loader-specific functionality, for example getting data associated with a loader. + It is **strongly** recommended that you rely on :attr:`__spec__` + instead instead of this attribute. + .. attribute:: __package__ - The module's ``__package__`` attribute must be set. Its value must + The module's ``__package__`` attribute may be set. Its value must be a string, but it can be the same value as its ``__name__``. When the module is a package, its ``__package__`` value should be set to its ``__name__``. When the module is not a package, ``__package__`` @@ -562,14 +568,23 @@ the module. submodules, to the parent package's name. See :pep:`366` for further details. - This attribute is used instead of ``__name__`` to calculate explicit - relative imports for main modules, as defined in :pep:`366`. It is - expected to have the same value as ``__spec__.parent``. + It is **strongly** recommended that you rely on :attr:`__spec__` + instead instead of this attribute. .. versionchanged:: 3.6 The value of ``__package__`` is expected to be the same as ``__spec__.parent``. + .. versionchanged:: 3.10 + :exc:`ImportWarning` is raised if import falls back to + ``__package__`` instead of + :attr:`~importlib.machinery.ModuleSpec.parent`. + + .. versionchanged:: 3.12 + Raise :exc:`DeprecationWarning` instead of :exc:`ImportWarning` + when falling back to ``__package__``. + + .. attribute:: __spec__ The ``__spec__`` attribute must be set to the module spec that was @@ -578,7 +593,7 @@ the module. interpreter startup `. The one exception is ``__main__``, where ``__spec__`` is :ref:`set to None in some cases `. - When ``__package__`` is not defined, ``__spec__.parent`` is used as + When ``__spec__.parent`` is not set, ``__package__`` is used as a fallback. .. versionadded:: 3.4 @@ -623,6 +638,9 @@ the module. if a loader can load from a cached module but otherwise does not load from a file, that atypical scenario may be appropriate. + It is **strongly** recommended that you rely on :attr:`__spec__` + instead instead of ``__cached__``. + .. _package-path-rules: module.__path__ diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 2e9515d036e736..507ba35221467e 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -215,6 +215,11 @@ Deprecated may be removed in a future version of Python. Use the single-arg versions of these functions instead. (Contributed by Ofey Chan in :gh:`89874`.) +* :exc:`DeprecationWarning` is now raised when ``__package__`` on a + module differs from ``__spec__.parent`` (previously it was + :exc:`ImportWarning`). + (Contributed by Brett Cannon in :gh:`65961`.) + Pending Removal in Python 3.13 ------------------------------ @@ -275,6 +280,9 @@ Pending Removal in Python 3.14 * Creating :c:data:`immutable types ` with mutable bases using the C API. +* ``__package__`` will cease to be set or taken into consideration by + the import system (:gh:`97879`). + Pending Removal in Future Versions ---------------------------------- @@ -432,6 +440,10 @@ Removed * References to, and support for ``module_repr()`` has been eradicated. +* ``importlib.util.set_package`` has been removed. + (Contributed by Brett Cannon in :gh:`65961`.) + + Porting to Python 3.12 ====================== diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 5d3c9fe3fbd2fe..1c132106ce5a8f 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -1228,7 +1228,7 @@ def _calc___package__(globals): if spec is not None and package != spec.parent: _warnings.warn("__package__ != __spec__.parent " f"({package!r} != {spec.parent!r})", - ImportWarning, stacklevel=3) + DeprecationWarning, stacklevel=3) return package elif spec is not None: return spec.parent diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index 8623c89840c6a2..7f15b029b24050 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -141,26 +141,6 @@ def _module_to_load(name): module.__initializing__ = False -def set_package(fxn): - """Set __package__ on the returned module. - - This function is deprecated. - - """ - @functools.wraps(fxn) - def set_package_wrapper(*args, **kwargs): - warnings.warn('The import system now takes care of this automatically; ' - 'this decorator is slated for removal in Python 3.12', - DeprecationWarning, stacklevel=2) - module = fxn(*args, **kwargs) - if getattr(module, '__package__', None) is None: - module.__package__ = module.__name__ - if not hasattr(module, '__path__'): - module.__package__ = module.__package__.rpartition('.')[0] - return module - return set_package_wrapper - - def set_loader(fxn): """Set __loader__ on the returned module. diff --git a/Lib/test/test_importlib/import_/test___package__.py b/Lib/test/test_importlib/import_/test___package__.py index 1ab5018a431de2..ab1b35ee3c1a4e 100644 --- a/Lib/test/test_importlib/import_/test___package__.py +++ b/Lib/test/test_importlib/import_/test___package__.py @@ -74,8 +74,8 @@ def test_spec_fallback(self): self.assertEqual(module.__name__, 'pkg') def test_warn_when_package_and_spec_disagree(self): - # Raise an ImportWarning if __package__ != __spec__.parent. - with self.assertWarns(ImportWarning): + # Raise a DeprecationWarning if __package__ != __spec__.parent. + with self.assertWarns(DeprecationWarning): self.import_module({'__package__': 'pkg.fake', '__spec__': FakeSpec('pkg.fakefake')}) diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index a62d68fcd8b333..e70971e9d3bc84 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -252,69 +252,6 @@ def load_module(self, module): ) = util.test_both(ModuleForLoaderTests, util=importlib_util) -class SetPackageTests: - - """Tests for importlib.util.set_package.""" - - def verify(self, module, expect): - """Verify the module has the expected value for __package__ after - passing through set_package.""" - fxn = lambda: module - wrapped = self.util.set_package(fxn) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - wrapped() - self.assertTrue(hasattr(module, '__package__')) - self.assertEqual(expect, module.__package__) - - def test_top_level(self): - # __package__ should be set to the empty string if a top-level module. - # Implicitly tests when package is set to None. - module = types.ModuleType('module') - module.__package__ = None - self.verify(module, '') - - def test_package(self): - # Test setting __package__ for a package. - module = types.ModuleType('pkg') - module.__path__ = [''] - module.__package__ = None - self.verify(module, 'pkg') - - def test_submodule(self): - # Test __package__ for a module in a package. - module = types.ModuleType('pkg.mod') - module.__package__ = None - self.verify(module, 'pkg') - - def test_setting_if_missing(self): - # __package__ should be set if it is missing. - module = types.ModuleType('mod') - if hasattr(module, '__package__'): - delattr(module, '__package__') - self.verify(module, '') - - def test_leaving_alone(self): - # If __package__ is set and not None then leave it alone. - for value in (True, False): - module = types.ModuleType('mod') - module.__package__ = value - self.verify(module, value) - - def test_decorator_attrs(self): - def fxn(module): pass - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - wrapped = self.util.set_package(fxn) - self.assertEqual(wrapped.__name__, fxn.__name__) - self.assertEqual(wrapped.__qualname__, fxn.__qualname__) - - -(Frozen_SetPackageTests, - Source_SetPackageTests - ) = util.test_both(SetPackageTests, util=importlib_util) - - class SetLoaderTests: """Tests importlib.util.set_loader().""" diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-05-00-37-27.gh-issue-65961.z0Ys0y.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-00-37-27.gh-issue-65961.z0Ys0y.rst new file mode 100644 index 00000000000000..0c034263c1a89a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-00-37-27.gh-issue-65961.z0Ys0y.rst @@ -0,0 +1,5 @@ +When ``__package__`` is different than ``__spec__.parent``, raise a +``DeprecationWarning`` instead of ``ImportWarning``. + +Also remove ``importlib.util.set_package()`` which was scheduled for +removal. diff --git a/Python/import.c b/Python/import.c index 54c21fa4a56aa9..698ef37ce0a131 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1573,7 +1573,7 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level goto error; } else if (equal == 0) { - if (PyErr_WarnEx(PyExc_ImportWarning, + if (PyErr_WarnEx(PyExc_DeprecationWarning, "__package__ != __spec__.parent", 1) < 0) { goto error; } From adc28150390dea8da870638269563963c5501a39 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Wed, 5 Oct 2022 15:25:55 -0700 Subject: [PATCH 055/151] gh-96865: [Enum] fix Flag to use CONFORM boundary (GH-97528) --- Lib/enum.py | 2 +- Lib/test/test_enum.py | 2 +- .../2022-09-24-18-56-23.gh-issue-96865.o9WUkW.rst | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-09-24-18-56-23.gh-issue-96865.o9WUkW.rst diff --git a/Lib/enum.py b/Lib/enum.py index c3aafc283719ab..a66c344dbc3db2 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1269,7 +1269,7 @@ class FlagBoundary(StrEnum): STRICT, CONFORM, EJECT, KEEP = FlagBoundary -class Flag(Enum, boundary=STRICT): +class Flag(Enum, boundary=CONFORM): """ Support for flags """ diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index ad421f87d6d07c..f50017d916f5e3 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2927,7 +2927,7 @@ def test_bool(self): self.assertEqual(bool(f.value), bool(f)) def test_boundary(self): - self.assertIs(enum.Flag._boundary_, STRICT) + self.assertIs(enum.Flag._boundary_, CONFORM) class Iron(Flag, boundary=STRICT): ONE = 1 TWO = 2 diff --git a/Misc/NEWS.d/next/Library/2022-09-24-18-56-23.gh-issue-96865.o9WUkW.rst b/Misc/NEWS.d/next/Library/2022-09-24-18-56-23.gh-issue-96865.o9WUkW.rst new file mode 100644 index 00000000000000..b054fdeee0785c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-09-24-18-56-23.gh-issue-96865.o9WUkW.rst @@ -0,0 +1,9 @@ +fix Flag to use boundary CONFORM + +This restores previous Flag behavior of allowing flags with non-sequential values to be combined; e.g. + + class Skip(Flag): + TWO = 2 + EIGHT = 8 + + Skip.TWO | Skip.EIGHT -> From 3335bbe620a8ede20667a3bb3ad10a7496bad5cd Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 5 Oct 2022 16:42:01 -0700 Subject: [PATCH 056/151] GH-88968: Add notes about socket ownership transfers (#97936) --- Doc/library/asyncio-eventloop.rst | 24 ++++++++++++++++++++++++ Doc/library/asyncio-stream.rst | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 6fe95687c151de..2f306b7edb8fe2 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -468,6 +468,12 @@ Opening network connections *happy_eyeballs_delay*, *interleave* and *local_addr* should be specified. + .. note:: + + The *sock* argument transfers ownership of the socket to the + transport created. To close the socket, call the transport's + :meth:`~asyncio.BaseTransport.close` method. + * *local_addr*, if given, is a ``(local_host, local_port)`` tuple used to bind the socket locally. The *local_host* and *local_port* are looked up using ``getaddrinfo()``, similarly to *host* and *port*. @@ -577,6 +583,12 @@ Opening network connections transport. If specified, *local_addr* and *remote_addr* should be omitted (must be :const:`None`). + .. note:: + + The *sock* argument transfers ownership of the socket to the + transport created. To close the socket, call the transport's + :meth:`~asyncio.BaseTransport.close` method. + See :ref:`UDP echo client protocol ` and :ref:`UDP echo server protocol ` examples. @@ -688,6 +700,12 @@ Creating network servers * *sock* can optionally be specified in order to use a preexisting socket object. If specified, *host* and *port* must not be specified. + .. note:: + + The *sock* argument transfers ownership of the socket to the + server created. To close the socket, call the server's + :meth:`~asyncio.Server.close` method. + * *backlog* is the maximum number of queued connections passed to :meth:`~socket.socket.listen` (defaults to 100). @@ -789,6 +807,12 @@ Creating network servers * *sock* is a preexisting socket object returned from :meth:`socket.accept `. + .. note:: + + The *sock* argument transfers ownership of the socket to the + transport created. To close the socket, call the transport's + :meth:`~asyncio.BaseTransport.close` method. + * *ssl* can be set to an :class:`~ssl.SSLContext` to enable SSL over the accepted connections. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 9b468670a2521e..ce88d70d6607d9 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -67,6 +67,12 @@ and work with streams: The rest of the arguments are passed directly to :meth:`loop.create_connection`. + .. note:: + + The *sock* argument transfers ownership of the socket to the + :class:`StreamWriter` created. To close the socket, call its + :meth:`~asyncio.StreamWriter.close` method. + .. versionchanged:: 3.7 Added the *ssl_handshake_timeout* parameter. @@ -103,6 +109,12 @@ and work with streams: The rest of the arguments are passed directly to :meth:`loop.create_server`. + .. note:: + + The *sock* argument transfers ownership of the socket to the + server created. To close the socket, call the server's + :meth:`~asyncio.Server.close` method. + .. versionchanged:: 3.7 Added the *ssl_handshake_timeout* and *start_serving* parameters. @@ -123,6 +135,12 @@ and work with streams: See also the documentation of :meth:`loop.create_unix_connection`. + .. note:: + + The *sock* argument transfers ownership of the socket to the + :class:`StreamWriter` created. To close the socket, call its + :meth:`~asyncio.StreamWriter.close` method. + .. availability:: Unix. .. versionchanged:: 3.7 @@ -143,6 +161,12 @@ and work with streams: See also the documentation of :meth:`loop.create_unix_server`. + .. note:: + + The *sock* argument transfers ownership of the socket to the + server created. To close the socket, call the server's + :meth:`~asyncio.Server.close` method. + .. availability:: Unix. .. versionchanged:: 3.7 From 97a27d8932a895157e2b70e47f1bd264b59588a5 Mon Sep 17 00:00:00 2001 From: 180909 <734461790@qq.com> Date: Thu, 6 Oct 2022 07:57:42 +0800 Subject: [PATCH 057/151] gh-95691: Doc BufferedWriter and BufferedReader (#95703) --- Doc/library/io.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 7ec990c3212a3e..8fd6b3537019aa 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -272,7 +272,7 @@ to provide an interface to files in the machine's file system. The :class:`BufferedIOBase` ABC extends :class:`IOBase`. It deals with buffering on a raw binary stream (:class:`RawIOBase`). Its subclasses, :class:`BufferedWriter`, :class:`BufferedReader`, and :class:`BufferedRWPair` -buffer raw binary streams that are readable, writable, and both readable and writable, +buffer raw binary streams that are writable, readable, and both readable and writable, respectively. :class:`BufferedRandom` provides a buffered interface to seekable streams. Another :class:`BufferedIOBase` subclass, :class:`BytesIO`, is a stream of in-memory bytes. From a857648bf31bb7ad40be5298c0393d01d626ab9d Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 6 Oct 2022 15:16:16 +0300 Subject: [PATCH 058/151] gh-94808: Cover `PyObject_PyBytes` case with custom `__bytes__` method (#96610) Co-authored-by: Jelle Zijlstra --- Lib/test/test_long.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index d092e0176c2616..b6407b5a7c881e 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1518,6 +1518,22 @@ def __init__(self, value): self.assertEqual(i, 1) self.assertEqual(getattr(i, 'foo', 'none'), 'bar') + class ValidBytes: + def __bytes__(self): + return b'\x01' + class InvalidBytes: + def __bytes__(self): + return 'abc' + class MissingBytes: ... + class RaisingBytes: + def __bytes__(self): + 1 / 0 + + self.assertEqual(int.from_bytes(ValidBytes()), 1) + self.assertRaises(TypeError, int.from_bytes, InvalidBytes()) + self.assertRaises(TypeError, int.from_bytes, MissingBytes()) + self.assertRaises(ZeroDivisionError, int.from_bytes, RaisingBytes()) + @support.cpython_only def test_from_bytes_small(self): # bpo-46361 From d458682a2e793b672504d902d728f53ab5b22ae7 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 6 Oct 2022 18:20:22 +0300 Subject: [PATCH 059/151] gh-94808: Cover `PyUnicode_Count` in CAPI (#96929) --- Lib/test/test_unicode.py | 38 +++++++++++++++++++++++++++++++++++++ Modules/_testcapi/unicode.c | 21 ++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 63bccb72e04646..30faaaf83bec96 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2945,6 +2945,44 @@ def test_asutf8andsize(self): self.assertEqual(unicode_asutf8andsize(nonbmp), (b'\xf4\x8f\xbf\xbf', 4)) self.assertRaises(UnicodeEncodeError, unicode_asutf8andsize, 'a\ud800b\udfffc') + # Test PyUnicode_Count() + @support.cpython_only + @unittest.skipIf(_testcapi is None, 'need _testcapi module') + def test_count(self): + from _testcapi import unicode_count + + st = 'abcabd' + self.assertEqual(unicode_count(st, 'a', 0, len(st)), 2) + self.assertEqual(unicode_count(st, 'ab', 0, len(st)), 2) + self.assertEqual(unicode_count(st, 'abc', 0, len(st)), 1) + self.assertEqual(unicode_count(st, 'а', 0, len(st)), 0) # cyrillic "a" + # start < end + self.assertEqual(unicode_count(st, 'a', 3, len(st)), 1) + self.assertEqual(unicode_count(st, 'a', 4, len(st)), 0) + self.assertEqual(unicode_count(st, 'a', 0, sys.maxsize), 2) + # start >= end + self.assertEqual(unicode_count(st, 'abc', 0, 0), 0) + self.assertEqual(unicode_count(st, 'a', 3, 2), 0) + self.assertEqual(unicode_count(st, 'a', sys.maxsize, 5), 0) + # negative + self.assertEqual(unicode_count(st, 'ab', -len(st), -1), 2) + self.assertEqual(unicode_count(st, 'a', -len(st), -3), 1) + # wrong args + self.assertRaises(TypeError, unicode_count, 'a', 'a') + self.assertRaises(TypeError, unicode_count, 'a', 'a', 1) + self.assertRaises(TypeError, unicode_count, 1, 'a', 0, 1) + self.assertRaises(TypeError, unicode_count, 'a', 1, 0, 1) + # empty string + self.assertEqual(unicode_count('abc', '', 0, 3), 4) + self.assertEqual(unicode_count('abc', '', 1, 3), 3) + self.assertEqual(unicode_count('', '', 0, 1), 1) + self.assertEqual(unicode_count('', 'a', 0, 1), 0) + # different unicode kinds + for uni in "\xa1", "\u8000\u8080", "\ud800\udc02", "\U0001f100\U0001f1f1": + for ch in uni: + self.assertEqual(unicode_count(uni, ch, 0, len(uni)), 1) + self.assertEqual(unicode_count(st, ch, 0, len(st)), 0) + # Test PyUnicode_FindChar() @support.cpython_only @unittest.skipIf(_testcapi is None, 'need _testcapi module') diff --git a/Modules/_testcapi/unicode.c b/Modules/_testcapi/unicode.c index d0f1e2abdc8259..d5c4a9e5b95ec6 100644 --- a/Modules/_testcapi/unicode.c +++ b/Modules/_testcapi/unicode.c @@ -223,6 +223,26 @@ unicode_asutf8andsize(PyObject *self, PyObject *args) return Py_BuildValue("(Nn)", result, utf8_len); } +static PyObject * +unicode_count(PyObject *self, PyObject *args) +{ + PyObject *str; + PyObject *substr; + Py_ssize_t result; + Py_ssize_t start, end; + + if (!PyArg_ParseTuple(args, "UUnn:unicode_count", &str, &substr, + &start, &end)) { + return NULL; + } + + result = PyUnicode_Count(str, substr, start, end); + if (result == -1) + return NULL; + else + return PyLong_FromSsize_t(result); +} + static PyObject * unicode_findchar(PyObject *self, PyObject *args) { @@ -696,6 +716,7 @@ static PyMethodDef TestMethods[] = { {"unicode_asucs4", unicode_asucs4, METH_VARARGS}, {"unicode_asutf8", unicode_asutf8, METH_VARARGS}, {"unicode_asutf8andsize", unicode_asutf8andsize, METH_VARARGS}, + {"unicode_count", unicode_count, METH_VARARGS}, {"unicode_findchar", unicode_findchar, METH_VARARGS}, {"unicode_copycharacters", unicode_copycharacters, METH_VARARGS}, {NULL}, From 79fbf9797e0a1fd3bf39b86711cc4141c503cfa1 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 6 Oct 2022 09:11:47 -0700 Subject: [PATCH 060/151] gh-97897: Prevent os.mkfifo and os.mknod segfaults with macOS 13 SDK (GH-97944) The macOS 13 SDK includes support for the `mkfifoat` and `mknodat` system calls. Using the `dir_fd` option with either `os.mkfifo` or `os.mknod` could result in a segfault if cpython is built with the macOS 13 SDK but run on an earlier version of macOS. Prevent this by adding runtime support for detection of these system calls ("weaklinking") as is done for other newer syscalls on macOS. --- Lib/test/test_posix.py | 22 +++++++ ...2-10-05-15-26-58.gh-issue-97897.Rf-C6u.rst | 6 ++ Modules/posixmodule.c | 60 ++++++++++++++++--- 3 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2022-10-05-15-26-58.gh-issue-97897.Rf-C6u.rst diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index ae25ef55885dd6..e643d8e5a4ce63 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -2090,6 +2090,28 @@ def test_mkdir(self): with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"): os.mkdir("dir", dir_fd=0) + def test_mkfifo(self): + self._verify_available("HAVE_MKFIFOAT") + if self.mac_ver >= (13, 0): + self.assertIn("HAVE_MKFIFOAT", posix._have_functions) + + else: + self.assertNotIn("HAVE_MKFIFOAT", posix._have_functions) + + with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"): + os.mkfifo("path", dir_fd=0) + + def test_mknod(self): + self._verify_available("HAVE_MKNODAT") + if self.mac_ver >= (13, 0): + self.assertIn("HAVE_MKNODAT", posix._have_functions) + + else: + self.assertNotIn("HAVE_MKNODAT", posix._have_functions) + + with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"): + os.mknod("path", dir_fd=0) + def test_rename_replace(self): self._verify_available("HAVE_RENAMEAT") if self.mac_ver >= (10, 10): diff --git a/Misc/NEWS.d/next/macOS/2022-10-05-15-26-58.gh-issue-97897.Rf-C6u.rst b/Misc/NEWS.d/next/macOS/2022-10-05-15-26-58.gh-issue-97897.Rf-C6u.rst new file mode 100644 index 00000000000000..0d21e98b37c5a3 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2022-10-05-15-26-58.gh-issue-97897.Rf-C6u.rst @@ -0,0 +1,6 @@ +The macOS 13 SDK includes support for the ``mkfifoat`` and ``mknodat`` system calls. +Using the ``dir_fd`` option with either :func:`os.mkfifo` or :func:`os.mknod` could result in a +segfault if cpython is built with the macOS 13 SDK but run on an earlier +version of macOS. Prevent this by adding runtime support for detection of +these system calls ("weaklinking") as is done for other newer syscalls on +macOS. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 3810bc87c1fbab..cbdc259737583c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -91,6 +91,8 @@ # define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) # define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) # define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) # define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) @@ -175,6 +177,8 @@ # define HAVE_FUTIMENS_RUNTIME 1 # define HAVE_UTIMENSAT_RUNTIME 1 # define HAVE_PWRITEV_RUNTIME 1 +# define HAVE_MKFIFOAT_RUNTIME 1 +# define HAVE_MKNODAT_RUNTIME 1 #endif @@ -10619,18 +10623,35 @@ os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd) { int result; int async_err = 0; +#ifdef HAVE_MKFIFOAT + int mkfifoat_unavailable = 0; +#endif do { Py_BEGIN_ALLOW_THREADS #ifdef HAVE_MKFIFOAT - if (dir_fd != DEFAULT_DIR_FD) - result = mkfifoat(dir_fd, path->narrow, mode); - else + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_MKFIFOAT_RUNTIME) { + result = mkfifoat(dir_fd, path->narrow, mode); + + } else { + mkfifoat_unavailable = 1; + result = 0; + } + } else #endif result = mkfifo(path->narrow, mode); Py_END_ALLOW_THREADS } while (result != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + +#ifdef HAVE_MKFIFOAT + if (mkfifoat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + if (result != 0) return (!async_err) ? posix_error() : NULL; @@ -10671,18 +10692,33 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device, { int result; int async_err = 0; +#ifdef HAVE_MKNODAT + int mknodat_unavailable = 0; +#endif do { Py_BEGIN_ALLOW_THREADS #ifdef HAVE_MKNODAT - if (dir_fd != DEFAULT_DIR_FD) - result = mknodat(dir_fd, path->narrow, mode, device); - else + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_MKNODAT_RUNTIME) { + result = mknodat(dir_fd, path->narrow, mode, device); + + } else { + mknodat_unavailable = 1; + result = 0; + } + } else #endif result = mknod(path->narrow, mode, device); Py_END_ALLOW_THREADS } while (result != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); +#ifdef HAVE_MKNODAT + if (mknodat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif if (result != 0) return (!async_err) ? posix_error() : NULL; @@ -15525,6 +15561,14 @@ PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME) PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME) #endif +#ifdef HAVE_MKFIFOAT +PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME) +#endif + +#ifdef HAVE_MKNODAT +PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME) +#endif + #ifdef HAVE_RENAMEAT PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME) #endif @@ -15658,11 +15702,11 @@ static const struct have_function { #endif #ifdef HAVE_MKFIFOAT - { "HAVE_MKFIFOAT", NULL }, + { "HAVE_MKFIFOAT", probe_mkfifoat }, #endif #ifdef HAVE_MKNODAT - { "HAVE_MKNODAT", NULL }, + { "HAVE_MKNODAT", probe_mknodat }, #endif #ifdef HAVE_OPENAT From a0d2d63271bc16d9f97c217d5a734131edda9065 Mon Sep 17 00:00:00 2001 From: 180909 <734461790@qq.com> Date: Fri, 7 Oct 2022 00:52:21 +0800 Subject: [PATCH 061/151] gh-95986: Fix the example using match keyword (#95989) --- Doc/whatsnew/3.10.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index db8d9281b1f2ed..428a19453db522 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -669,6 +669,7 @@ Several other key features: GREEN = 1 BLUE = 2 + color = Color.GREEN match color: case Color.RED: print("I see red!") From 32cc2fd3fbe565cf61f592b65b5b85e3778034f5 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 6 Oct 2022 18:11:37 +0100 Subject: [PATCH 062/151] gh-93738: Disallow pre-v3 syntax in the C domain (#97962) Also, disable using invalid sphinx-lint 0.6.2. --- Doc/conf.py | 25 ------------------------- Doc/extending/newtypes.rst | 6 +++--- Doc/extending/newtypes_tutorial.rst | 2 +- Doc/howto/isolating-extensions.rst | 2 +- Doc/requirements.txt | 5 ++++- Doc/whatsnew/2.2.rst | 2 +- Doc/whatsnew/2.5.rst | 2 +- 7 files changed, 11 insertions(+), 33 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index 909f992d9d821a..be1c9fff51a277 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -239,28 +239,3 @@ # Relative filename of the data files refcount_file = 'data/refcounts.dat' stable_abi_file = 'data/stable_abi.dat' - -# Sphinx 2 and Sphinx 3 compatibility -# ----------------------------------- - -# bpo-40204: Allow Sphinx 2 syntax in the C domain -c_allow_pre_v3 = True - -# bpo-40204: Disable warnings on Sphinx 2 syntax of the C domain since the -# documentation is built with -W (warnings treated as errors). -c_warn_on_allowed_pre_v3 = False - -# Fix '!' not working with C domain when pre_v3 is enabled -import sphinx - -if sphinx.version_info[:2] < (5, 3): - from sphinx.domains.c import CXRefRole - - original_run = CXRefRole.run - - def new_run(self): - if self.disabled: - return super(CXRefRole, self).run() - return original_run(self) - - CXRefRole.run = new_run diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index a076eae534b91e..3de849ade78888 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -208,7 +208,7 @@ a special case, for which the new value passed to the handler is ``NULL``. Python supports two pairs of attribute handlers; a type that supports attributes only needs to implement the functions for one pair. The difference is that one pair takes the name of the attribute as a :c:expr:`char\*`, while the other -accepts a :c:type:`PyObject\*`. Each type can use whichever pair makes more +accepts a :c:expr:`PyObject*`. Each type can use whichever pair makes more sense for the implementation's convenience. :: getattrfunc tp_getattr; /* char * version */ @@ -219,7 +219,7 @@ sense for the implementation's convenience. :: If accessing attributes of an object is always a simple operation (this will be explained shortly), there are generic implementations which can be used to -provide the :c:type:`PyObject\*` version of the attribute management functions. +provide the :c:expr:`PyObject*` version of the attribute management functions. The actual need for type-specific attribute handlers almost completely disappeared starting with Python 2.2, though there are many examples which have not been updated to use some of the new generic mechanism that is available. @@ -341,7 +341,7 @@ Type-specific Attribute Management For simplicity, only the :c:expr:`char\*` version will be demonstrated here; the type of the name parameter is the only difference between the :c:expr:`char\*` -and :c:type:`PyObject\*` flavors of the interface. This example effectively does +and :c:expr:`PyObject*` flavors of the interface. This example effectively does the same thing as the generic example above, but does not use the generic support added in Python 2.2. It explains how the handler functions are called, so that if you do need to extend their functionality, you'll understand diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 34c25d1f6f199c..5d4a3f06dd5402 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -24,7 +24,7 @@ The Basics ========== The :term:`CPython` runtime sees all Python objects as variables of type -:c:type:`PyObject\*`, which serves as a "base type" for all Python objects. +:c:expr:`PyObject*`, which serves as a "base type" for all Python objects. The :c:type:`PyObject` structure itself only contains the object's :term:`reference count` and a pointer to the object's "type object". This is where the action is; the type object determines which (C) functions diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index 8ee7e5e28479a5..2657b4ec6aaf9f 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -411,7 +411,7 @@ that subclass, which may be defined in different module than yours. pass For a method to get its "defining class", it must use the -:c:data:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS` +:data:`METH_METHOD | METH_FASTCALL | METH_KEYWORDS` :c:type:`calling convention ` and the corresponding :c:type:`PyCMethod` signature:: diff --git a/Doc/requirements.txt b/Doc/requirements.txt index f8a7f9db144c21..be058733fcf4d7 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -7,7 +7,10 @@ sphinx==4.5.0 blurb -sphinx-lint<1 +# sphinx-lint 0.6.2 yields many default role errors due to the new regular +# expression used for default role detection, so we don't use the version +# until the errors are fixed. +sphinx-lint<1,!=0.6.2 # The theme used by the documentation is stored separately, so we need # to install that as well. diff --git a/Doc/whatsnew/2.2.rst b/Doc/whatsnew/2.2.rst index bfb2aacbc07747..39997661bb96c4 100644 --- a/Doc/whatsnew/2.2.rst +++ b/Doc/whatsnew/2.2.rst @@ -1102,7 +1102,7 @@ code, none of the changes described here will affect you very much. * A different argument parsing function, :c:func:`PyArg_UnpackTuple`, has been added that's simpler and presumably faster. Instead of specifying a format string, the caller simply gives the minimum and maximum number of arguments - expected, and a set of pointers to :c:type:`PyObject\*` variables that will be + expected, and a set of pointers to :c:expr:`PyObject*` variables that will be filled in with argument values. * Two new flags :const:`METH_NOARGS` and :const:`METH_O` are available in method diff --git a/Doc/whatsnew/2.5.rst b/Doc/whatsnew/2.5.rst index 0aca2fe697ccdb..dcfaef6ed29494 100644 --- a/Doc/whatsnew/2.5.rst +++ b/Doc/whatsnew/2.5.rst @@ -1725,7 +1725,7 @@ attribute of the function object to change this:: ``ctypes.pythonapi`` object. This object does *not* release the global interpreter lock before calling a function, because the lock must be held when calling into the interpreter's code. There's a :class:`py_object()` type -constructor that will create a :c:type:`PyObject \*` pointer. A simple usage:: +constructor that will create a :c:expr:`PyObject *` pointer. A simple usage:: import ctypes From bf234f5c0ff5b4a21559eb96b17b643975a1fbd7 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Thu, 6 Oct 2022 22:48:19 +0530 Subject: [PATCH 063/151] GH-88050: fix race in closing subprocess pipe in asyncio (#97951) Check for None when iterating over `self._pipes.values()`. --- Lib/asyncio/base_subprocess.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py index c2ca4a2792f666..e15bb4141fc02a 100644 --- a/Lib/asyncio/base_subprocess.py +++ b/Lib/asyncio/base_subprocess.py @@ -216,7 +216,9 @@ def _process_exited(self, returncode): self._proc.returncode = returncode self._call(self._protocol.process_exited) for p in self._pipes.values(): - p.pipe.close() + if p is not None: + p.pipe.close() + self._try_finish() async def _wait(self): From af96109373994b5246031aaef1ecaf21b3a60c3e Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Thu, 6 Oct 2022 13:39:17 -0400 Subject: [PATCH 064/151] gh-94808: Coverage: Test that maximum indentation level is handled (#95926) * gh-94808: Coverage: Test that maximum indentation level is handled * Use "compile" rather than "exec" --- Lib/test/test_tokenize.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 1272e1e9be002e..47f2c06685bcaa 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -3,7 +3,7 @@ from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP, STRING, ENDMARKER, ENCODING, tok_name, detect_encoding, open as tokenize_open, Untokenizer, generate_tokens, - NEWLINE, _generate_tokens_from_c_tokenizer) + NEWLINE, _generate_tokens_from_c_tokenizer, DEDENT) from io import BytesIO, StringIO import unittest from textwrap import dedent @@ -2512,6 +2512,26 @@ def get_tokens(string): self.assertRaises(SyntaxError, get_tokens, "("*1000+"a"+")"*1000) self.assertRaises(SyntaxError, get_tokens, "]") + def test_max_indent(self): + MAXINDENT = 100 + + def generate_source(indents): + source = ''.join((' ' * x) + 'if True:\n' for x in range(indents)) + source += ' ' * indents + 'pass\n' + return source + + valid = generate_source(MAXINDENT - 1) + tokens = list(_generate_tokens_from_c_tokenizer(valid)) + self.assertEqual(tokens[-1].type, DEDENT) + compile(valid, "", "exec") + + invalid = generate_source(MAXINDENT) + tokens = list(_generate_tokens_from_c_tokenizer(invalid)) + self.assertEqual(tokens[-1].type, NEWLINE) + self.assertRaises( + IndentationError, compile, invalid, "", "exec" + ) + def test_continuation_lines_indentation(self): def get_tokens(string): return [(kind, string) for (kind, string, *_) in _generate_tokens_from_c_tokenizer(string)] From 149ed7f58ef31dce0a55557e366a336900787047 Mon Sep 17 00:00:00 2001 From: larryhastings Date: Thu, 6 Oct 2022 12:23:20 -0700 Subject: [PATCH 065/151] gh-97943: PyFunction_GetAnnotations should return a borrowed reference. (#97949) --- .../2022-10-05-17-02-22.gh-issue-97943.LYAWlE.rst | 2 ++ Objects/funcobject.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-05-17-02-22.gh-issue-97943.LYAWlE.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-05-17-02-22.gh-issue-97943.LYAWlE.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-17-02-22.gh-issue-97943.LYAWlE.rst new file mode 100644 index 00000000000000..9b4a421a9d475a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-17-02-22.gh-issue-97943.LYAWlE.rst @@ -0,0 +1,2 @@ +Bugfix: :func:`PyFunction_GetAnnotations` should return a borrowed +reference. It was returning a new reference. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index ee863507593d37..7704a515eb2185 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -340,7 +340,6 @@ func_get_annotation_dict(PyFunctionObject *op) } Py_SETREF(op->func_annotations, ann_dict); } - Py_INCREF(op->func_annotations); assert(PyDict_Check(op->func_annotations)); return op->func_annotations; } @@ -575,7 +574,11 @@ func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored)) if (op->func_annotations == NULL) return NULL; } - return func_get_annotation_dict(op); + PyObject *d = func_get_annotation_dict(op); + if (d) { + Py_INCREF(d); + } + return d; } static int From ff0d6218d7586890433bb03072793f756d76a577 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 6 Oct 2022 15:24:17 -0400 Subject: [PATCH 066/151] gh-86482: Document assignment expression need for ()s (#23291) Co-authored-by: Jelle Zijlstra --- Doc/reference/expressions.rst | 7 +++++++ .../2020-11-15-02-08-43.bpo-42316.LqdkWK.rst | 1 + 2 files changed, 8 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-11-15-02-08-43.bpo-42316.LqdkWK.rst diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index a6ca55dafe5365..a661e03b173498 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1766,6 +1766,13 @@ Or, when processing a file stream in chunks: while chunk := file.read(9000): process(chunk) +Assignment expressions must be surrounded by parentheses when used +as sub-expressions in slicing, conditional, lambda, +keyword-argument, and comprehension-if expressions +and in ``assert`` and ``with`` statements. +In all other places where they can be used, parentheses are not required, +including in ``if`` and ``while`` statements. + .. versionadded:: 3.8 See :pep:`572` for more details about assignment expressions. diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-11-15-02-08-43.bpo-42316.LqdkWK.rst b/Misc/NEWS.d/next/Core and Builtins/2020-11-15-02-08-43.bpo-42316.LqdkWK.rst new file mode 100644 index 00000000000000..ea997800bf076e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-11-15-02-08-43.bpo-42316.LqdkWK.rst @@ -0,0 +1 @@ +Document some places where an assignment expression needs parentheses. From d68c84f5b5e2219762c4f5659cdfb9f84deecfe8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 6 Oct 2022 15:25:24 -0400 Subject: [PATCH 067/151] gh-97781: Apply changes from importlib_metadata 5. (GH-97785) * gh-97781: Apply changes from importlib_metadata 5. * Apply changes from upstream * Apply changes from upstream. --- Doc/library/importlib.metadata.rst | 108 ++++++--- Lib/importlib/metadata/__init__.py | 211 +----------------- Lib/test/test_importlib/test_main.py | 10 - Lib/test/test_importlib/test_metadata_api.py | 56 ----- ...2-10-03-13-25-19.gh-issue-97781.gCLLef.rst | 5 + 5 files changed, 88 insertions(+), 302 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-03-13-25-19.gh-issue-97781.gCLLef.rst diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index a1af7a754ba4ef..094c2688a8cd87 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -13,21 +13,39 @@ **Source code:** :source:`Lib/importlib/metadata/__init__.py` -``importlib.metadata`` is a library that provides access to installed -package metadata, such as its entry points or its -top-level name. Built in part on Python's import system, this library +``importlib_metadata`` is a library that provides access to +the metadata of an installed `Distribution Package `_, +such as its entry points +or its top-level names (`Import Package `_\s, modules, if any). +Built in part on Python's import system, this library intends to replace similar functionality in the `entry point API`_ and `metadata API`_ of ``pkg_resources``. Along with :mod:`importlib.resources`, this package can eliminate the need to use the older and less efficient ``pkg_resources`` package. -By "installed package" we generally mean a third-party package installed into -Python's ``site-packages`` directory via tools such as `pip -`_. Specifically, -it means a package with either a discoverable ``dist-info`` or ``egg-info`` -directory, and metadata defined by :pep:`566` or its older specifications. -By default, package metadata can live on the file system or in zip archives on +``importlib_metadata`` operates on third-party *distribution packages* +installed into Python's ``site-packages`` directory via tools such as +`pip `_. +Specifically, it works with distributions with discoverable +``dist-info`` or ``egg-info`` directories, +and metadata defined by the `Core metadata specifications `_. + +.. important:: + + These are *not* necessarily equivalent to or correspond 1:1 with + the top-level *import package* names + that can be imported inside Python code. + One *distribution package* can contain multiple *import packages* + (and single modules), + and one top-level *import package* + may map to multiple *distribution packages* + if it is a namespace package. + You can use :ref:`package_distributions() ` + to get a mapping between them. + +By default, distribution metadata can live on the file system +or in zip archives on :data:`sys.path`. Through an extension mechanism, the metadata can live almost anywhere. @@ -37,12 +55,19 @@ anywhere. https://importlib-metadata.readthedocs.io/ The documentation for ``importlib_metadata``, which supplies a backport of ``importlib.metadata``. + This includes an `API reference + `__ + for this module's classes and functions, + as well as a `migration guide + `__ + for existing users of ``pkg_resources``. Overview ======== -Let's say you wanted to get the version string for a package you've installed +Let's say you wanted to get the version string for a +`Distribution Package `_ you've installed using ``pip``. We start by creating a virtual environment and installing something into it: @@ -151,11 +176,10 @@ for more information on entry points, their definition, and usage. The "selectable" entry points were introduced in ``importlib_metadata`` 3.6 and Python 3.10. Prior to those changes, ``entry_points`` accepted no parameters and always returned a dictionary of entry points, keyed -by group. For compatibility, if no parameters are passed to entry_points, -a ``SelectableGroups`` object is returned, implementing that dict -interface. In the future, calling ``entry_points`` with no parameters -will return an ``EntryPoints`` object. Users should rely on the selection -interface to retrieve entry points by group. +by group. With ``importlib_metadata`` 5.0 and Python 3.12, +``entry_points`` always returns an ``EntryPoints`` object. See +`backports.entry_points_selectable `_ +for compatibility options. .. _metadata: @@ -163,7 +187,8 @@ interface to retrieve entry points by group. Distribution metadata --------------------- -Every distribution includes some metadata, which you can extract using the +Every `Distribution Package `_ includes some metadata, +which you can extract using the ``metadata()`` function:: >>> wheel_metadata = metadata('wheel') # doctest: +SKIP @@ -201,7 +226,8 @@ all the metadata in a JSON-compatible form per :PEP:`566`:: Distribution versions --------------------- -The ``version()`` function is the quickest way to get a distribution's version +The ``version()`` function is the quickest way to get a +`Distribution Package `_'s version number, as a string:: >>> version('wheel') # doctest: +SKIP @@ -214,7 +240,8 @@ Distribution files ------------------ You can also get the full set of files contained within a distribution. The -``files()`` function takes a distribution package name and returns all of the +``files()`` function takes a `Distribution Package `_ name +and returns all of the files installed by this distribution. Each file object returned is a ``PackagePath``, a :class:`pathlib.PurePath` derived object with additional ``dist``, ``size``, and ``hash`` properties as indicated by the metadata. For example:: @@ -259,19 +286,24 @@ distribution is not known to have the metadata present. Distribution requirements ------------------------- -To get the full set of requirements for a distribution, use the ``requires()`` +To get the full set of requirements for a `Distribution Package `_, +use the ``requires()`` function:: >>> requires('wheel') # doctest: +SKIP ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] -Package distributions ---------------------- +.. _package-distributions: +.. _import-distribution-package-mapping: + +Mapping import to distribution packages +--------------------------------------- -A convenience method to resolve the distribution or -distributions (in the case of a namespace package) for top-level -Python packages or modules:: +A convenience method to resolve the `Distribution Package `_ +name (or names, in the case of a namespace package) +that provide each importable top-level +Python module or `Import Package `_:: >>> packages_distributions() {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...} @@ -285,7 +317,8 @@ Distributions While the above API is the most common and convenient usage, you can get all of that information from the ``Distribution`` class. A ``Distribution`` is an -abstract object that represents the metadata for a Python package. You can +abstract object that represents the metadata for +a Python `Distribution Package `_. You can get the ``Distribution`` instance:: >>> from importlib.metadata import distribution # doctest: +SKIP @@ -305,14 +338,16 @@ instance:: >>> dist.metadata['License'] # doctest: +SKIP 'MIT' -The full set of available metadata is not described here. See :pep:`566` -for additional details. +The full set of available metadata is not described here. +See the `Core metadata specifications `_ for additional details. Distribution Discovery ====================== -By default, this package provides built-in support for discovery of metadata for file system and zip file packages. This metadata finder search defaults to ``sys.path``, but varies slightly in how it interprets those values from how other import machinery does. In particular: +By default, this package provides built-in support for discovery of metadata +for file system and zip file `Distribution Package `_\s. +This metadata finder search defaults to ``sys.path``, but varies slightly in how it interprets those values from how other import machinery does. In particular: - ``importlib.metadata`` does not honor :class:`bytes` objects on ``sys.path``. - ``importlib.metadata`` will incidentally honor :py:class:`pathlib.Path` objects on ``sys.path`` even though such values will be ignored for imports. @@ -321,15 +356,18 @@ By default, this package provides built-in support for discovery of metadata for Extending the search algorithm ============================== -Because package metadata is not available through :data:`sys.path` searches, or -package loaders directly, the metadata for a package is found through import -system :ref:`finders `. To find a distribution package's metadata, +Because `Distribution Package `_ metadata +is not available through :data:`sys.path` searches, or +package loaders directly, +the metadata for a distribution is found through import +system `finders`_. To find a distribution package's metadata, ``importlib.metadata`` queries the list of :term:`meta path finders ` on :data:`sys.meta_path`. -The default ``PathFinder`` for Python includes a hook that calls into -``importlib.metadata.MetadataPathFinder`` for finding distributions -loaded from typical file-system-based paths. +By default ``importlib_metadata`` installs a finder for distribution packages +found on the file system. +This finder doesn't actually find any *distributions*, +but it can find their metadata. The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the interface expected of finders by Python's import system. @@ -358,4 +396,4 @@ a custom finder, return instances of this derived ``Distribution`` in the .. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points .. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api -.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html +.. _`finders`: https://docs.python.org/3/reference/import.html#finders-and-loaders diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index b01de145c33671..40ab1a1aaac328 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -24,7 +24,7 @@ from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import List, Mapping, Optional, Union +from typing import List, Mapping, Optional __all__ = [ @@ -134,6 +134,7 @@ class DeprecatedTuple: 1 """ + # Do not remove prior to 2023-05-01 or Python 3.13 _warn = functools.partial( warnings.warn, "EntryPoint tuple interface is deprecated. Access members by name.", @@ -184,6 +185,10 @@ class EntryPoint(DeprecatedTuple): following the attr, and following any extras. """ + name: str + value: str + group: str + dist: Optional['Distribution'] = None def __init__(self, name, value, group): @@ -218,17 +223,6 @@ def _for(self, dist): vars(self).update(dist=dist) return self - def __iter__(self): - """ - Supply iter so one may construct dicts of EntryPoints by name. - """ - msg = ( - "Construction of dict of EntryPoints is deprecated in " - "favor of EntryPoints." - ) - warnings.warn(msg, DeprecationWarning) - return iter((self.name, self)) - def matches(self, **params): """ EntryPoint matches the given parameters. @@ -274,77 +268,7 @@ def __hash__(self): return hash(self._key()) -class DeprecatedList(list): - """ - Allow an otherwise immutable object to implement mutability - for compatibility. - - >>> recwarn = getfixture('recwarn') - >>> dl = DeprecatedList(range(3)) - >>> dl[0] = 1 - >>> dl.append(3) - >>> del dl[3] - >>> dl.reverse() - >>> dl.sort() - >>> dl.extend([4]) - >>> dl.pop(-1) - 4 - >>> dl.remove(1) - >>> dl += [5] - >>> dl + [6] - [1, 2, 5, 6] - >>> dl + (6,) - [1, 2, 5, 6] - >>> dl.insert(0, 0) - >>> dl - [0, 1, 2, 5] - >>> dl == [0, 1, 2, 5] - True - >>> dl == (0, 1, 2, 5) - True - >>> len(recwarn) - 1 - """ - - __slots__ = () - - _warn = functools.partial( - warnings.warn, - "EntryPoints list interface is deprecated. Cast to list if needed.", - DeprecationWarning, - stacklevel=2, - ) - - def _wrap_deprecated_method(method_name: str): # type: ignore - def wrapped(self, *args, **kwargs): - self._warn() - return getattr(super(), method_name)(*args, **kwargs) - - return method_name, wrapped - - locals().update( - map( - _wrap_deprecated_method, - '__setitem__ __delitem__ append reverse extend pop remove ' - '__iadd__ insert sort'.split(), - ) - ) - - def __add__(self, other): - if not isinstance(other, tuple): - self._warn() - other = tuple(other) - return self.__class__(tuple(self) + other) - - def __eq__(self, other): - if not isinstance(other, tuple): - self._warn() - other = tuple(other) - - return tuple(self).__eq__(other) - - -class EntryPoints(DeprecatedList): +class EntryPoints(tuple): """ An immutable collection of selectable EntryPoint objects. """ @@ -355,14 +279,6 @@ def __getitem__(self, name): # -> EntryPoint: """ Get the EntryPoint in self matching name. """ - if isinstance(name, int): - warnings.warn( - "Accessing entry points by index is deprecated. " - "Cast to tuple if needed.", - DeprecationWarning, - stacklevel=2, - ) - return super().__getitem__(name) try: return next(iter(self.select(name=name))) except StopIteration: @@ -386,10 +302,6 @@ def names(self): def groups(self): """ Return the set of all groups of all entry points. - - For coverage while SelectableGroups is present. - >>> EntryPoints().groups - set() """ return {ep.group for ep in self} @@ -405,101 +317,6 @@ def _from_text(text): ) -class Deprecated: - """ - Compatibility add-in for mapping to indicate that - mapping behavior is deprecated. - - >>> recwarn = getfixture('recwarn') - >>> class DeprecatedDict(Deprecated, dict): pass - >>> dd = DeprecatedDict(foo='bar') - >>> dd.get('baz', None) - >>> dd['foo'] - 'bar' - >>> list(dd) - ['foo'] - >>> list(dd.keys()) - ['foo'] - >>> 'foo' in dd - True - >>> list(dd.values()) - ['bar'] - >>> len(recwarn) - 1 - """ - - _warn = functools.partial( - warnings.warn, - "SelectableGroups dict interface is deprecated. Use select.", - DeprecationWarning, - stacklevel=2, - ) - - def __getitem__(self, name): - self._warn() - return super().__getitem__(name) - - def get(self, name, default=None): - self._warn() - return super().get(name, default) - - def __iter__(self): - self._warn() - return super().__iter__() - - def __contains__(self, *args): - self._warn() - return super().__contains__(*args) - - def keys(self): - self._warn() - return super().keys() - - def values(self): - self._warn() - return super().values() - - -class SelectableGroups(Deprecated, dict): - """ - A backward- and forward-compatible result from - entry_points that fully implements the dict interface. - """ - - @classmethod - def load(cls, eps): - by_group = operator.attrgetter('group') - ordered = sorted(eps, key=by_group) - grouped = itertools.groupby(ordered, by_group) - return cls((group, EntryPoints(eps)) for group, eps in grouped) - - @property - def _all(self): - """ - Reconstruct a list of all entrypoints from the groups. - """ - groups = super(Deprecated, self).values() - return EntryPoints(itertools.chain.from_iterable(groups)) - - @property - def groups(self): - return self._all.groups - - @property - def names(self): - """ - for coverage: - >>> SelectableGroups().names - set() - """ - return self._all.names - - def select(self, **params): - if not params: - return self - return self._all.select(**params) - - class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" @@ -1013,27 +830,19 @@ def version(distribution_name): """ -def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: +def entry_points(**params) -> EntryPoints: """Return EntryPoint objects for all installed packages. Pass selection parameters (group or name) to filter the result to entry points matching those properties (see EntryPoints.select()). - For compatibility, returns ``SelectableGroups`` object unless - selection parameters are supplied. In the future, this function - will return ``EntryPoints`` instead of ``SelectableGroups`` - even when no selection parameters are supplied. - - For maximum future compatibility, pass selection parameters - or invoke ``.select`` with parameters on the result. - - :return: EntryPoints or SelectableGroups for all installed packages. + :return: EntryPoints for all installed packages. """ eps = itertools.chain.from_iterable( dist.entry_points for dist in _unique(distributions()) ) - return SelectableGroups.load(eps).select(**params) + return EntryPoints(eps).select(**params) def files(distribution_name): diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index d9d067c4b23d66..30b68b6ae7d86e 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -1,8 +1,6 @@ import re -import json import pickle import unittest -import warnings import importlib.metadata try: @@ -260,14 +258,6 @@ def test_hashable(self): """EntryPoints should be hashable""" hash(self.ep) - def test_json_dump(self): - """ - json should not expect to be able to dump an EntryPoint - """ - with self.assertRaises(Exception): - with warnings.catch_warnings(record=True): - json.dumps(self.ep) - def test_module(self): assert self.ep.module == 'value' diff --git a/Lib/test/test_importlib/test_metadata_api.py b/Lib/test/test_importlib/test_metadata_api.py index 69c78e9820c044..71c47e62d27124 100644 --- a/Lib/test/test_importlib/test_metadata_api.py +++ b/Lib/test/test_importlib/test_metadata_api.py @@ -124,62 +124,6 @@ def test_entry_points_missing_name(self): def test_entry_points_missing_group(self): assert entry_points(group='missing') == () - def test_entry_points_dict_construction(self): - """ - Prior versions of entry_points() returned simple lists and - allowed casting those lists into maps by name using ``dict()``. - Capture this now deprecated use-case. - """ - with suppress_known_deprecation() as caught: - eps = dict(entry_points(group='entries')) - - assert 'main' in eps - assert eps['main'] == entry_points(group='entries')['main'] - - # check warning - expected = next(iter(caught)) - assert expected.category is DeprecationWarning - assert "Construction of dict of EntryPoints is deprecated" in str(expected) - - def test_entry_points_by_index(self): - """ - Prior versions of Distribution.entry_points would return a - tuple that allowed access by index. - Capture this now deprecated use-case - See python/importlib_metadata#300 and bpo-44246. - """ - eps = distribution('distinfo-pkg').entry_points - with suppress_known_deprecation() as caught: - eps[0] - - # check warning - expected = next(iter(caught)) - assert expected.category is DeprecationWarning - assert "Accessing entry points by index is deprecated" in str(expected) - - def test_entry_points_groups_getitem(self): - """ - Prior versions of entry_points() returned a dict. Ensure - that callers using '.__getitem__()' are supported but warned to - migrate. - """ - with suppress_known_deprecation(): - entry_points()['entries'] == entry_points(group='entries') - - with self.assertRaises(KeyError): - entry_points()['missing'] - - def test_entry_points_groups_get(self): - """ - Prior versions of entry_points() returned a dict. Ensure - that callers using '.get()' are supported but warned to - migrate. - """ - with suppress_known_deprecation(): - entry_points().get('missing', 'default') == 'default' - entry_points().get('entries', 'default') == entry_points()['entries'] - entry_points().get('missing', ()) == () - def test_entry_points_allows_no_attributes(self): ep = entry_points().select(group='entries', name='main') with self.assertRaises(AttributeError): diff --git a/Misc/NEWS.d/next/Library/2022-10-03-13-25-19.gh-issue-97781.gCLLef.rst b/Misc/NEWS.d/next/Library/2022-10-03-13-25-19.gh-issue-97781.gCLLef.rst new file mode 100644 index 00000000000000..8c36d9c3afd2dd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-03-13-25-19.gh-issue-97781.gCLLef.rst @@ -0,0 +1,5 @@ +Removed deprecated interfaces in ``importlib.metadata`` (entry points +accessed as dictionary, implicit dictionary construction of sequence of +``EntryPoint`` objects, mutablility of ``EntryPoints`` result, access of +entry point by index). ``entry_points`` now has a simpler, more +straightforward API (returning ``EntryPoints``). From aa83510634ee34e445456b96fe638fae08c88170 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 6 Oct 2022 13:29:52 -0700 Subject: [PATCH 068/151] Add Pynche's move to the What's new in 3.11 (#97974) * Add Pynche's move to the What's new in 3.11 * Update Doc/whatsnew/3.11.rst Co-authored-by: Ezio Melotti Co-authored-by: Ezio Melotti --- Doc/whatsnew/3.11.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index dafbbb673f0ed6..6103da76a34d03 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1667,6 +1667,10 @@ Removed Python's test suite." (Contributed by Victor Stinner in :issue:`46852`.) +* Pynche --- The Pythonically Natural Color and Hue Editor --- has been moved out + of ``Tools/scripts`` and is `being developed independently + `_ from the Python source tree. + Porting to Python 3.11 ====================== From a9a8f2ee1b964a8e09927a244c5929733b8d9d6a Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Thu, 6 Oct 2022 15:35:53 -0500 Subject: [PATCH 069/151] gh-94590: add signatures to operator itemgetter, attrgetter, methodcaller (#94591) These were intentionally skipped when operator was updated to use the argument clinic: https://github.com/python/cpython/issues/64385#issuecomment-1093641466 However, by not using the argument clinic, they missed out on getting signatures. This is a narrow PR to update the docstrings so that `__text_signature__` can be extracted from them. Updating to use the argument clinic is beyond scope. `methodcaller` uses `*args, **kwargs` to match variadic names used elsewhere, including in `operator.call`. --- Modules/_operator.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Modules/_operator.c b/Modules/_operator.c index 51ca74eeeaee21..77eabdb57af921 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1162,8 +1162,7 @@ static PyMemberDef itemgetter_members[] = { }; PyDoc_STRVAR(itemgetter_doc, -"itemgetter(item, ...) --> itemgetter object\n\ -\n\ +"itemgetter(item, /, *items)\n--\n\n\ Return a callable object that fetches the given item(s) from its operand.\n\ After f = itemgetter(2), the call f(r) returns r[2].\n\ After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])"); @@ -1523,8 +1522,7 @@ static PyMemberDef attrgetter_members[] = { }; PyDoc_STRVAR(attrgetter_doc, -"attrgetter(attr, ...) --> attrgetter object\n\ -\n\ +"attrgetter(attr, /, *attrs)\n--\n\n\ Return a callable object that fetches the given attribute(s) from its operand.\n\ After f = attrgetter('name'), the call f(r) returns r.name.\n\ After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\ @@ -1775,8 +1773,7 @@ static PyMethodDef methodcaller_methods[] = { {NULL} }; PyDoc_STRVAR(methodcaller_doc, -"methodcaller(name, ...) --> methodcaller object\n\ -\n\ +"methodcaller(name, /, *args, **kwargs)\n--\n\n\ Return a callable object that calls the given method on its operand.\n\ After f = methodcaller('name'), the call f(r) returns r.name().\n\ After g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\ From 22986c38f38cda8fa38d5a054263de96016bf052 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 6 Oct 2022 13:58:41 -0700 Subject: [PATCH 070/151] Docs: pin sphinx-lint (GH-97992) --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index be058733fcf4d7..960ac54d7b91ef 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -10,7 +10,7 @@ blurb # sphinx-lint 0.6.2 yields many default role errors due to the new regular # expression used for default role detection, so we don't use the version # until the errors are fixed. -sphinx-lint<1,!=0.6.2 +sphinx-lint==0.6.1 # The theme used by the documentation is stored separately, so we need # to install that as well. From 9ffbad4db04152f686c8ec6b0349bec7a5bacb2d Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 6 Oct 2022 14:01:06 -0700 Subject: [PATCH 071/151] gh-97850: Remove the open issues section from the import reference (#97935) Remove the open issues section from the import reference Tracking in https://github.com/python/cpython/issues/97850 instead. --- Doc/reference/import.rst | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 58f4ef897bdb7d..b7a53cd0886f29 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -1025,25 +1025,6 @@ and ``__main__.__spec__`` is set accordingly, they're still considered to populate the ``__main__`` namespace, and not during normal import. -Open issues -=========== - -XXX It would be really nice to have a diagram. - -XXX * (import_machinery.rst) how about a section devoted just to the -attributes of modules and packages, perhaps expanding upon or supplanting the -related entries in the data model reference page? - -XXX runpy, pkgutil, et al in the library manual should all get "See Also" -links at the top pointing to the new import system section. - -XXX Add more explanation regarding the different ways in which -``__main__`` is initialized? - -XXX Add more info on ``__main__`` quirks/pitfalls (i.e. copy from -:pep:`395`). - - References ========== From 7064cd5057d1f6fee44c1e2553066a7aaa3c99da Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 6 Oct 2022 15:40:22 -0700 Subject: [PATCH 072/151] gh-65961: Do not rely solely on `__cached__` (GH-97990) Make sure `__spec__.cached` (at minimum) can be used. --- Doc/c-api/import.rst | 9 +++ Doc/library/runpy.rst | 9 +++ Doc/whatsnew/3.12.rst | 4 +- Lib/cProfile.py | 8 ++- Lib/importlib/_bootstrap_external.py | 28 +++++--- Lib/inspect.py | 21 +----- Lib/profile.py | 8 ++- .../test_importlib/import_/test_helpers.py | 71 +++++++++++++++++++ Lib/test/test_inspect.py | 3 + Lib/test/test_pydoc.py | 2 +- ...2-10-06-17-59-22.gh-issue-65961.SXlQnI.rst | 2 + 11 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 Lib/test/test_importlib/import_/test_helpers.py create mode 100644 Misc/NEWS.d/next/Library/2022-10-06-17-59-22.gh-issue-65961.SXlQnI.rst diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 0922956c607bc3..a51619db6d3d97 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -150,6 +150,11 @@ Importing Modules See also :c:func:`PyImport_ExecCodeModuleEx` and :c:func:`PyImport_ExecCodeModuleWithPathnames`. + .. versionchanged:: 3.12 + The setting of :attr:`__cached__` and :attr:`__loader__` is + deprecated. See :class:`~importlib.machinery.ModuleSpec` for + alternatives. + .. c:function:: PyObject* PyImport_ExecCodeModuleEx(const char *name, PyObject *co, const char *pathname) @@ -167,6 +172,10 @@ Importing Modules .. versionadded:: 3.3 + .. versionchanged:: 3.12 + Setting :attr:`__cached__` is deprecated. See + :class:`~importlib.machinery.ModuleSpec` for alternatives. + .. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, const char *pathname, const char *cpathname) diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst index 26a4f1435214e8..501f4ddf5a3e3f 100644 --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -93,6 +93,11 @@ The :mod:`runpy` module provides two functions: run this way, as well as ensuring the real module name is always accessible as ``__spec__.name``. + .. versionchanged:: 3.12 + The setting of ``__cached__``, ``__loader__``, and + ``__package__`` are deprecated. See + :class:`~importlib.machinery.ModuleSpec` for alternatives. + .. function:: run_path(path_name, init_globals=None, run_name=None) .. index:: @@ -163,6 +168,10 @@ The :mod:`runpy` module provides two functions: case where ``__main__`` is imported from a valid sys.path entry rather than being executed directly. + .. versionchanged:: 3.12 + The setting of ``__cached__``, ``__loader__``, and + ``__package__`` are deprecated. + .. seealso:: :pep:`338` -- Executing modules as scripts diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 507ba35221467e..d18c31fbe9866e 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -280,8 +280,8 @@ Pending Removal in Python 3.14 * Creating :c:data:`immutable types ` with mutable bases using the C API. -* ``__package__`` will cease to be set or taken into consideration by - the import system (:gh:`97879`). +* ``__package__`` and ``__cached__`` will cease to be set or taken + into consideration by the import system (:gh:`97879`). Pending Removal in Future Versions diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 9fc97883020840..f7000a8bfa0ddb 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -7,6 +7,7 @@ __all__ = ["run", "runctx", "Profile"] import _lsprof +import importlib.machinery import profile as _pyprofile # ____________________________________________________________ @@ -169,9 +170,12 @@ def main(): sys.path.insert(0, os.path.dirname(progname)) with open(progname, 'rb') as fp: code = compile(fp.read(), progname, 'exec') + spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, + origin=progname) globs = { - '__file__': progname, - '__name__': '__main__', + '__spec__': spec, + '__file__': spec.origin, + '__name__': spec.name, '__package__': None, '__cached__': None, } diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index b3c31b9659d849..efda49382540c5 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -182,6 +182,16 @@ def _path_isabs(path): return path.startswith(path_separators) +def _path_abspath(path): + """Replacement for os.path.abspath.""" + if not _path_isabs(path): + for sep in path_separators: + path = path.removeprefix(f".{sep}") + return _path_join(_os.getcwd(), path) + else: + return path + + def _write_atomic(path, data, mode=0o666): """Best-effort function to write data to a path atomically. Be prepared to handle a FileExistsError if concurrent writing of the @@ -494,8 +504,7 @@ def cache_from_source(path, debug_override=None, *, optimization=None): # make it absolute (`C:\Somewhere\Foo\Bar`), then make it root-relative # (`Somewhere\Foo\Bar`), so we end up placing the bytecode file in an # unambiguous `C:\Bytecode\Somewhere\Foo\Bar\`. - if not _path_isabs(head): - head = _path_join(_os.getcwd(), head) + head = _path_abspath(head) # Strip initial drive from a Windows path. We know we have an absolute # path here, so the second part of the check rules out a POSIX path that @@ -808,11 +817,10 @@ def spec_from_file_location(name, location=None, *, loader=None, pass else: location = _os.fspath(location) - if not _path_isabs(location): - try: - location = _path_join(_os.getcwd(), location) - except OSError: - pass + try: + location = _path_abspath(location) + except OSError: + pass # If the location is on the filesystem, but doesn't actually exist, # we could return None here, indicating that the location is not @@ -1564,10 +1572,8 @@ def __init__(self, path, *loader_details): # Base (directory) path if not path or path == '.': self.path = _os.getcwd() - elif not _path_isabs(path): - self.path = _path_join(_os.getcwd(), path) else: - self.path = path + self.path = _path_abspath(path) self._path_mtime = -1 self._path_cache = set() self._relaxed_path_cache = set() @@ -1717,6 +1723,8 @@ def _fix_up_module(ns, name, pathname, cpathname=None): loader = SourceFileLoader(name, pathname) if not spec: spec = spec_from_file_location(name, pathname, loader=loader) + if cpathname: + spec.cached = _path_abspath(cpathname) try: ns['__spec__'] = spec ns['__loader__'] = loader diff --git a/Lib/inspect.py b/Lib/inspect.py index 498ee7ab9eaf8a..8a107a894909eb 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -281,30 +281,15 @@ def get_annotations(obj, *, globals=None, locals=None, eval_str=False): # ----------------------------------------------------------- type-checking def ismodule(object): - """Return true if the object is a module. - - Module objects provide these attributes: - __cached__ pathname to byte compiled file - __doc__ documentation string - __file__ filename (missing for built-in modules)""" + """Return true if the object is a module.""" return isinstance(object, types.ModuleType) def isclass(object): - """Return true if the object is a class. - - Class objects provide these attributes: - __doc__ documentation string - __module__ name of module in which this class was defined""" + """Return true if the object is a class.""" return isinstance(object, type) def ismethod(object): - """Return true if the object is an instance method. - - Instance method objects provide these attributes: - __doc__ documentation string - __name__ name with which this method was defined - __func__ function object containing implementation of method - __self__ instance to which this method is bound""" + """Return true if the object is an instance method.""" return isinstance(object, types.MethodType) def ismethoddescriptor(object): diff --git a/Lib/profile.py b/Lib/profile.py index d8599fb4eebd66..453e56285c510c 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -24,6 +24,7 @@ # governing permissions and limitations under the License. +import importlib.machinery import sys import time import marshal @@ -589,9 +590,12 @@ def main(): sys.path.insert(0, os.path.dirname(progname)) with open(progname, 'rb') as fp: code = compile(fp.read(), progname, 'exec') + spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, + origin=progname) globs = { - '__file__': progname, - '__name__': '__main__', + '__spec__': spec, + '__file__': spec.origin, + '__name__': spec.name, '__package__': None, '__cached__': None, } diff --git a/Lib/test/test_importlib/import_/test_helpers.py b/Lib/test/test_importlib/import_/test_helpers.py new file mode 100644 index 00000000000000..90df56f09fe52d --- /dev/null +++ b/Lib/test/test_importlib/import_/test_helpers.py @@ -0,0 +1,71 @@ +"""Tests for helper functions used by import.c .""" + +from importlib import _bootstrap_external, machinery +import os.path +import unittest + +from .. import util + + +class FixUpModuleTests: + + def test_no_loader_but_spec(self): + loader = object() + name = "hello" + path = "hello.py" + spec = machinery.ModuleSpec(name, loader) + ns = {"__spec__": spec} + _bootstrap_external._fix_up_module(ns, name, path) + + expected = {"__spec__": spec, "__loader__": loader, "__file__": path, + "__cached__": None} + self.assertEqual(ns, expected) + + def test_no_loader_no_spec_but_sourceless(self): + name = "hello" + path = "hello.py" + ns = {} + _bootstrap_external._fix_up_module(ns, name, path, path) + + expected = {"__file__": path, "__cached__": path} + + for key, val in expected.items(): + with self.subTest(f"{key}: {val}"): + self.assertEqual(ns[key], val) + + spec = ns["__spec__"] + self.assertIsInstance(spec, machinery.ModuleSpec) + self.assertEqual(spec.name, name) + self.assertEqual(spec.origin, os.path.abspath(path)) + self.assertEqual(spec.cached, os.path.abspath(path)) + self.assertIsInstance(spec.loader, machinery.SourcelessFileLoader) + self.assertEqual(spec.loader.name, name) + self.assertEqual(spec.loader.path, path) + self.assertEqual(spec.loader, ns["__loader__"]) + + def test_no_loader_no_spec_but_source(self): + name = "hello" + path = "hello.py" + ns = {} + _bootstrap_external._fix_up_module(ns, name, path) + + expected = {"__file__": path, "__cached__": None} + + for key, val in expected.items(): + with self.subTest(f"{key}: {val}"): + self.assertEqual(ns[key], val) + + spec = ns["__spec__"] + self.assertIsInstance(spec, machinery.ModuleSpec) + self.assertEqual(spec.name, name) + self.assertEqual(spec.origin, os.path.abspath(path)) + self.assertIsInstance(spec.loader, machinery.SourceFileLoader) + self.assertEqual(spec.loader.name, name) + self.assertEqual(spec.loader.path, path) + self.assertEqual(spec.loader, ns["__loader__"]) + + +FrozenFixUpModuleTests, SourceFixUpModuleTests = util.test_both(FixUpModuleTests) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index be9f29e04ae110..710b609ce550d6 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -4358,8 +4358,11 @@ def test_details(self): 'unittest', '--details') output = out.decode() # Just a quick sanity check on the output + self.assertIn(module.__spec__.name, output) self.assertIn(module.__name__, output) + self.assertIn(module.__spec__.origin, output) self.assertIn(module.__file__, output) + self.assertIn(module.__spec__.cached, output) self.assertIn(module.__cached__, output) self.assertEqual(err, b'') diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 8ab3289dd74006..cefc71cb5a7f54 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -702,7 +702,7 @@ def test_synopsis(self): def test_synopsis_sourceless(self): os = import_helper.import_fresh_module('os') expected = os.__doc__.splitlines()[0] - filename = os.__cached__ + filename = os.__spec__.cached synopsis = pydoc.synopsis(filename) self.assertEqual(synopsis, expected) diff --git a/Misc/NEWS.d/next/Library/2022-10-06-17-59-22.gh-issue-65961.SXlQnI.rst b/Misc/NEWS.d/next/Library/2022-10-06-17-59-22.gh-issue-65961.SXlQnI.rst new file mode 100644 index 00000000000000..f708a75a50450e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-06-17-59-22.gh-issue-65961.SXlQnI.rst @@ -0,0 +1,2 @@ +Do not rely solely on ``__cached__`` on modules; code will also support +``__spec__.cached``. From 0c970bdae97f29c098272170b4564b0d16a5c22c Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Fri, 7 Oct 2022 07:57:37 +0900 Subject: [PATCH 073/151] fixes gh-96078: os.sched_yield release the GIL while calling sched_yield(2). (gh-97965) --- .../2022-10-06-15-45-57.gh-issue-96078.fS-6mU.rst | 2 ++ Modules/posixmodule.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-06-15-45-57.gh-issue-96078.fS-6mU.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-06-15-45-57.gh-issue-96078.fS-6mU.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-15-45-57.gh-issue-96078.fS-6mU.rst new file mode 100644 index 00000000000000..d1f949c6e13adc --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-15-45-57.gh-issue-96078.fS-6mU.rst @@ -0,0 +1,2 @@ +:func:`os.sched_yield` now release the GIL while calling sched_yield(2). +Patch by Dong-hee Na. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cbdc259737583c..a72d57771c2294 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7075,8 +7075,13 @@ static PyObject * os_sched_yield_impl(PyObject *module) /*[clinic end generated code: output=902323500f222cac input=e54d6f98189391d4]*/ { - if (sched_yield()) + int result; + Py_BEGIN_ALLOW_THREADS + result = sched_yield(); + Py_END_ALLOW_THREADS + if (result < 0) { return posix_error(); + } Py_RETURN_NONE; } From 078e3eddac3f46469962f8205580cdc6348e631d Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Thu, 6 Oct 2022 16:07:17 -0700 Subject: [PATCH 074/151] gh-97973: Return all necessary information from the tokenizer (GH-97984) Right now, the tokenizer only returns type and two pointers to the start and end of the token. This PR modifies the tokenizer to return the type and set all of the necessary information, so that the parser does not have to this. --- ...2-10-06-20-41-29.gh-issue-97973.gB-xWi.rst | 1 + Parser/pegen.c | 54 ++--- Parser/pegen_errors.c | 5 +- Parser/tokenizer.c | 220 ++++++++++-------- Parser/tokenizer.h | 8 +- Python/Python-tokenize.c | 17 +- 6 files changed, 159 insertions(+), 146 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-06-20-41-29.gh-issue-97973.gB-xWi.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-06-20-41-29.gh-issue-97973.gB-xWi.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-20-41-29.gh-issue-97973.gB-xWi.rst new file mode 100644 index 00000000000000..a0095a61ec32b7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-20-41-29.gh-issue-97973.gB-xWi.rst @@ -0,0 +1 @@ +Modify the tokenizer to return all necessary information the parser needs to set location information in the AST nodes, so that the parser does not have to calculate those doing pointer arithmetic. diff --git a/Parser/pegen.c b/Parser/pegen.c index a5d123da51296c..1317606749b8c0 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -123,16 +123,18 @@ growable_comment_array_deallocate(growable_comment_array *arr) { } static int -_get_keyword_or_name_type(Parser *p, const char *name, int name_len) +_get_keyword_or_name_type(Parser *p, struct token *new_token) { + int name_len = new_token->end_col_offset - new_token->col_offset; assert(name_len > 0); + if (name_len >= p->n_keyword_lists || p->keywords[name_len] == NULL || p->keywords[name_len]->type == -1) { return NAME; } for (KeywordToken *k = p->keywords[name_len]; k != NULL && k->type != -1; k++) { - if (strncmp(k->str, name, name_len) == 0) { + if (strncmp(k->str, new_token->start, name_len) == 0) { return k->type; } } @@ -140,33 +142,26 @@ _get_keyword_or_name_type(Parser *p, const char *name, int name_len) } static int -initialize_token(Parser *p, Token *token, const char *start, const char *end, int token_type) { - assert(token != NULL); +initialize_token(Parser *p, Token *parser_token, struct token *new_token, int token_type) { + assert(parser_token != NULL); - token->type = (token_type == NAME) ? _get_keyword_or_name_type(p, start, (int)(end - start)) : token_type; - token->bytes = PyBytes_FromStringAndSize(start, end - start); - if (token->bytes == NULL) { + parser_token->type = (token_type == NAME) ? _get_keyword_or_name_type(p, new_token) : token_type; + parser_token->bytes = PyBytes_FromStringAndSize(new_token->start, new_token->end - new_token->start); + if (parser_token->bytes == NULL) { return -1; } - - if (_PyArena_AddPyObject(p->arena, token->bytes) < 0) { - Py_DECREF(token->bytes); + if (_PyArena_AddPyObject(p->arena, parser_token->bytes) < 0) { + Py_DECREF(parser_token->bytes); return -1; } - token->level = p->tok->level; - - const char *line_start = token_type == STRING ? p->tok->multi_line_start : p->tok->line_start; - int lineno = token_type == STRING ? p->tok->first_lineno : p->tok->lineno; - int end_lineno = p->tok->lineno; - - int col_offset = (start != NULL && start >= line_start) ? (int)(start - line_start) : -1; - int end_col_offset = (end != NULL && end >= p->tok->line_start) ? (int)(end - p->tok->line_start) : -1; - - token->lineno = lineno; - token->col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + col_offset : col_offset; - token->end_lineno = end_lineno; - token->end_col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + end_col_offset : end_col_offset; + parser_token->level = new_token->level; + parser_token->lineno = new_token->lineno; + parser_token->col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + new_token->col_offset + : new_token->col_offset; + parser_token->end_lineno = new_token->end_lineno; + parser_token->end_col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + new_token->end_col_offset + : new_token->end_col_offset; p->fill += 1; @@ -202,26 +197,25 @@ _resize_tokens_array(Parser *p) { int _PyPegen_fill_token(Parser *p) { - const char *start; - const char *end; - int type = _PyTokenizer_Get(p->tok, &start, &end); + struct token new_token; + int type = _PyTokenizer_Get(p->tok, &new_token); // Record and skip '# type: ignore' comments while (type == TYPE_IGNORE) { - Py_ssize_t len = end - start; + Py_ssize_t len = new_token.end_col_offset - new_token.col_offset; char *tag = PyMem_Malloc(len + 1); if (tag == NULL) { PyErr_NoMemory(); return -1; } - strncpy(tag, start, len); + strncpy(tag, new_token.start, len); tag[len] = '\0'; // Ownership of tag passes to the growable array if (!growable_comment_array_add(&p->type_ignore_comments, p->tok->lineno, tag)) { PyErr_NoMemory(); return -1; } - type = _PyTokenizer_Get(p->tok, &start, &end); + type = _PyTokenizer_Get(p->tok, &new_token); } // If we have reached the end and we are in single input mode we need to insert a newline and reset the parsing @@ -244,7 +238,7 @@ _PyPegen_fill_token(Parser *p) } Token *t = p->tokens[p->fill]; - return initialize_token(p, t, start, end, type); + return initialize_token(p, t, &new_token, type); } #if defined(Py_DEBUG) diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index 95bbd43dc32621..7738cbaf9ef39e 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -164,11 +164,10 @@ _PyPegen_tokenize_full_source_to_check_for_errors(Parser *p) { Py_ssize_t current_err_line = current_token->lineno; int ret = 0; + struct token new_token; for (;;) { - const char *start; - const char *end; - switch (_PyTokenizer_Get(p->tok, &start, &end)) { + switch (_PyTokenizer_Get(p->tok, &new_token)) { case ERRORTOKEN: if (p->tok->level != 0) { int error_lineno = p->tok->parenlinenostack[p->tok->level-1]; diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 3c37fd9c45a49e..c5d3e580247cc1 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -36,6 +36,8 @@ /* Don't ever change this -- it would break the portability of Python code */ #define TABSIZE 8 +#define MAKE_TOKEN(token_type) token_setup(tok, token, token_type, p_start, p_end) + /* Forward */ static struct tok_state *tok_new(void); static int tok_nextc(struct tok_state *tok); @@ -1174,8 +1176,6 @@ syntaxerror_known_range(struct tok_state *tok, return ret; } - - static int indenterror(struct tok_state *tok) { @@ -1391,12 +1391,32 @@ tok_continuation_line(struct tok_state *tok) { } static int -tok_get(struct tok_state *tok, const char **p_start, const char **p_end) +token_setup(struct tok_state *tok, struct token *token, int type, const char *start, const char *end) +{ + assert((start == NULL && end == NULL) || (start != NULL && end != NULL)); + token->level = tok->level; + token->lineno = type == STRING ? tok->first_lineno : tok->lineno; + token->end_lineno = tok->lineno; + token->col_offset = -1; + token->end_col_offset = -1; + token->start = start; + token->end = end; + if (start != NULL && end != NULL) { + const char *line_start = type == STRING ? tok->multi_line_start : tok->line_start; + token->col_offset = (start >= line_start) ? (int)(start - line_start) : -1; + token->end_col_offset = (end >= tok->line_start) ? (int)(end - tok->line_start) : -1; + } + return type; +} + +static int +tok_get(struct tok_state *tok, struct token *token) { int c; int blankline, nonascii; - *p_start = *p_end = NULL; + const char *p_start = NULL; + const char *p_end = NULL; nextline: tok->start = NULL; blankline = 0; @@ -1426,7 +1446,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) // the level of indentation of whatever comes next. cont_line_col = cont_line_col ? cont_line_col : col; if ((c = tok_continuation_line(tok)) == -1) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } else { @@ -1461,7 +1481,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (col == tok->indstack[tok->indent]) { /* No change */ if (altcol != tok->altindstack[tok->indent]) { - return indenterror(tok); + return MAKE_TOKEN(indenterror(tok)); } } else if (col > tok->indstack[tok->indent]) { @@ -1469,10 +1489,10 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (tok->indent+1 >= MAXINDENT) { tok->done = E_TOODEEP; tok->cur = tok->inp; - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } if (altcol <= tok->altindstack[tok->indent]) { - return indenterror(tok); + return MAKE_TOKEN(indenterror(tok)); } tok->pendin++; tok->indstack[++tok->indent] = col; @@ -1488,10 +1508,10 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (col != tok->indstack[tok->indent]) { tok->done = E_DEDENT; tok->cur = tok->inp; - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } if (altcol != tok->altindstack[tok->indent]) { - return indenterror(tok); + return MAKE_TOKEN(indenterror(tok)); } } } @@ -1503,11 +1523,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (tok->pendin != 0) { if (tok->pendin < 0) { tok->pendin++; - return DEDENT; + return MAKE_TOKEN(DEDENT); } else { tok->pendin--; - return INDENT; + return MAKE_TOKEN(INDENT); } } @@ -1587,34 +1607,34 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) && ((unsigned char)ignore_end[0] >= 128 || Py_ISALNUM(ignore_end[0])))); if (is_type_ignore) { - *p_start = ignore_end; - *p_end = tok->cur; + p_start = ignore_end; + p_end = tok->cur; /* If this type ignore is the only thing on the line, consume the newline also. */ if (blankline) { tok_nextc(tok); tok->atbol = 1; } - return TYPE_IGNORE; + return MAKE_TOKEN(TYPE_IGNORE); } else { - *p_start = type_start; /* after type_comment_prefix */ - *p_end = tok->cur; - return TYPE_COMMENT; + p_start = type_start; + p_end = tok->cur; + return MAKE_TOKEN(TYPE_COMMENT); } } } } if (tok->done == E_INTERACT_STOP) { - return ENDMARKER; + return MAKE_TOKEN(ENDMARKER); } /* Check for EOF and errors now */ if (c == EOF) { if (tok->level) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } - return tok->done == E_EOF ? ENDMARKER : ERRORTOKEN; + return MAKE_TOKEN(tok->done == E_EOF ? ENDMARKER : ERRORTOKEN); } /* Identifier (most frequent token!) */ @@ -1654,11 +1674,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } tok_backup(tok, c); if (nonascii && !verify_identifier(tok)) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } - *p_start = tok->start; - *p_end = tok->cur; + p_start = tok->start; + p_end = tok->cur; /* async/await parsing block. */ if (tok->cur - tok->start == 5 && tok->start[0] == 'a') { @@ -1673,10 +1693,10 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (!tok->async_hacks || tok->async_def) { /* Always recognize the keywords. */ if (memcmp(tok->start, "async", 5) == 0) { - return ASYNC; + return MAKE_TOKEN(ASYNC); } if (memcmp(tok->start, "await", 5) == 0) { - return AWAIT; + return MAKE_TOKEN(AWAIT); } } else if (memcmp(tok->start, "async", 5) == 0) { @@ -1684,13 +1704,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) Look ahead one token to see if that is 'def'. */ struct tok_state ahead_tok; - const char *ahead_tok_start = NULL; - const char *ahead_tok_end = NULL; + struct token ahead_token; int ahead_tok_kind; memcpy(&ahead_tok, tok, sizeof(ahead_tok)); - ahead_tok_kind = tok_get(&ahead_tok, &ahead_tok_start, - &ahead_tok_end); + ahead_tok_kind = tok_get(&ahead_tok, &ahead_token); if (ahead_tok_kind == NAME && ahead_tok.cur - ahead_tok.start == 3 @@ -1700,12 +1718,12 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) returning a plain NAME token, return ASYNC. */ tok->async_def_indent = tok->indent; tok->async_def = 1; - return ASYNC; + return MAKE_TOKEN(ASYNC); } } } - return NAME; + return MAKE_TOKEN(NAME); } /* Newline */ @@ -1714,15 +1732,15 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (blankline || tok->level > 0) { goto nextline; } - *p_start = tok->start; - *p_end = tok->cur - 1; /* Leave '\n' out of the string */ + p_start = tok->start; + p_end = tok->cur - 1; /* Leave '\n' out of the string */ tok->cont_line = 0; if (tok->async_def) { /* We're somewhere inside an 'async def' function, and we've encountered a NEWLINE after its signature. */ tok->async_def_nl = 1; } - return NEWLINE; + return MAKE_TOKEN(NEWLINE); } /* Period or number starting with period? */ @@ -1733,9 +1751,9 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } else if (c == '.') { c = tok_nextc(tok); if (c == '.') { - *p_start = tok->start; - *p_end = tok->cur; - return ELLIPSIS; + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(ELLIPSIS); } else { tok_backup(tok, c); @@ -1745,9 +1763,9 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) else { tok_backup(tok, c); } - *p_start = tok->start; - *p_end = tok->cur; - return DOT; + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(DOT); } /* Number */ @@ -1764,14 +1782,14 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } if (!isxdigit(c)) { tok_backup(tok, c); - return syntaxerror(tok, "invalid hexadecimal literal"); + return MAKE_TOKEN(syntaxerror(tok, "invalid hexadecimal literal")); } do { c = tok_nextc(tok); } while (isxdigit(c)); } while (c == '_'); if (!verify_end_of_number(tok, c, "hexadecimal")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } else if (c == 'o' || c == 'O') { @@ -1783,12 +1801,12 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } if (c < '0' || c >= '8') { if (isdigit(c)) { - return syntaxerror(tok, - "invalid digit '%c' in octal literal", c); + return MAKE_TOKEN(syntaxerror(tok, + "invalid digit '%c' in octal literal", c)); } else { tok_backup(tok, c); - return syntaxerror(tok, "invalid octal literal"); + return MAKE_TOKEN(syntaxerror(tok, "invalid octal literal")); } } do { @@ -1796,11 +1814,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } while ('0' <= c && c < '8'); } while (c == '_'); if (isdigit(c)) { - return syntaxerror(tok, - "invalid digit '%c' in octal literal", c); + return MAKE_TOKEN(syntaxerror(tok, + "invalid digit '%c' in octal literal", c)); } if (!verify_end_of_number(tok, c, "octal")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } else if (c == 'b' || c == 'B') { @@ -1812,12 +1830,11 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } if (c != '0' && c != '1') { if (isdigit(c)) { - return syntaxerror(tok, - "invalid digit '%c' in binary literal", c); + return MAKE_TOKEN(syntaxerror(tok, "invalid digit '%c' in binary literal", c)); } else { tok_backup(tok, c); - return syntaxerror(tok, "invalid binary literal"); + return MAKE_TOKEN(syntaxerror(tok, "invalid binary literal")); } } do { @@ -1825,11 +1842,10 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } while (c == '0' || c == '1'); } while (c == '_'); if (isdigit(c)) { - return syntaxerror(tok, - "invalid digit '%c' in binary literal", c); + return MAKE_TOKEN(syntaxerror(tok, "invalid digit '%c' in binary literal", c)); } if (!verify_end_of_number(tok, c, "binary")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } else { @@ -1841,7 +1857,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) c = tok_nextc(tok); if (!isdigit(c)) { tok_backup(tok, c); - return syntaxerror(tok, "invalid decimal literal"); + return MAKE_TOKEN(syntaxerror(tok, "invalid decimal literal")); } } if (c != '0') { @@ -1854,7 +1870,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) nonzero = 1; c = tok_decimal_tail(tok); if (c == 0) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } if (c == '.') { @@ -1870,15 +1886,15 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) else if (nonzero) { /* Old-style octal: now disallowed. */ tok_backup(tok, c); - return syntaxerror_known_range( + return MAKE_TOKEN(syntaxerror_known_range( tok, (int)(tok->start + 1 - tok->line_start), (int)(zeros_end - tok->line_start), "leading zeros in decimal integer " "literals are not permitted; " - "use an 0o prefix for octal integers"); + "use an 0o prefix for octal integers")); } if (!verify_end_of_number(tok, c, "decimal")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } } @@ -1886,7 +1902,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) /* Decimal */ c = tok_decimal_tail(tok); if (c == 0) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } { /* Accept floating point numbers. */ @@ -1897,7 +1913,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (isdigit(c)) { c = tok_decimal_tail(tok); if (c == 0) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } } @@ -1911,21 +1927,21 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) c = tok_nextc(tok); if (!isdigit(c)) { tok_backup(tok, c); - return syntaxerror(tok, "invalid decimal literal"); + return MAKE_TOKEN(syntaxerror(tok, "invalid decimal literal")); } } else if (!isdigit(c)) { tok_backup(tok, c); if (!verify_end_of_number(tok, e, "decimal")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } tok_backup(tok, e); - *p_start = tok->start; - *p_end = tok->cur; - return NUMBER; + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(NUMBER); } c = tok_decimal_tail(tok); if (c == 0) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } if (c == 'j' || c == 'J') { @@ -1933,18 +1949,18 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) imaginary: c = tok_nextc(tok); if (!verify_end_of_number(tok, c, "imaginary")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } else if (!verify_end_of_number(tok, c, "decimal")) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } } tok_backup(tok, c); - *p_start = tok->start; - *p_end = tok->cur; - return NUMBER; + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(NUMBER); } letter_quote: @@ -1997,7 +2013,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (c != '\n') { tok->done = E_EOFS; } - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } else { syntaxerror(tok, "unterminated string literal (detected at" @@ -2005,7 +2021,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (c != '\n') { tok->done = E_EOLS; } - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } } if (c == quote) { @@ -2019,15 +2035,15 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) } } - *p_start = tok->start; - *p_end = tok->cur; - return STRING; + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(STRING); } /* Line continuation */ if (c == '\\') { if ((c = tok_continuation_line(tok)) == -1) { - return ERRORTOKEN; + return MAKE_TOKEN(ERRORTOKEN); } tok->cont_line = 1; goto again; /* Read next line */ @@ -2036,19 +2052,19 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) /* Check for two-character token */ { int c2 = tok_nextc(tok); - int token = _PyToken_TwoChars(c, c2); - if (token != OP) { + int current_token = _PyToken_TwoChars(c, c2); + if (current_token != OP) { int c3 = tok_nextc(tok); - int token3 = _PyToken_ThreeChars(c, c2, c3); - if (token3 != OP) { - token = token3; + int current_token3 = _PyToken_ThreeChars(c, c2, c3); + if (current_token3 != OP) { + current_token = current_token3; } else { tok_backup(tok, c3); } - *p_start = tok->start; - *p_end = tok->cur; - return token; + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(current_token); } tok_backup(tok, c2); } @@ -2059,7 +2075,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) case '[': case '{': if (tok->level >= MAXLEVEL) { - return syntaxerror(tok, "too many nested parentheses"); + return MAKE_TOKEN(syntaxerror(tok, "too many nested parentheses")); } tok->parenstack[tok->level] = c; tok->parenlinenostack[tok->level] = tok->lineno; @@ -2070,7 +2086,7 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) case ']': case '}': if (!tok->level) { - return syntaxerror(tok, "unmatched '%c'", c); + return MAKE_TOKEN(syntaxerror(tok, "unmatched '%c'", c)); } tok->level--; int opening = tok->parenstack[tok->level]; @@ -2079,16 +2095,16 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) (opening == '{' && c == '}'))) { if (tok->parenlinenostack[tok->level] != tok->lineno) { - return syntaxerror(tok, + return MAKE_TOKEN(syntaxerror(tok, "closing parenthesis '%c' does not match " "opening parenthesis '%c' on line %d", - c, opening, tok->parenlinenostack[tok->level]); + c, opening, tok->parenlinenostack[tok->level])); } else { - return syntaxerror(tok, + return MAKE_TOKEN(syntaxerror(tok, "closing parenthesis '%c' does not match " "opening parenthesis '%c'", - c, opening); + c, opening)); } } break; @@ -2097,20 +2113,19 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end) if (!Py_UNICODE_ISPRINTABLE(c)) { char hex[9]; (void)PyOS_snprintf(hex, sizeof(hex), "%04X", c); - return syntaxerror(tok, "invalid non-printable character U+%s", hex); + return MAKE_TOKEN(syntaxerror(tok, "invalid non-printable character U+%s", hex)); } /* Punctuation character */ - *p_start = tok->start; - *p_end = tok->cur; - return _PyToken_OneChar(c); + p_start = tok->start; + p_end = tok->cur; + return MAKE_TOKEN(_PyToken_OneChar(c)); } int -_PyTokenizer_Get(struct tok_state *tok, - const char **p_start, const char **p_end) +_PyTokenizer_Get(struct tok_state *tok, struct token *token) { - int result = tok_get(tok, p_start, p_end); + int result = tok_get(tok, token); if (tok->decoding_erred) { result = ERRORTOKEN; tok->done = E_DECODE; @@ -2166,8 +2181,6 @@ _PyTokenizer_FindEncodingFilename(int fd, PyObject *filename) { struct tok_state *tok; FILE *fp; - const char *p_start = NULL; - const char *p_end = NULL; char *encoding = NULL; fp = fdopen_borrow(fd); @@ -2191,8 +2204,9 @@ _PyTokenizer_FindEncodingFilename(int fd, PyObject *filename) return encoding; } } + struct token token; while (tok->lineno < 2 && tok->done == E_OK) { - _PyTokenizer_Get(tok, &p_start, &p_end); + _PyTokenizer_Get(tok, &token); } fclose(fp); if (tok->encoding) { diff --git a/Parser/tokenizer.h b/Parser/tokenizer.h index 5ac64a99b7d661..5b8c7f314386ec 100644 --- a/Parser/tokenizer.h +++ b/Parser/tokenizer.h @@ -27,6 +27,12 @@ enum interactive_underflow_t { IUNDERFLOW_STOP, }; +struct token { + int level; + int lineno, col_offset, end_lineno, end_col_offset; + const char *start, *end; +}; + /* Tokenizer state */ struct tok_state { /* Input state; buf <= cur <= inp <= end */ @@ -94,7 +100,7 @@ extern struct tok_state *_PyTokenizer_FromUTF8(const char *, int); extern struct tok_state *_PyTokenizer_FromFile(FILE *, const char*, const char *, const char *); extern void _PyTokenizer_Free(struct tok_state *); -extern int _PyTokenizer_Get(struct tok_state *, const char **, const char **); +extern int _PyTokenizer_Get(struct tok_state *, struct token *); #define tok_dump _Py_tok_dump diff --git a/Python/Python-tokenize.c b/Python/Python-tokenize.c index c5124a6942e7f2..8daa9877254e2e 100644 --- a/Python/Python-tokenize.c +++ b/Python/Python-tokenize.c @@ -60,9 +60,8 @@ tokenizeriter_new_impl(PyTypeObject *type, const char *source) static PyObject * tokenizeriter_next(tokenizeriterobject *it) { - const char *start; - const char *end; - int type = _PyTokenizer_Get(it->tok, &start, &end); + struct token token; + int type = _PyTokenizer_Get(it->tok, &token); if (type == ERRORTOKEN && PyErr_Occurred()) { return NULL; } @@ -71,11 +70,11 @@ tokenizeriter_next(tokenizeriterobject *it) return NULL; } PyObject *str = NULL; - if (start == NULL || end == NULL) { + if (token.start == NULL || token.end == NULL) { str = PyUnicode_FromString(""); } else { - str = PyUnicode_FromStringAndSize(start, end - start); + str = PyUnicode_FromStringAndSize(token.start, token.end - token.start); } if (str == NULL) { return NULL; @@ -92,11 +91,11 @@ tokenizeriter_next(tokenizeriterobject *it) int end_lineno = it->tok->lineno; int col_offset = -1; int end_col_offset = -1; - if (start != NULL && start >= line_start) { - col_offset = (int)(start - line_start); + if (token.start != NULL && token.start >= line_start) { + col_offset = (int)(token.start - line_start); } - if (end != NULL && end >= it->tok->line_start) { - end_col_offset = (int)(end - it->tok->line_start); + if (token.end != NULL && token.end >= it->tok->line_start) { + end_col_offset = (int)(token.end - it->tok->line_start); } return Py_BuildValue("(NiiiiiN)", str, type, lineno, end_lineno, col_offset, end_col_offset, line); From cea36970acc5aee83ed91d180f81cb91c84c26d0 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Thu, 6 Oct 2022 16:20:01 -0700 Subject: [PATCH 075/151] GH-97002: Prevent `_PyInterpreterFrame`s from backing more than one `PyFrameObject` (GH-97996) --- Lib/test/test_frame.py | 65 +++++++++++++++++++ ...2-10-06-02-11-34.gh-issue-97002.Zvsk71.rst | 3 + Python/frame.c | 29 +++++++-- 3 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-06-02-11-34.gh-issue-97002.Zvsk71.rst diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 5dda2fbfac374c..4b86a60d2f4c36 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -1,3 +1,4 @@ +import gc import re import sys import textwrap @@ -261,5 +262,69 @@ def gen(): """) assert_python_ok("-c", code) + @support.cpython_only + def test_sneaky_frame_object(self): + + def trace(frame, event, arg): + """ + Don't actually do anything, just force a frame object to be created. + """ + + def callback(phase, info): + """ + Yo dawg, I heard you like frames, so I'm allocating a frame while + you're allocating a frame, so you can have a frame while you have a + frame! + """ + nonlocal sneaky_frame_object + sneaky_frame_object = sys._getframe().f_back + # We're done here: + gc.callbacks.remove(callback) + + def f(): + while True: + yield + + old_threshold = gc.get_threshold() + old_callbacks = gc.callbacks[:] + old_enabled = gc.isenabled() + old_trace = sys.gettrace() + try: + # Stop the GC for a second while we set things up: + gc.disable() + # Create a paused generator: + g = f() + next(g) + # Move all objects to the oldest generation, and tell the GC to run + # on the *very next* allocation: + gc.collect() + gc.set_threshold(1, 0, 0) + # Okay, so here's the nightmare scenario: + # - We're tracing the resumption of a generator, which creates a new + # frame object. + # - The allocation of this frame object triggers a collection + # *before* the frame object is actually created. + # - During the collection, we request the exact same frame object. + # This test does it with a GC callback, but in real code it would + # likely be a trace function, weakref callback, or finalizer. + # - The collection finishes, and the original frame object is + # created. We now have two frame objects fighting over ownership + # of the same interpreter frame! + sys.settrace(trace) + gc.callbacks.append(callback) + sneaky_frame_object = None + gc.enable() + next(g) + # g.gi_frame should be the the frame object from the callback (the + # one that was *requested* second, but *created* first): + self.assertIs(g.gi_frame, sneaky_frame_object) + finally: + gc.set_threshold(*old_threshold) + gc.callbacks[:] = old_callbacks + sys.settrace(old_trace) + if old_enabled: + gc.enable() + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-06-02-11-34.gh-issue-97002.Zvsk71.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-02-11-34.gh-issue-97002.Zvsk71.rst new file mode 100644 index 00000000000000..1f577e02e1fd8a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-02-11-34.gh-issue-97002.Zvsk71.rst @@ -0,0 +1,3 @@ +Fix an issue where several frame objects could be backed by the same +interpreter frame, possibly leading to corrupted memory and hard crashes of +the interpreter. diff --git a/Python/frame.c b/Python/frame.c index 96566de63a78fd..89f084b110cb5a 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -37,14 +37,31 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame) Py_XDECREF(error_type); Py_XDECREF(error_value); Py_XDECREF(error_traceback); + return NULL; } - else { - assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); - assert(frame->owner != FRAME_CLEARED); - f->f_frame = frame; - frame->frame_obj = f; - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_Restore(error_type, error_value, error_traceback); + if (frame->frame_obj) { + // GH-97002: How did we get into this horrible situation? Most likely, + // allocating f triggered a GC collection, which ran some code that + // *also* created the same frame... while we were in the middle of + // creating it! See test_sneaky_frame_object in test_frame.py for a + // concrete example. + // + // Regardless, just throw f away and use that frame instead, since it's + // already been exposed to user code. It's actually a bit tricky to do + // this, since we aren't backed by a real _PyInterpreterFrame anymore. + // Just pretend that we have an owned, cleared frame so frame_dealloc + // doesn't make the situation worse: + f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data; + f->f_frame->owner = FRAME_CLEARED; + f->f_frame->frame_obj = f; + Py_DECREF(f); + return frame->frame_obj; } + assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); + assert(frame->owner != FRAME_CLEARED); + f->f_frame = frame; + frame->frame_obj = f; return f; } From f8e002a698a470cc19e9b941cc94d3207c834694 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 6 Oct 2022 16:43:16 -0700 Subject: [PATCH 076/151] bpo-38693: Use f-strings instead of str.format() within importlib (#17058) This is a small performance improvement, especially for one or two hot places such as _handle_fromlist() that are called a lot and the .format() method was being used just to join two strings with a dot. Otherwise it is merely a readability improvement. We keep `_ERR_MSG` and `_ERR_MSG_PREFIX` as those may be used elsewhere for canonical looking error messages. --- Lib/importlib/_bootstrap.py | 49 +- .../2019-11-04-22-21-27.bpo-38693.w_OAov.rst | 1 + Python/importlib.h | 1783 +++++++++++++++++ 3 files changed, 1807 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst create mode 100644 Python/importlib.h diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 1c132106ce5a8f..e0110e701d6cde 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -136,7 +136,7 @@ def release(self): self.wakeup.release() def __repr__(self): - return '_ModuleLock({!r}) at {}'.format(self.name, id(self)) + return f'_ModuleLock({self.name!r}) at {id(self)}' class _DummyModuleLock: @@ -157,7 +157,7 @@ def release(self): self.count -= 1 def __repr__(self): - return '_DummyModuleLock({!r}) at {}'.format(self.name, id(self)) + return f'_DummyModuleLock({self.name!r}) at {id(self)}' class _ModuleLockManager: @@ -253,7 +253,7 @@ def _requires_builtin(fxn): """Decorator to verify the named module is built-in.""" def _requires_builtin_wrapper(self, fullname): if fullname not in sys.builtin_module_names: - raise ImportError('{!r} is not a built-in module'.format(fullname), + raise ImportError(f'{fullname!r} is not a built-in module', name=fullname) return fxn(self, fullname) _wrap(_requires_builtin_wrapper, fxn) @@ -264,7 +264,7 @@ def _requires_frozen(fxn): """Decorator to verify the named module is frozen.""" def _requires_frozen_wrapper(self, fullname): if not _imp.is_frozen(fullname): - raise ImportError('{!r} is not a frozen module'.format(fullname), + raise ImportError(f'{fullname!r} is not a frozen module', name=fullname) return fxn(self, fullname) _wrap(_requires_frozen_wrapper, fxn) @@ -305,11 +305,11 @@ def _module_repr(module): filename = module.__file__ except AttributeError: if loader is None: - return ''.format(name) + return f'' else: - return ''.format(name, loader) + return f'' else: - return ''.format(name, filename) + return f'' class ModuleSpec: @@ -363,14 +363,12 @@ def __init__(self, name, loader, *, origin=None, loader_state=None, self._cached = None def __repr__(self): - args = ['name={!r}'.format(self.name), - 'loader={!r}'.format(self.loader)] + args = [f'name={self.name!r}', f'loader={self.loader!r}'] if self.origin is not None: - args.append('origin={!r}'.format(self.origin)) + args.append(f'origin={self.origin!r}') if self.submodule_search_locations is not None: - args.append('submodule_search_locations={}' - .format(self.submodule_search_locations)) - return '{}({})'.format(self.__class__.__name__, ', '.join(args)) + args.append(f'submodule_search_locations={self.submodule_search_locations}') + return f'{self.__class__.__name__}({", ".join(args)})' def __eq__(self, other): smsl = self.submodule_search_locations @@ -580,14 +578,14 @@ def _module_repr_from_spec(spec): name = '?' if spec.name is None else spec.name if spec.origin is None: if spec.loader is None: - return ''.format(name) + return f'' else: - return ''.format(name, spec.loader) + return f'' else: if spec.has_location: - return ''.format(name, spec.origin) + return f'' else: - return ''.format(spec.name, spec.origin) + return f'' # Used by importlib.reload() and _load_module_shim(). @@ -596,7 +594,7 @@ def _exec(spec, module): name = spec.name with _ModuleLockManager(name): if sys.modules.get(name) is not module: - msg = 'module {!r} not in sys.modules'.format(name) + msg = f'module {name!r} not in sys.modules' raise ImportError(msg, name=name) try: if spec.loader is None: @@ -756,7 +754,7 @@ def find_module(cls, fullname, path=None): def create_module(spec): """Create a built-in module""" if spec.name not in sys.builtin_module_names: - raise ImportError('{!r} is not a built-in module'.format(spec.name), + raise ImportError(f'{spec.name!r} is not a built-in module', name=spec.name) return _call_with_frames_removed(_imp.create_builtin, spec) @@ -1012,7 +1010,7 @@ def _resolve_name(name, package, level): if len(bits) < level: raise ImportError('attempted relative import beyond top-level package') base = bits[0] - return '{}.{}'.format(base, name) if name else base + return f'{base}.{name}' if name else base def _find_spec_legacy(finder, name, path): @@ -1075,7 +1073,7 @@ def _find_spec(name, path, target=None): def _sanity_check(name, package, level): """Verify arguments are "sane".""" if not isinstance(name, str): - raise TypeError('module name must be str, not {}'.format(type(name))) + raise TypeError(f'module name must be str, not {type(name)}') if level < 0: raise ValueError('level must be >= 0') if level > 0: @@ -1105,13 +1103,13 @@ def _find_and_load_unlocked(name, import_): try: path = parent_module.__path__ except AttributeError: - msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent) + msg = f'{_ERR_MSG_PREFIX} {name!r}; {parent!r} is not a package' raise ModuleNotFoundError(msg, name=name) from None parent_spec = parent_module.__spec__ child = name.rpartition('.')[2] spec = _find_spec(name, path) if spec is None: - raise ModuleNotFoundError(_ERR_MSG.format(name), name=name) + raise ModuleNotFoundError(f'{_ERR_MSG_PREFIX}{name!r}', name=name) else: if parent_spec: # Temporarily add child we are currently importing to parent's @@ -1156,8 +1154,7 @@ def _find_and_load(name, import_): _lock_unlock_module(name) if module is None: - message = ('import of {} halted; ' - 'None in sys.modules'.format(name)) + message = f'import of {name} halted; None in sys.modules' raise ModuleNotFoundError(message, name=name) return module @@ -1201,7 +1198,7 @@ def _handle_fromlist(module, fromlist, import_, *, recursive=False): _handle_fromlist(module, module.__all__, import_, recursive=True) elif not hasattr(module, x): - from_name = '{}.{}'.format(module.__name__, x) + from_name = f'{module.__name__}.{x}' try: _call_with_frames_removed(import_, from_name) except ModuleNotFoundError as exc: diff --git a/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst b/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst new file mode 100644 index 00000000000000..6d0ad36809ee77 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst @@ -0,0 +1 @@ +importlib now uses f-strings internally instead of str.format(). diff --git a/Python/importlib.h b/Python/importlib.h new file mode 100644 index 00000000000000..586f3b21f46246 --- /dev/null +++ b/Python/importlib.h @@ -0,0 +1,1783 @@ +/* Auto-generated by Programs/_freeze_importlib.c */ +const unsigned char _Py_M__importlib_bootstrap[] = { + 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,0,0,0,64,0,0,0,115,194,1,0,0,100,0, + 90,0,100,1,97,1,100,2,100,3,132,0,90,2,100,4, + 100,5,132,0,90,3,105,0,90,4,105,0,90,5,71,0, + 100,6,100,7,132,0,100,7,101,6,131,3,90,7,71,0, + 100,8,100,9,132,0,100,9,131,2,90,8,71,0,100,10, + 100,11,132,0,100,11,131,2,90,9,71,0,100,12,100,13, + 132,0,100,13,131,2,90,10,100,14,100,15,132,0,90,11, + 100,16,100,17,132,0,90,12,100,18,100,19,132,0,90,13, + 100,20,100,21,156,1,100,22,100,23,132,2,90,14,100,24, + 100,25,132,0,90,15,100,26,100,27,132,0,90,16,100,28, + 100,29,132,0,90,17,100,30,100,31,132,0,90,18,71,0, + 100,32,100,33,132,0,100,33,131,2,90,19,100,1,100,1, + 100,34,156,2,100,35,100,36,132,2,90,20,100,94,100,37, + 100,38,132,1,90,21,100,39,100,40,156,1,100,41,100,42, + 132,2,90,22,100,43,100,44,132,0,90,23,100,45,100,46, + 132,0,90,24,100,47,100,48,132,0,90,25,100,49,100,50, + 132,0,90,26,100,51,100,52,132,0,90,27,100,53,100,54, + 132,0,90,28,71,0,100,55,100,56,132,0,100,56,131,2, + 90,29,71,0,100,57,100,58,132,0,100,58,131,2,90,30, + 71,0,100,59,100,60,132,0,100,60,131,2,90,31,100,61, + 100,62,132,0,90,32,100,63,100,64,132,0,90,33,100,95, + 100,65,100,66,132,1,90,34,100,67,100,68,132,0,90,35, + 100,69,90,36,101,36,100,70,23,0,90,37,100,71,100,72, + 132,0,90,38,101,39,131,0,90,40,100,73,100,74,132,0, + 90,41,100,96,100,76,100,77,132,1,90,42,100,39,100,78, + 156,1,100,79,100,80,132,2,90,43,100,81,100,82,132,0, + 90,44,100,97,100,84,100,85,132,1,90,45,100,86,100,87, + 132,0,90,46,100,88,100,89,132,0,90,47,100,90,100,91, + 132,0,90,48,100,92,100,93,132,0,90,49,100,1,83,0, + 41,98,97,83,1,0,0,67,111,114,101,32,105,109,112,108, + 101,109,101,110,116,97,116,105,111,110,32,111,102,32,105,109, + 112,111,114,116,46,10,10,84,104,105,115,32,109,111,100,117, + 108,101,32,105,115,32,78,79,84,32,109,101,97,110,116,32, + 116,111,32,98,101,32,100,105,114,101,99,116,108,121,32,105, + 109,112,111,114,116,101,100,33,32,73,116,32,104,97,115,32, + 98,101,101,110,32,100,101,115,105,103,110,101,100,32,115,117, + 99,104,10,116,104,97,116,32,105,116,32,99,97,110,32,98, + 101,32,98,111,111,116,115,116,114,97,112,112,101,100,32,105, + 110,116,111,32,80,121,116,104,111,110,32,97,115,32,116,104, + 101,32,105,109,112,108,101,109,101,110,116,97,116,105,111,110, + 32,111,102,32,105,109,112,111,114,116,46,32,65,115,10,115, + 117,99,104,32,105,116,32,114,101,113,117,105,114,101,115,32, + 116,104,101,32,105,110,106,101,99,116,105,111,110,32,111,102, + 32,115,112,101,99,105,102,105,99,32,109,111,100,117,108,101, + 115,32,97,110,100,32,97,116,116,114,105,98,117,116,101,115, + 32,105,110,32,111,114,100,101,114,32,116,111,10,119,111,114, + 107,46,32,79,110,101,32,115,104,111,117,108,100,32,117,115, + 101,32,105,109,112,111,114,116,108,105,98,32,97,115,32,116, + 104,101,32,112,117,98,108,105,99,45,102,97,99,105,110,103, + 32,118,101,114,115,105,111,110,32,111,102,32,116,104,105,115, + 32,109,111,100,117,108,101,46,10,10,78,99,2,0,0,0, + 0,0,0,0,0,0,0,0,3,0,0,0,7,0,0,0, + 67,0,0,0,115,56,0,0,0,100,1,68,0,93,32,125, + 2,116,0,124,1,124,2,131,2,114,4,116,1,124,0,124, + 2,116,2,124,1,124,2,131,2,131,3,1,0,113,4,124, + 0,106,3,160,4,124,1,106,3,161,1,1,0,100,2,83, + 0,41,3,122,47,83,105,109,112,108,101,32,115,117,98,115, + 116,105,116,117,116,101,32,102,111,114,32,102,117,110,99,116, + 111,111,108,115,46,117,112,100,97,116,101,95,119,114,97,112, + 112,101,114,46,41,4,218,10,95,95,109,111,100,117,108,101, + 95,95,218,8,95,95,110,97,109,101,95,95,218,12,95,95, + 113,117,97,108,110,97,109,101,95,95,218,7,95,95,100,111, + 99,95,95,78,41,5,218,7,104,97,115,97,116,116,114,218, + 7,115,101,116,97,116,116,114,218,7,103,101,116,97,116,116, + 114,218,8,95,95,100,105,99,116,95,95,218,6,117,112,100, + 97,116,101,41,3,90,3,110,101,119,90,3,111,108,100,218, + 7,114,101,112,108,97,99,101,169,0,114,10,0,0,0,250, + 29,60,102,114,111,122,101,110,32,105,109,112,111,114,116,108, + 105,98,46,95,98,111,111,116,115,116,114,97,112,62,218,5, + 95,119,114,97,112,27,0,0,0,115,8,0,0,0,0,2, + 8,1,10,1,20,1,114,12,0,0,0,99,1,0,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0, + 67,0,0,0,115,12,0,0,0,116,0,116,1,131,1,124, + 0,131,1,83,0,169,1,78,41,2,218,4,116,121,112,101, + 218,3,115,121,115,169,1,218,4,110,97,109,101,114,10,0, + 0,0,114,10,0,0,0,114,11,0,0,0,218,11,95,110, + 101,119,95,109,111,100,117,108,101,35,0,0,0,115,2,0, + 0,0,0,1,114,18,0,0,0,99,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,1,0,0,0,64,0, + 0,0,115,12,0,0,0,101,0,90,1,100,0,90,2,100, + 1,83,0,41,2,218,14,95,68,101,97,100,108,111,99,107, + 69,114,114,111,114,78,41,3,114,1,0,0,0,114,0,0, + 0,0,114,2,0,0,0,114,10,0,0,0,114,10,0,0, + 0,114,10,0,0,0,114,11,0,0,0,114,19,0,0,0, + 48,0,0,0,115,2,0,0,0,8,1,114,19,0,0,0, + 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,0,0,0,64,0,0,0,115,56,0,0,0,101,0, + 90,1,100,0,90,2,100,1,90,3,100,2,100,3,132,0, + 90,4,100,4,100,5,132,0,90,5,100,6,100,7,132,0, + 90,6,100,8,100,9,132,0,90,7,100,10,100,11,132,0, + 90,8,100,12,83,0,41,13,218,11,95,77,111,100,117,108, + 101,76,111,99,107,122,169,65,32,114,101,99,117,114,115,105, + 118,101,32,108,111,99,107,32,105,109,112,108,101,109,101,110, + 116,97,116,105,111,110,32,119,104,105,99,104,32,105,115,32, + 97,98,108,101,32,116,111,32,100,101,116,101,99,116,32,100, + 101,97,100,108,111,99,107,115,10,32,32,32,32,40,101,46, + 103,46,32,116,104,114,101,97,100,32,49,32,116,114,121,105, + 110,103,32,116,111,32,116,97,107,101,32,108,111,99,107,115, + 32,65,32,116,104,101,110,32,66,44,32,97,110,100,32,116, + 104,114,101,97,100,32,50,32,116,114,121,105,110,103,32,116, + 111,10,32,32,32,32,116,97,107,101,32,108,111,99,107,115, + 32,66,32,116,104,101,110,32,65,41,46,10,32,32,32,32, + 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,2,0,0,0,67,0,0,0,115,48,0,0,0,116,0, + 160,1,161,0,124,0,95,2,116,0,160,1,161,0,124,0, + 95,3,124,1,124,0,95,4,100,0,124,0,95,5,100,1, + 124,0,95,6,100,1,124,0,95,7,100,0,83,0,169,2, + 78,233,0,0,0,0,41,8,218,7,95,116,104,114,101,97, + 100,90,13,97,108,108,111,99,97,116,101,95,108,111,99,107, + 218,4,108,111,99,107,218,6,119,97,107,101,117,112,114,17, + 0,0,0,218,5,111,119,110,101,114,218,5,99,111,117,110, + 116,218,7,119,97,105,116,101,114,115,169,2,218,4,115,101, + 108,102,114,17,0,0,0,114,10,0,0,0,114,10,0,0, + 0,114,11,0,0,0,218,8,95,95,105,110,105,116,95,95, + 58,0,0,0,115,12,0,0,0,0,1,10,1,10,1,6, + 1,6,1,6,1,122,20,95,77,111,100,117,108,101,76,111, + 99,107,46,95,95,105,110,105,116,95,95,99,1,0,0,0, + 0,0,0,0,0,0,0,0,4,0,0,0,3,0,0,0, + 67,0,0,0,115,60,0,0,0,116,0,160,1,161,0,125, + 1,124,0,106,2,125,2,116,3,160,4,124,2,161,1,125, + 3,124,3,100,0,107,8,114,36,100,1,83,0,124,3,106, + 2,125,2,124,2,124,1,107,2,114,14,100,2,83,0,113, + 14,100,0,83,0,41,3,78,70,84,41,5,114,23,0,0, + 0,218,9,103,101,116,95,105,100,101,110,116,114,26,0,0, + 0,218,12,95,98,108,111,99,107,105,110,103,95,111,110,218, + 3,103,101,116,41,4,114,30,0,0,0,90,2,109,101,218, + 3,116,105,100,114,24,0,0,0,114,10,0,0,0,114,10, + 0,0,0,114,11,0,0,0,218,12,104,97,115,95,100,101, + 97,100,108,111,99,107,66,0,0,0,115,16,0,0,0,0, + 2,8,1,6,2,10,1,8,1,4,1,6,1,8,1,122, + 24,95,77,111,100,117,108,101,76,111,99,107,46,104,97,115, + 95,100,101,97,100,108,111,99,107,99,1,0,0,0,0,0, + 0,0,0,0,0,0,2,0,0,0,9,0,0,0,67,0, + 0,0,115,178,0,0,0,116,0,160,1,161,0,125,1,124, + 0,116,2,124,1,60,0,122,148,124,0,106,3,143,110,1, + 0,124,0,106,4,100,1,107,2,115,46,124,0,106,5,124, + 1,107,2,114,84,124,1,124,0,95,5,124,0,4,0,106, + 4,100,2,55,0,2,0,95,4,87,0,53,0,81,0,82, + 0,163,0,87,0,162,86,100,3,83,0,124,0,160,6,161, + 0,114,104,116,7,100,4,124,0,22,0,131,1,130,1,124, + 0,106,8,160,9,100,5,161,1,114,130,124,0,4,0,106, + 10,100,2,55,0,2,0,95,10,87,0,53,0,81,0,82, + 0,88,0,124,0,106,8,160,9,161,0,1,0,124,0,106, + 8,160,11,161,0,1,0,113,18,87,0,53,0,116,2,124, + 1,61,0,88,0,100,6,83,0,41,7,122,185,10,32,32, + 32,32,32,32,32,32,65,99,113,117,105,114,101,32,116,104, + 101,32,109,111,100,117,108,101,32,108,111,99,107,46,32,32, + 73,102,32,97,32,112,111,116,101,110,116,105,97,108,32,100, + 101,97,100,108,111,99,107,32,105,115,32,100,101,116,101,99, + 116,101,100,44,10,32,32,32,32,32,32,32,32,97,32,95, + 68,101,97,100,108,111,99,107,69,114,114,111,114,32,105,115, + 32,114,97,105,115,101,100,46,10,32,32,32,32,32,32,32, + 32,79,116,104,101,114,119,105,115,101,44,32,116,104,101,32, + 108,111,99,107,32,105,115,32,97,108,119,97,121,115,32,97, + 99,113,117,105,114,101,100,32,97,110,100,32,84,114,117,101, + 32,105,115,32,114,101,116,117,114,110,101,100,46,10,32,32, + 32,32,32,32,32,32,114,22,0,0,0,233,1,0,0,0, + 84,122,23,100,101,97,100,108,111,99,107,32,100,101,116,101, + 99,116,101,100,32,98,121,32,37,114,70,78,41,12,114,23, + 0,0,0,114,32,0,0,0,114,33,0,0,0,114,24,0, + 0,0,114,27,0,0,0,114,26,0,0,0,114,36,0,0, + 0,114,19,0,0,0,114,25,0,0,0,218,7,97,99,113, + 117,105,114,101,114,28,0,0,0,218,7,114,101,108,101,97, + 115,101,169,2,114,30,0,0,0,114,35,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,11,0,0,0,114,38,0, + 0,0,78,0,0,0,115,30,0,0,0,0,6,8,1,8, + 1,2,2,8,1,20,1,6,1,14,1,18,1,8,1,12, + 1,12,1,24,2,10,1,16,2,122,19,95,77,111,100,117, + 108,101,76,111,99,107,46,97,99,113,117,105,114,101,99,1, + 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,9, + 0,0,0,67,0,0,0,115,122,0,0,0,116,0,160,1, + 161,0,125,1,124,0,106,2,143,98,1,0,124,0,106,3, + 124,1,107,3,114,34,116,4,100,1,131,1,130,1,124,0, + 106,5,100,2,107,4,115,48,74,0,130,1,124,0,4,0, + 106,5,100,3,56,0,2,0,95,5,124,0,106,5,100,2, + 107,2,114,108,100,0,124,0,95,3,124,0,106,6,114,108, + 124,0,4,0,106,6,100,3,56,0,2,0,95,6,124,0, + 106,7,160,8,161,0,1,0,87,0,53,0,81,0,82,0, + 88,0,100,0,83,0,41,4,78,250,31,99,97,110,110,111, + 116,32,114,101,108,101,97,115,101,32,117,110,45,97,99,113, + 117,105,114,101,100,32,108,111,99,107,114,22,0,0,0,114, + 37,0,0,0,41,9,114,23,0,0,0,114,32,0,0,0, + 114,24,0,0,0,114,26,0,0,0,218,12,82,117,110,116, + 105,109,101,69,114,114,111,114,114,27,0,0,0,114,28,0, + 0,0,114,25,0,0,0,114,39,0,0,0,114,40,0,0, + 0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0, + 114,39,0,0,0,103,0,0,0,115,22,0,0,0,0,1, + 8,1,8,1,10,1,8,1,14,1,14,1,10,1,6,1, + 6,1,14,1,122,19,95,77,111,100,117,108,101,76,111,99, + 107,46,114,101,108,101,97,115,101,99,1,0,0,0,0,0, + 0,0,0,0,0,0,1,0,0,0,5,0,0,0,67,0, + 0,0,115,22,0,0,0,100,1,124,0,106,0,155,2,100, + 2,116,1,124,0,131,1,155,0,157,4,83,0,41,3,78, + 122,12,95,77,111,100,117,108,101,76,111,99,107,40,250,5, + 41,32,97,116,32,169,2,114,17,0,0,0,218,2,105,100, + 169,1,114,30,0,0,0,114,10,0,0,0,114,10,0,0, + 0,114,11,0,0,0,218,8,95,95,114,101,112,114,95,95, + 116,0,0,0,115,2,0,0,0,0,1,122,20,95,77,111, + 100,117,108,101,76,111,99,107,46,95,95,114,101,112,114,95, + 95,78,41,9,114,1,0,0,0,114,0,0,0,0,114,2, + 0,0,0,114,3,0,0,0,114,31,0,0,0,114,36,0, + 0,0,114,38,0,0,0,114,39,0,0,0,114,47,0,0, + 0,114,10,0,0,0,114,10,0,0,0,114,10,0,0,0, + 114,11,0,0,0,114,20,0,0,0,52,0,0,0,115,12, + 0,0,0,8,1,4,5,8,8,8,12,8,25,8,13,114, + 20,0,0,0,99,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,2,0,0,0,64,0,0,0,115,48,0, + 0,0,101,0,90,1,100,0,90,2,100,1,90,3,100,2, + 100,3,132,0,90,4,100,4,100,5,132,0,90,5,100,6, + 100,7,132,0,90,6,100,8,100,9,132,0,90,7,100,10, + 83,0,41,11,218,16,95,68,117,109,109,121,77,111,100,117, + 108,101,76,111,99,107,122,86,65,32,115,105,109,112,108,101, + 32,95,77,111,100,117,108,101,76,111,99,107,32,101,113,117, + 105,118,97,108,101,110,116,32,102,111,114,32,80,121,116,104, + 111,110,32,98,117,105,108,100,115,32,119,105,116,104,111,117, + 116,10,32,32,32,32,109,117,108,116,105,45,116,104,114,101, + 97,100,105,110,103,32,115,117,112,112,111,114,116,46,99,2, + 0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2, + 0,0,0,67,0,0,0,115,16,0,0,0,124,1,124,0, + 95,0,100,1,124,0,95,1,100,0,83,0,114,21,0,0, + 0,41,2,114,17,0,0,0,114,27,0,0,0,114,29,0, + 0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0, + 0,114,31,0,0,0,124,0,0,0,115,4,0,0,0,0, + 1,6,1,122,25,95,68,117,109,109,121,77,111,100,117,108, + 101,76,111,99,107,46,95,95,105,110,105,116,95,95,99,1, + 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,3, + 0,0,0,67,0,0,0,115,18,0,0,0,124,0,4,0, + 106,0,100,1,55,0,2,0,95,0,100,2,83,0,41,3, + 78,114,37,0,0,0,84,41,1,114,27,0,0,0,114,46, + 0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,114,38,0,0,0,128,0,0,0,115,4,0,0,0, + 0,1,14,1,122,24,95,68,117,109,109,121,77,111,100,117, + 108,101,76,111,99,107,46,97,99,113,117,105,114,101,99,1, + 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,3, + 0,0,0,67,0,0,0,115,36,0,0,0,124,0,106,0, + 100,1,107,2,114,18,116,1,100,2,131,1,130,1,124,0, + 4,0,106,0,100,3,56,0,2,0,95,0,100,0,83,0, + 41,4,78,114,22,0,0,0,114,41,0,0,0,114,37,0, + 0,0,41,2,114,27,0,0,0,114,42,0,0,0,114,46, + 0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,114,39,0,0,0,132,0,0,0,115,6,0,0,0, + 0,1,10,1,8,1,122,24,95,68,117,109,109,121,77,111, + 100,117,108,101,76,111,99,107,46,114,101,108,101,97,115,101, + 99,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0, + 0,5,0,0,0,67,0,0,0,115,22,0,0,0,100,1, + 124,0,106,0,155,2,100,2,116,1,124,0,131,1,155,0, + 157,4,83,0,41,3,78,122,17,95,68,117,109,109,121,77, + 111,100,117,108,101,76,111,99,107,40,114,43,0,0,0,114, + 44,0,0,0,114,46,0,0,0,114,10,0,0,0,114,10, + 0,0,0,114,11,0,0,0,114,47,0,0,0,137,0,0, + 0,115,2,0,0,0,0,1,122,25,95,68,117,109,109,121, + 77,111,100,117,108,101,76,111,99,107,46,95,95,114,101,112, + 114,95,95,78,41,8,114,1,0,0,0,114,0,0,0,0, + 114,2,0,0,0,114,3,0,0,0,114,31,0,0,0,114, + 38,0,0,0,114,39,0,0,0,114,47,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,114,48,0,0,0,120,0,0,0,115,10,0,0,0, + 8,1,4,3,8,4,8,4,8,5,114,48,0,0,0,99, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,0,0,0,64,0,0,0,115,36,0,0,0,101,0,90, + 1,100,0,90,2,100,1,100,2,132,0,90,3,100,3,100, + 4,132,0,90,4,100,5,100,6,132,0,90,5,100,7,83, + 0,41,8,218,18,95,77,111,100,117,108,101,76,111,99,107, + 77,97,110,97,103,101,114,99,2,0,0,0,0,0,0,0, + 0,0,0,0,2,0,0,0,2,0,0,0,67,0,0,0, + 115,16,0,0,0,124,1,124,0,95,0,100,0,124,0,95, + 1,100,0,83,0,114,13,0,0,0,41,2,218,5,95,110, + 97,109,101,218,5,95,108,111,99,107,114,29,0,0,0,114, + 10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,31, + 0,0,0,143,0,0,0,115,4,0,0,0,0,1,6,1, + 122,27,95,77,111,100,117,108,101,76,111,99,107,77,97,110, + 97,103,101,114,46,95,95,105,110,105,116,95,95,99,1,0, + 0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0, + 0,0,67,0,0,0,115,26,0,0,0,116,0,124,0,106, + 1,131,1,124,0,95,2,124,0,106,2,160,3,161,0,1, + 0,100,0,83,0,114,13,0,0,0,41,4,218,16,95,103, + 101,116,95,109,111,100,117,108,101,95,108,111,99,107,114,50, + 0,0,0,114,51,0,0,0,114,38,0,0,0,114,46,0, + 0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0, + 0,218,9,95,95,101,110,116,101,114,95,95,147,0,0,0, + 115,4,0,0,0,0,1,12,1,122,28,95,77,111,100,117, + 108,101,76,111,99,107,77,97,110,97,103,101,114,46,95,95, + 101,110,116,101,114,95,95,99,1,0,0,0,0,0,0,0, + 0,0,0,0,3,0,0,0,2,0,0,0,79,0,0,0, + 115,14,0,0,0,124,0,106,0,160,1,161,0,1,0,100, + 0,83,0,114,13,0,0,0,41,2,114,51,0,0,0,114, + 39,0,0,0,41,3,114,30,0,0,0,218,4,97,114,103, + 115,90,6,107,119,97,114,103,115,114,10,0,0,0,114,10, + 0,0,0,114,11,0,0,0,218,8,95,95,101,120,105,116, + 95,95,151,0,0,0,115,2,0,0,0,0,1,122,27,95, + 77,111,100,117,108,101,76,111,99,107,77,97,110,97,103,101, + 114,46,95,95,101,120,105,116,95,95,78,41,6,114,1,0, + 0,0,114,0,0,0,0,114,2,0,0,0,114,31,0,0, + 0,114,53,0,0,0,114,55,0,0,0,114,10,0,0,0, + 114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114, + 49,0,0,0,141,0,0,0,115,6,0,0,0,8,2,8, + 4,8,4,114,49,0,0,0,99,1,0,0,0,0,0,0, + 0,0,0,0,0,3,0,0,0,8,0,0,0,67,0,0, + 0,115,130,0,0,0,116,0,160,1,161,0,1,0,122,106, + 122,14,116,3,124,0,25,0,131,0,125,1,87,0,110,24, + 4,0,116,4,107,10,114,48,1,0,1,0,1,0,100,1, + 125,1,89,0,110,2,88,0,124,1,100,1,107,8,114,112, + 116,5,100,1,107,8,114,76,116,6,124,0,131,1,125,1, + 110,8,116,7,124,0,131,1,125,1,124,0,102,1,100,2, + 100,3,132,1,125,2,116,8,160,9,124,1,124,2,161,2, + 116,3,124,0,60,0,87,0,53,0,116,0,160,2,161,0, + 1,0,88,0,124,1,83,0,41,4,122,139,71,101,116,32, + 111,114,32,99,114,101,97,116,101,32,116,104,101,32,109,111, + 100,117,108,101,32,108,111,99,107,32,102,111,114,32,97,32, + 103,105,118,101,110,32,109,111,100,117,108,101,32,110,97,109, + 101,46,10,10,32,32,32,32,65,99,113,117,105,114,101,47, + 114,101,108,101,97,115,101,32,105,110,116,101,114,110,97,108, + 108,121,32,116,104,101,32,103,108,111,98,97,108,32,105,109, + 112,111,114,116,32,108,111,99,107,32,116,111,32,112,114,111, + 116,101,99,116,10,32,32,32,32,95,109,111,100,117,108,101, + 95,108,111,99,107,115,46,78,99,2,0,0,0,0,0,0, + 0,0,0,0,0,2,0,0,0,8,0,0,0,83,0,0, + 0,115,48,0,0,0,116,0,160,1,161,0,1,0,122,24, + 116,3,160,4,124,1,161,1,124,0,107,8,114,30,116,3, + 124,1,61,0,87,0,53,0,116,0,160,2,161,0,1,0, + 88,0,100,0,83,0,114,13,0,0,0,41,5,218,4,95, + 105,109,112,218,12,97,99,113,117,105,114,101,95,108,111,99, + 107,218,12,114,101,108,101,97,115,101,95,108,111,99,107,218, + 13,95,109,111,100,117,108,101,95,108,111,99,107,115,114,34, + 0,0,0,41,2,218,3,114,101,102,114,17,0,0,0,114, + 10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,2, + 99,98,176,0,0,0,115,10,0,0,0,0,1,8,1,2, + 4,14,1,10,2,122,28,95,103,101,116,95,109,111,100,117, + 108,101,95,108,111,99,107,46,60,108,111,99,97,108,115,62, + 46,99,98,41,10,114,56,0,0,0,114,57,0,0,0,114, + 58,0,0,0,114,59,0,0,0,218,8,75,101,121,69,114, + 114,111,114,114,23,0,0,0,114,48,0,0,0,114,20,0, + 0,0,218,8,95,119,101,97,107,114,101,102,114,60,0,0, + 0,41,3,114,17,0,0,0,114,24,0,0,0,114,61,0, + 0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0, + 0,114,52,0,0,0,157,0,0,0,115,28,0,0,0,0, + 6,8,1,2,1,2,1,14,1,14,1,10,2,8,1,8, + 1,10,2,8,2,12,11,20,2,10,2,114,52,0,0,0, + 99,1,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,8,0,0,0,67,0,0,0,115,54,0,0,0,116,0, + 124,0,131,1,125,1,122,12,124,1,160,1,161,0,1,0, + 87,0,110,20,4,0,116,2,107,10,114,40,1,0,1,0, + 1,0,89,0,110,10,88,0,124,1,160,3,161,0,1,0, + 100,1,83,0,41,2,122,189,65,99,113,117,105,114,101,115, + 32,116,104,101,110,32,114,101,108,101,97,115,101,115,32,116, + 104,101,32,109,111,100,117,108,101,32,108,111,99,107,32,102, + 111,114,32,97,32,103,105,118,101,110,32,109,111,100,117,108, + 101,32,110,97,109,101,46,10,10,32,32,32,32,84,104,105, + 115,32,105,115,32,117,115,101,100,32,116,111,32,101,110,115, + 117,114,101,32,97,32,109,111,100,117,108,101,32,105,115,32, + 99,111,109,112,108,101,116,101,108,121,32,105,110,105,116,105, + 97,108,105,122,101,100,44,32,105,110,32,116,104,101,10,32, + 32,32,32,101,118,101,110,116,32,105,116,32,105,115,32,98, + 101,105,110,103,32,105,109,112,111,114,116,101,100,32,98,121, + 32,97,110,111,116,104,101,114,32,116,104,114,101,97,100,46, + 10,32,32,32,32,78,41,4,114,52,0,0,0,114,38,0, + 0,0,114,19,0,0,0,114,39,0,0,0,41,2,114,17, + 0,0,0,114,24,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,218,19,95,108,111,99,107,95,117, + 110,108,111,99,107,95,109,111,100,117,108,101,194,0,0,0, + 115,12,0,0,0,0,6,8,1,2,1,12,1,14,3,6, + 2,114,64,0,0,0,99,1,0,0,0,0,0,0,0,0, + 0,0,0,3,0,0,0,3,0,0,0,79,0,0,0,115, + 10,0,0,0,124,0,124,1,124,2,142,1,83,0,41,1, + 97,46,1,0,0,114,101,109,111,118,101,95,105,109,112,111, + 114,116,108,105,98,95,102,114,97,109,101,115,32,105,110,32, + 105,109,112,111,114,116,46,99,32,119,105,108,108,32,97,108, + 119,97,121,115,32,114,101,109,111,118,101,32,115,101,113,117, + 101,110,99,101,115,10,32,32,32,32,111,102,32,105,109,112, + 111,114,116,108,105,98,32,102,114,97,109,101,115,32,116,104, + 97,116,32,101,110,100,32,119,105,116,104,32,97,32,99,97, + 108,108,32,116,111,32,116,104,105,115,32,102,117,110,99,116, + 105,111,110,10,10,32,32,32,32,85,115,101,32,105,116,32, + 105,110,115,116,101,97,100,32,111,102,32,97,32,110,111,114, + 109,97,108,32,99,97,108,108,32,105,110,32,112,108,97,99, + 101,115,32,119,104,101,114,101,32,105,110,99,108,117,100,105, + 110,103,32,116,104,101,32,105,109,112,111,114,116,108,105,98, + 10,32,32,32,32,102,114,97,109,101,115,32,105,110,116,114, + 111,100,117,99,101,115,32,117,110,119,97,110,116,101,100,32, + 110,111,105,115,101,32,105,110,116,111,32,116,104,101,32,116, + 114,97,99,101,98,97,99,107,32,40,101,46,103,46,32,119, + 104,101,110,32,101,120,101,99,117,116,105,110,103,10,32,32, + 32,32,109,111,100,117,108,101,32,99,111,100,101,41,10,32, + 32,32,32,114,10,0,0,0,41,3,218,1,102,114,54,0, + 0,0,90,4,107,119,100,115,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,218,25,95,99,97,108,108,95,119, + 105,116,104,95,102,114,97,109,101,115,95,114,101,109,111,118, + 101,100,211,0,0,0,115,2,0,0,0,0,8,114,66,0, + 0,0,114,37,0,0,0,41,1,218,9,118,101,114,98,111, + 115,105,116,121,99,1,0,0,0,0,0,0,0,1,0,0, + 0,3,0,0,0,4,0,0,0,71,0,0,0,115,54,0, + 0,0,116,0,106,1,106,2,124,1,107,5,114,50,124,0, + 160,3,100,1,161,1,115,30,100,2,124,0,23,0,125,0, + 116,4,124,0,106,5,124,2,142,0,116,0,106,6,100,3, + 141,2,1,0,100,4,83,0,41,5,122,61,80,114,105,110, + 116,32,116,104,101,32,109,101,115,115,97,103,101,32,116,111, + 32,115,116,100,101,114,114,32,105,102,32,45,118,47,80,89, + 84,72,79,78,86,69,82,66,79,83,69,32,105,115,32,116, + 117,114,110,101,100,32,111,110,46,41,2,250,1,35,122,7, + 105,109,112,111,114,116,32,122,2,35,32,41,1,90,4,102, + 105,108,101,78,41,7,114,15,0,0,0,218,5,102,108,97, + 103,115,218,7,118,101,114,98,111,115,101,218,10,115,116,97, + 114,116,115,119,105,116,104,218,5,112,114,105,110,116,218,6, + 102,111,114,109,97,116,218,6,115,116,100,101,114,114,41,3, + 218,7,109,101,115,115,97,103,101,114,67,0,0,0,114,54, + 0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,218,16,95,118,101,114,98,111,115,101,95,109,101,115, + 115,97,103,101,222,0,0,0,115,8,0,0,0,0,2,12, + 1,10,1,8,1,114,76,0,0,0,99,1,0,0,0,0, + 0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,3, + 0,0,0,115,26,0,0,0,135,0,102,1,100,1,100,2, + 132,8,125,1,116,0,124,1,136,0,131,2,1,0,124,1, + 83,0,41,3,122,49,68,101,99,111,114,97,116,111,114,32, + 116,111,32,118,101,114,105,102,121,32,116,104,101,32,110,97, + 109,101,100,32,109,111,100,117,108,101,32,105,115,32,98,117, + 105,108,116,45,105,110,46,99,2,0,0,0,0,0,0,0, + 0,0,0,0,2,0,0,0,4,0,0,0,19,0,0,0, + 115,38,0,0,0,124,1,116,0,106,1,107,7,114,28,116, + 2,124,1,155,2,100,1,157,2,124,1,100,2,141,2,130, + 1,136,0,124,0,124,1,131,2,83,0,41,3,78,250,25, + 32,105,115,32,110,111,116,32,97,32,98,117,105,108,116,45, + 105,110,32,109,111,100,117,108,101,114,16,0,0,0,41,3, + 114,15,0,0,0,218,20,98,117,105,108,116,105,110,95,109, + 111,100,117,108,101,95,110,97,109,101,115,218,11,73,109,112, + 111,114,116,69,114,114,111,114,169,2,114,30,0,0,0,218, + 8,102,117,108,108,110,97,109,101,169,1,218,3,102,120,110, + 114,10,0,0,0,114,11,0,0,0,218,25,95,114,101,113, + 117,105,114,101,115,95,98,117,105,108,116,105,110,95,119,114, + 97,112,112,101,114,232,0,0,0,115,10,0,0,0,0,1, + 10,1,10,1,2,255,6,2,122,52,95,114,101,113,117,105, + 114,101,115,95,98,117,105,108,116,105,110,46,60,108,111,99, + 97,108,115,62,46,95,114,101,113,117,105,114,101,115,95,98, + 117,105,108,116,105,110,95,119,114,97,112,112,101,114,169,1, + 114,12,0,0,0,41,2,114,83,0,0,0,114,84,0,0, + 0,114,10,0,0,0,114,82,0,0,0,114,11,0,0,0, + 218,17,95,114,101,113,117,105,114,101,115,95,98,117,105,108, + 116,105,110,230,0,0,0,115,6,0,0,0,0,2,12,5, + 10,1,114,86,0,0,0,99,1,0,0,0,0,0,0,0, + 0,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0, + 115,26,0,0,0,135,0,102,1,100,1,100,2,132,8,125, + 1,116,0,124,1,136,0,131,2,1,0,124,1,83,0,41, + 3,122,47,68,101,99,111,114,97,116,111,114,32,116,111,32, + 118,101,114,105,102,121,32,116,104,101,32,110,97,109,101,100, + 32,109,111,100,117,108,101,32,105,115,32,102,114,111,122,101, + 110,46,99,2,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,4,0,0,0,19,0,0,0,115,38,0,0,0, + 116,0,160,1,124,1,161,1,115,28,116,2,124,1,155,2, + 100,1,157,2,124,1,100,2,141,2,130,1,136,0,124,0, + 124,1,131,2,83,0,169,3,78,122,23,32,105,115,32,110, + 111,116,32,97,32,102,114,111,122,101,110,32,109,111,100,117, + 108,101,114,16,0,0,0,41,3,114,56,0,0,0,218,9, + 105,115,95,102,114,111,122,101,110,114,79,0,0,0,114,80, + 0,0,0,114,82,0,0,0,114,10,0,0,0,114,11,0, + 0,0,218,24,95,114,101,113,117,105,114,101,115,95,102,114, + 111,122,101,110,95,119,114,97,112,112,101,114,243,0,0,0, + 115,10,0,0,0,0,1,10,1,10,1,2,255,6,2,122, + 50,95,114,101,113,117,105,114,101,115,95,102,114,111,122,101, + 110,46,60,108,111,99,97,108,115,62,46,95,114,101,113,117, + 105,114,101,115,95,102,114,111,122,101,110,95,119,114,97,112, + 112,101,114,114,85,0,0,0,41,2,114,83,0,0,0,114, + 89,0,0,0,114,10,0,0,0,114,82,0,0,0,114,11, + 0,0,0,218,16,95,114,101,113,117,105,114,101,115,95,102, + 114,111,122,101,110,241,0,0,0,115,6,0,0,0,0,2, + 12,5,10,1,114,90,0,0,0,99,2,0,0,0,0,0, + 0,0,0,0,0,0,4,0,0,0,3,0,0,0,67,0, + 0,0,115,62,0,0,0,116,0,124,1,124,0,131,2,125, + 2,124,1,116,1,106,2,107,6,114,50,116,1,106,2,124, + 1,25,0,125,3,116,3,124,2,124,3,131,2,1,0,116, + 1,106,2,124,1,25,0,83,0,116,4,124,2,131,1,83, + 0,100,1,83,0,41,2,122,128,76,111,97,100,32,116,104, + 101,32,115,112,101,99,105,102,105,101,100,32,109,111,100,117, + 108,101,32,105,110,116,111,32,115,121,115,46,109,111,100,117, + 108,101,115,32,97,110,100,32,114,101,116,117,114,110,32,105, + 116,46,10,10,32,32,32,32,84,104,105,115,32,109,101,116, + 104,111,100,32,105,115,32,100,101,112,114,101,99,97,116,101, + 100,46,32,32,85,115,101,32,108,111,97,100,101,114,46,101, + 120,101,99,95,109,111,100,117,108,101,32,105,110,115,116,101, + 97,100,46,10,10,32,32,32,32,78,41,5,218,16,115,112, + 101,99,95,102,114,111,109,95,108,111,97,100,101,114,114,15, + 0,0,0,218,7,109,111,100,117,108,101,115,218,5,95,101, + 120,101,99,218,5,95,108,111,97,100,41,4,114,30,0,0, + 0,114,81,0,0,0,218,4,115,112,101,99,218,6,109,111, + 100,117,108,101,114,10,0,0,0,114,10,0,0,0,114,11, + 0,0,0,218,17,95,108,111,97,100,95,109,111,100,117,108, + 101,95,115,104,105,109,253,0,0,0,115,12,0,0,0,0, + 6,10,1,10,1,10,1,10,1,10,2,114,97,0,0,0, + 99,1,0,0,0,0,0,0,0,0,0,0,0,5,0,0, + 0,8,0,0,0,67,0,0,0,115,240,0,0,0,116,0, + 124,0,100,1,100,0,131,3,125,1,116,1,124,1,100,2, + 131,2,114,56,122,12,124,1,160,2,124,0,161,1,87,0, + 83,0,4,0,116,3,107,10,114,54,1,0,1,0,1,0, + 89,0,110,2,88,0,122,10,124,0,106,4,125,2,87,0, + 110,20,4,0,116,5,107,10,114,86,1,0,1,0,1,0, + 89,0,110,18,88,0,124,2,100,0,107,9,114,104,116,6, + 124,2,131,1,83,0,122,10,124,0,106,7,125,3,87,0, + 110,24,4,0,116,5,107,10,114,138,1,0,1,0,1,0, + 100,3,125,3,89,0,110,2,88,0,122,10,124,0,106,8, + 125,4,87,0,110,66,4,0,116,5,107,10,114,216,1,0, + 1,0,1,0,124,1,100,0,107,8,114,190,100,4,124,3, + 155,2,100,5,157,3,6,0,89,0,83,0,100,4,124,3, + 155,2,100,6,124,1,155,2,100,7,157,5,6,0,89,0, + 83,0,89,0,110,20,88,0,100,4,124,3,155,2,100,8, + 124,4,155,2,100,5,157,5,83,0,100,0,83,0,41,9, + 78,218,10,95,95,108,111,97,100,101,114,95,95,218,11,109, + 111,100,117,108,101,95,114,101,112,114,250,1,63,250,8,60, + 109,111,100,117,108,101,32,250,1,62,250,2,32,40,250,2, + 41,62,250,6,32,102,114,111,109,32,41,9,114,6,0,0, + 0,114,4,0,0,0,114,99,0,0,0,218,9,69,120,99, + 101,112,116,105,111,110,218,8,95,95,115,112,101,99,95,95, + 218,14,65,116,116,114,105,98,117,116,101,69,114,114,111,114, + 218,22,95,109,111,100,117,108,101,95,114,101,112,114,95,102, + 114,111,109,95,115,112,101,99,114,1,0,0,0,218,8,95, + 95,102,105,108,101,95,95,41,5,114,96,0,0,0,218,6, + 108,111,97,100,101,114,114,95,0,0,0,114,17,0,0,0, + 218,8,102,105,108,101,110,97,109,101,114,10,0,0,0,114, + 10,0,0,0,114,11,0,0,0,218,12,95,109,111,100,117, + 108,101,95,114,101,112,114,13,1,0,0,115,46,0,0,0, + 0,2,12,1,10,4,2,1,12,1,14,1,6,1,2,1, + 10,1,14,1,6,2,8,1,8,4,2,1,10,1,14,1, + 10,1,2,1,10,1,14,1,8,1,16,2,28,2,114,113, + 0,0,0,99,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,4,0,0,0,64,0,0,0,115,114,0,0, + 0,101,0,90,1,100,0,90,2,100,1,90,3,100,2,100, + 2,100,2,100,3,156,3,100,4,100,5,132,2,90,4,100, + 6,100,7,132,0,90,5,100,8,100,9,132,0,90,6,101, + 7,100,10,100,11,132,0,131,1,90,8,101,8,106,9,100, + 12,100,11,132,0,131,1,90,8,101,7,100,13,100,14,132, + 0,131,1,90,10,101,7,100,15,100,16,132,0,131,1,90, + 11,101,11,106,9,100,17,100,16,132,0,131,1,90,11,100, + 2,83,0,41,18,218,10,77,111,100,117,108,101,83,112,101, + 99,97,208,5,0,0,84,104,101,32,115,112,101,99,105,102, + 105,99,97,116,105,111,110,32,102,111,114,32,97,32,109,111, + 100,117,108,101,44,32,117,115,101,100,32,102,111,114,32,108, + 111,97,100,105,110,103,46,10,10,32,32,32,32,65,32,109, + 111,100,117,108,101,39,115,32,115,112,101,99,32,105,115,32, + 116,104,101,32,115,111,117,114,99,101,32,102,111,114,32,105, + 110,102,111,114,109,97,116,105,111,110,32,97,98,111,117,116, + 32,116,104,101,32,109,111,100,117,108,101,46,32,32,70,111, + 114,10,32,32,32,32,100,97,116,97,32,97,115,115,111,99, + 105,97,116,101,100,32,119,105,116,104,32,116,104,101,32,109, + 111,100,117,108,101,44,32,105,110,99,108,117,100,105,110,103, + 32,115,111,117,114,99,101,44,32,117,115,101,32,116,104,101, + 32,115,112,101,99,39,115,10,32,32,32,32,108,111,97,100, + 101,114,46,10,10,32,32,32,32,96,110,97,109,101,96,32, + 105,115,32,116,104,101,32,97,98,115,111,108,117,116,101,32, + 110,97,109,101,32,111,102,32,116,104,101,32,109,111,100,117, + 108,101,46,32,32,96,108,111,97,100,101,114,96,32,105,115, + 32,116,104,101,32,108,111,97,100,101,114,10,32,32,32,32, + 116,111,32,117,115,101,32,119,104,101,110,32,108,111,97,100, + 105,110,103,32,116,104,101,32,109,111,100,117,108,101,46,32, + 32,96,112,97,114,101,110,116,96,32,105,115,32,116,104,101, + 32,110,97,109,101,32,111,102,32,116,104,101,10,32,32,32, + 32,112,97,99,107,97,103,101,32,116,104,101,32,109,111,100, + 117,108,101,32,105,115,32,105,110,46,32,32,84,104,101,32, + 112,97,114,101,110,116,32,105,115,32,100,101,114,105,118,101, + 100,32,102,114,111,109,32,116,104,101,32,110,97,109,101,46, + 10,10,32,32,32,32,96,105,115,95,112,97,99,107,97,103, + 101,96,32,100,101,116,101,114,109,105,110,101,115,32,105,102, + 32,116,104,101,32,109,111,100,117,108,101,32,105,115,32,99, + 111,110,115,105,100,101,114,101,100,32,97,32,112,97,99,107, + 97,103,101,32,111,114,10,32,32,32,32,110,111,116,46,32, + 32,79,110,32,109,111,100,117,108,101,115,32,116,104,105,115, + 32,105,115,32,114,101,102,108,101,99,116,101,100,32,98,121, + 32,116,104,101,32,96,95,95,112,97,116,104,95,95,96,32, + 97,116,116,114,105,98,117,116,101,46,10,10,32,32,32,32, + 96,111,114,105,103,105,110,96,32,105,115,32,116,104,101,32, + 115,112,101,99,105,102,105,99,32,108,111,99,97,116,105,111, + 110,32,117,115,101,100,32,98,121,32,116,104,101,32,108,111, + 97,100,101,114,32,102,114,111,109,32,119,104,105,99,104,32, + 116,111,10,32,32,32,32,108,111,97,100,32,116,104,101,32, + 109,111,100,117,108,101,44,32,105,102,32,116,104,97,116,32, + 105,110,102,111,114,109,97,116,105,111,110,32,105,115,32,97, + 118,97,105,108,97,98,108,101,46,32,32,87,104,101,110,32, + 102,105,108,101,110,97,109,101,32,105,115,10,32,32,32,32, + 115,101,116,44,32,111,114,105,103,105,110,32,119,105,108,108, + 32,109,97,116,99,104,46,10,10,32,32,32,32,96,104,97, + 115,95,108,111,99,97,116,105,111,110,96,32,105,110,100,105, + 99,97,116,101,115,32,116,104,97,116,32,97,32,115,112,101, + 99,39,115,32,34,111,114,105,103,105,110,34,32,114,101,102, + 108,101,99,116,115,32,97,32,108,111,99,97,116,105,111,110, + 46,10,32,32,32,32,87,104,101,110,32,116,104,105,115,32, + 105,115,32,84,114,117,101,44,32,96,95,95,102,105,108,101, + 95,95,96,32,97,116,116,114,105,98,117,116,101,32,111,102, + 32,116,104,101,32,109,111,100,117,108,101,32,105,115,32,115, + 101,116,46,10,10,32,32,32,32,96,99,97,99,104,101,100, + 96,32,105,115,32,116,104,101,32,108,111,99,97,116,105,111, + 110,32,111,102,32,116,104,101,32,99,97,99,104,101,100,32, + 98,121,116,101,99,111,100,101,32,102,105,108,101,44,32,105, + 102,32,97,110,121,46,32,32,73,116,10,32,32,32,32,99, + 111,114,114,101,115,112,111,110,100,115,32,116,111,32,116,104, + 101,32,96,95,95,99,97,99,104,101,100,95,95,96,32,97, + 116,116,114,105,98,117,116,101,46,10,10,32,32,32,32,96, + 115,117,98,109,111,100,117,108,101,95,115,101,97,114,99,104, + 95,108,111,99,97,116,105,111,110,115,96,32,105,115,32,116, + 104,101,32,115,101,113,117,101,110,99,101,32,111,102,32,112, + 97,116,104,32,101,110,116,114,105,101,115,32,116,111,10,32, + 32,32,32,115,101,97,114,99,104,32,119,104,101,110,32,105, + 109,112,111,114,116,105,110,103,32,115,117,98,109,111,100,117, + 108,101,115,46,32,32,73,102,32,115,101,116,44,32,105,115, + 95,112,97,99,107,97,103,101,32,115,104,111,117,108,100,32, + 98,101,10,32,32,32,32,84,114,117,101,45,45,97,110,100, + 32,70,97,108,115,101,32,111,116,104,101,114,119,105,115,101, + 46,10,10,32,32,32,32,80,97,99,107,97,103,101,115,32, + 97,114,101,32,115,105,109,112,108,121,32,109,111,100,117,108, + 101,115,32,116,104,97,116,32,40,109,97,121,41,32,104,97, + 118,101,32,115,117,98,109,111,100,117,108,101,115,46,32,32, + 73,102,32,97,32,115,112,101,99,10,32,32,32,32,104,97, + 115,32,97,32,110,111,110,45,78,111,110,101,32,118,97,108, + 117,101,32,105,110,32,96,115,117,98,109,111,100,117,108,101, + 95,115,101,97,114,99,104,95,108,111,99,97,116,105,111,110, + 115,96,44,32,116,104,101,32,105,109,112,111,114,116,10,32, + 32,32,32,115,121,115,116,101,109,32,119,105,108,108,32,99, + 111,110,115,105,100,101,114,32,109,111,100,117,108,101,115,32, + 108,111,97,100,101,100,32,102,114,111,109,32,116,104,101,32, + 115,112,101,99,32,97,115,32,112,97,99,107,97,103,101,115, + 46,10,10,32,32,32,32,79,110,108,121,32,102,105,110,100, + 101,114,115,32,40,115,101,101,32,105,109,112,111,114,116,108, + 105,98,46,97,98,99,46,77,101,116,97,80,97,116,104,70, + 105,110,100,101,114,32,97,110,100,10,32,32,32,32,105,109, + 112,111,114,116,108,105,98,46,97,98,99,46,80,97,116,104, + 69,110,116,114,121,70,105,110,100,101,114,41,32,115,104,111, + 117,108,100,32,109,111,100,105,102,121,32,77,111,100,117,108, + 101,83,112,101,99,32,105,110,115,116,97,110,99,101,115,46, + 10,10,32,32,32,32,78,41,3,218,6,111,114,105,103,105, + 110,218,12,108,111,97,100,101,114,95,115,116,97,116,101,218, + 10,105,115,95,112,97,99,107,97,103,101,99,3,0,0,0, + 0,0,0,0,3,0,0,0,6,0,0,0,2,0,0,0, + 67,0,0,0,115,54,0,0,0,124,1,124,0,95,0,124, + 2,124,0,95,1,124,3,124,0,95,2,124,4,124,0,95, + 3,124,5,114,32,103,0,110,2,100,0,124,0,95,4,100, + 1,124,0,95,5,100,0,124,0,95,6,100,0,83,0,41, + 2,78,70,41,7,114,17,0,0,0,114,111,0,0,0,114, + 115,0,0,0,114,116,0,0,0,218,26,115,117,98,109,111, + 100,117,108,101,95,115,101,97,114,99,104,95,108,111,99,97, + 116,105,111,110,115,218,13,95,115,101,116,95,102,105,108,101, + 97,116,116,114,218,7,95,99,97,99,104,101,100,41,6,114, + 30,0,0,0,114,17,0,0,0,114,111,0,0,0,114,115, + 0,0,0,114,116,0,0,0,114,117,0,0,0,114,10,0, + 0,0,114,10,0,0,0,114,11,0,0,0,114,31,0,0, + 0,86,1,0,0,115,14,0,0,0,0,2,6,1,6,1, + 6,1,6,1,14,3,6,1,122,19,77,111,100,117,108,101, + 83,112,101,99,46,95,95,105,110,105,116,95,95,99,1,0, + 0,0,0,0,0,0,0,0,0,0,2,0,0,0,5,0, + 0,0,67,0,0,0,115,106,0,0,0,100,1,124,0,106, + 0,155,2,157,2,100,2,124,0,106,1,155,2,157,2,103, + 2,125,1,124,0,106,2,100,0,107,9,114,52,124,1,160, + 3,100,3,124,0,106,2,155,2,157,2,161,1,1,0,124, + 0,106,4,100,0,107,9,114,80,124,1,160,3,100,4,124, + 0,106,4,155,0,157,2,161,1,1,0,124,0,106,5,106, + 6,155,0,100,5,100,6,160,7,124,1,161,1,155,0,100, + 7,157,4,83,0,41,8,78,122,5,110,97,109,101,61,122, + 7,108,111,97,100,101,114,61,122,7,111,114,105,103,105,110, + 61,122,27,115,117,98,109,111,100,117,108,101,95,115,101,97, + 114,99,104,95,108,111,99,97,116,105,111,110,115,61,250,1, + 40,122,2,44,32,250,1,41,41,8,114,17,0,0,0,114, + 111,0,0,0,114,115,0,0,0,218,6,97,112,112,101,110, + 100,114,118,0,0,0,218,9,95,95,99,108,97,115,115,95, + 95,114,1,0,0,0,218,4,106,111,105,110,41,2,114,30, + 0,0,0,114,54,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,114,47,0,0,0,98,1,0,0, + 115,16,0,0,0,0,1,10,1,10,255,4,2,10,1,18, + 1,10,1,18,1,122,19,77,111,100,117,108,101,83,112,101, + 99,46,95,95,114,101,112,114,95,95,99,2,0,0,0,0, + 0,0,0,0,0,0,0,3,0,0,0,8,0,0,0,67, + 0,0,0,115,108,0,0,0,124,0,106,0,125,2,122,72, + 124,0,106,1,124,1,106,1,107,2,111,76,124,0,106,2, + 124,1,106,2,107,2,111,76,124,0,106,3,124,1,106,3, + 107,2,111,76,124,2,124,1,106,0,107,2,111,76,124,0, + 106,4,124,1,106,4,107,2,111,76,124,0,106,5,124,1, + 106,5,107,2,87,0,83,0,4,0,116,6,107,10,114,102, + 1,0,1,0,1,0,116,7,6,0,89,0,83,0,88,0, + 100,0,83,0,114,13,0,0,0,41,8,114,118,0,0,0, + 114,17,0,0,0,114,111,0,0,0,114,115,0,0,0,218, + 6,99,97,99,104,101,100,218,12,104,97,115,95,108,111,99, + 97,116,105,111,110,114,108,0,0,0,218,14,78,111,116,73, + 109,112,108,101,109,101,110,116,101,100,41,3,114,30,0,0, + 0,90,5,111,116,104,101,114,90,4,115,109,115,108,114,10, + 0,0,0,114,10,0,0,0,114,11,0,0,0,218,6,95, + 95,101,113,95,95,107,1,0,0,115,30,0,0,0,0,1, + 6,1,2,1,12,1,10,255,2,2,10,254,2,3,8,253, + 2,4,10,252,2,5,10,251,4,6,14,1,122,17,77,111, + 100,117,108,101,83,112,101,99,46,95,95,101,113,95,95,99, + 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, + 3,0,0,0,67,0,0,0,115,58,0,0,0,124,0,106, + 0,100,0,107,8,114,52,124,0,106,1,100,0,107,9,114, + 52,124,0,106,2,114,52,116,3,100,0,107,8,114,38,116, + 4,130,1,116,3,160,5,124,0,106,1,161,1,124,0,95, + 0,124,0,106,0,83,0,114,13,0,0,0,41,6,114,120, + 0,0,0,114,115,0,0,0,114,119,0,0,0,218,19,95, + 98,111,111,116,115,116,114,97,112,95,101,120,116,101,114,110, + 97,108,218,19,78,111,116,73,109,112,108,101,109,101,110,116, + 101,100,69,114,114,111,114,90,11,95,103,101,116,95,99,97, + 99,104,101,100,114,46,0,0,0,114,10,0,0,0,114,10, + 0,0,0,114,11,0,0,0,114,126,0,0,0,119,1,0, + 0,115,12,0,0,0,0,2,10,1,16,1,8,1,4,1, + 14,1,122,17,77,111,100,117,108,101,83,112,101,99,46,99, + 97,99,104,101,100,99,2,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,2,0,0,0,67,0,0,0,115,10, + 0,0,0,124,1,124,0,95,0,100,0,83,0,114,13,0, + 0,0,41,1,114,120,0,0,0,41,2,114,30,0,0,0, + 114,126,0,0,0,114,10,0,0,0,114,10,0,0,0,114, + 11,0,0,0,114,126,0,0,0,128,1,0,0,115,2,0, + 0,0,0,2,99,1,0,0,0,0,0,0,0,0,0,0, + 0,1,0,0,0,3,0,0,0,67,0,0,0,115,36,0, + 0,0,124,0,106,0,100,1,107,8,114,26,124,0,106,1, + 160,2,100,2,161,1,100,3,25,0,83,0,124,0,106,1, + 83,0,100,1,83,0,41,4,122,32,84,104,101,32,110,97, + 109,101,32,111,102,32,116,104,101,32,109,111,100,117,108,101, + 39,115,32,112,97,114,101,110,116,46,78,218,1,46,114,22, + 0,0,0,41,3,114,118,0,0,0,114,17,0,0,0,218, + 10,114,112,97,114,116,105,116,105,111,110,114,46,0,0,0, + 114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,218, + 6,112,97,114,101,110,116,132,1,0,0,115,6,0,0,0, + 0,3,10,1,16,2,122,17,77,111,100,117,108,101,83,112, + 101,99,46,112,97,114,101,110,116,99,1,0,0,0,0,0, + 0,0,0,0,0,0,1,0,0,0,1,0,0,0,67,0, + 0,0,115,6,0,0,0,124,0,106,0,83,0,114,13,0, + 0,0,41,1,114,119,0,0,0,114,46,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,11,0,0,0,114,127,0, + 0,0,140,1,0,0,115,2,0,0,0,0,2,122,23,77, + 111,100,117,108,101,83,112,101,99,46,104,97,115,95,108,111, + 99,97,116,105,111,110,99,2,0,0,0,0,0,0,0,0, + 0,0,0,2,0,0,0,2,0,0,0,67,0,0,0,115, + 14,0,0,0,116,0,124,1,131,1,124,0,95,1,100,0, + 83,0,114,13,0,0,0,41,2,218,4,98,111,111,108,114, + 119,0,0,0,41,2,114,30,0,0,0,218,5,118,97,108, + 117,101,114,10,0,0,0,114,10,0,0,0,114,11,0,0, + 0,114,127,0,0,0,144,1,0,0,115,2,0,0,0,0, + 2,41,12,114,1,0,0,0,114,0,0,0,0,114,2,0, + 0,0,114,3,0,0,0,114,31,0,0,0,114,47,0,0, + 0,114,129,0,0,0,218,8,112,114,111,112,101,114,116,121, + 114,126,0,0,0,218,6,115,101,116,116,101,114,114,134,0, + 0,0,114,127,0,0,0,114,10,0,0,0,114,10,0,0, + 0,114,10,0,0,0,114,11,0,0,0,114,114,0,0,0, + 49,1,0,0,115,32,0,0,0,8,1,4,36,4,1,2, + 255,12,12,8,9,8,12,2,1,10,8,4,1,10,3,2, + 1,10,7,2,1,10,3,4,1,114,114,0,0,0,169,2, + 114,115,0,0,0,114,117,0,0,0,99,2,0,0,0,0, + 0,0,0,2,0,0,0,6,0,0,0,8,0,0,0,67, + 0,0,0,115,154,0,0,0,116,0,124,1,100,1,131,2, + 114,74,116,1,100,2,107,8,114,22,116,2,130,1,116,1, + 106,3,125,4,124,3,100,2,107,8,114,48,124,4,124,0, + 124,1,100,3,141,2,83,0,124,3,114,56,103,0,110,2, + 100,2,125,5,124,4,124,0,124,1,124,5,100,4,141,3, + 83,0,124,3,100,2,107,8,114,138,116,0,124,1,100,5, + 131,2,114,134,122,14,124,1,160,4,124,0,161,1,125,3, + 87,0,113,138,4,0,116,5,107,10,114,130,1,0,1,0, + 1,0,100,2,125,3,89,0,113,138,88,0,110,4,100,6, + 125,3,116,6,124,0,124,1,124,2,124,3,100,7,141,4, + 83,0,41,8,122,53,82,101,116,117,114,110,32,97,32,109, + 111,100,117,108,101,32,115,112,101,99,32,98,97,115,101,100, + 32,111,110,32,118,97,114,105,111,117,115,32,108,111,97,100, + 101,114,32,109,101,116,104,111,100,115,46,90,12,103,101,116, + 95,102,105,108,101,110,97,109,101,78,41,1,114,111,0,0, + 0,41,2,114,111,0,0,0,114,118,0,0,0,114,117,0, + 0,0,70,114,139,0,0,0,41,7,114,4,0,0,0,114, + 130,0,0,0,114,131,0,0,0,218,23,115,112,101,99,95, + 102,114,111,109,95,102,105,108,101,95,108,111,99,97,116,105, + 111,110,114,117,0,0,0,114,79,0,0,0,114,114,0,0, + 0,41,6,114,17,0,0,0,114,111,0,0,0,114,115,0, + 0,0,114,117,0,0,0,114,140,0,0,0,90,6,115,101, + 97,114,99,104,114,10,0,0,0,114,10,0,0,0,114,11, + 0,0,0,114,91,0,0,0,149,1,0,0,115,36,0,0, + 0,0,2,10,1,8,1,4,1,6,2,8,1,12,1,12, + 1,6,1,2,255,6,3,8,1,10,1,2,1,14,1,14, + 1,12,3,4,2,114,91,0,0,0,99,3,0,0,0,0, + 0,0,0,0,0,0,0,8,0,0,0,8,0,0,0,67, + 0,0,0,115,56,1,0,0,122,10,124,0,106,0,125,3, + 87,0,110,20,4,0,116,1,107,10,114,30,1,0,1,0, + 1,0,89,0,110,14,88,0,124,3,100,0,107,9,114,44, + 124,3,83,0,124,0,106,2,125,4,124,1,100,0,107,8, + 114,90,122,10,124,0,106,3,125,1,87,0,110,20,4,0, + 116,1,107,10,114,88,1,0,1,0,1,0,89,0,110,2, + 88,0,122,10,124,0,106,4,125,5,87,0,110,24,4,0, + 116,1,107,10,114,124,1,0,1,0,1,0,100,0,125,5, + 89,0,110,2,88,0,124,2,100,0,107,8,114,184,124,5, + 100,0,107,8,114,180,122,10,124,1,106,5,125,2,87,0, + 113,184,4,0,116,1,107,10,114,176,1,0,1,0,1,0, + 100,0,125,2,89,0,113,184,88,0,110,4,124,5,125,2, + 122,10,124,0,106,6,125,6,87,0,110,24,4,0,116,1, + 107,10,114,218,1,0,1,0,1,0,100,0,125,6,89,0, + 110,2,88,0,122,14,116,7,124,0,106,8,131,1,125,7, + 87,0,110,26,4,0,116,1,107,10,144,1,114,4,1,0, + 1,0,1,0,100,0,125,7,89,0,110,2,88,0,116,9, + 124,4,124,1,124,2,100,1,141,3,125,3,124,5,100,0, + 107,8,144,1,114,34,100,2,110,2,100,3,124,3,95,10, + 124,6,124,3,95,11,124,7,124,3,95,12,124,3,83,0, + 41,4,78,169,1,114,115,0,0,0,70,84,41,13,114,107, + 0,0,0,114,108,0,0,0,114,1,0,0,0,114,98,0, + 0,0,114,110,0,0,0,218,7,95,79,82,73,71,73,78, + 218,10,95,95,99,97,99,104,101,100,95,95,218,4,108,105, + 115,116,218,8,95,95,112,97,116,104,95,95,114,114,0,0, + 0,114,119,0,0,0,114,126,0,0,0,114,118,0,0,0, + 41,8,114,96,0,0,0,114,111,0,0,0,114,115,0,0, + 0,114,95,0,0,0,114,17,0,0,0,90,8,108,111,99, + 97,116,105,111,110,114,126,0,0,0,114,118,0,0,0,114, + 10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,17, + 95,115,112,101,99,95,102,114,111,109,95,109,111,100,117,108, + 101,175,1,0,0,115,72,0,0,0,0,2,2,1,10,1, + 14,1,6,2,8,1,4,2,6,1,8,1,2,1,10,1, + 14,2,6,1,2,1,10,1,14,1,10,1,8,1,8,1, + 2,1,10,1,14,1,12,2,4,1,2,1,10,1,14,1, + 10,1,2,1,14,1,16,1,10,2,14,1,20,1,6,1, + 6,1,114,146,0,0,0,70,169,1,218,8,111,118,101,114, + 114,105,100,101,99,2,0,0,0,0,0,0,0,1,0,0, + 0,5,0,0,0,8,0,0,0,67,0,0,0,115,226,1, + 0,0,124,2,115,20,116,0,124,1,100,1,100,0,131,3, + 100,0,107,8,114,54,122,12,124,0,106,1,124,1,95,2, + 87,0,110,20,4,0,116,3,107,10,114,52,1,0,1,0, + 1,0,89,0,110,2,88,0,124,2,115,74,116,0,124,1, + 100,2,100,0,131,3,100,0,107,8,114,178,124,0,106,4, + 125,3,124,3,100,0,107,8,114,146,124,0,106,5,100,0, + 107,9,114,146,116,6,100,0,107,8,114,110,116,7,130,1, + 116,6,106,8,125,4,124,4,160,9,124,4,161,1,125,3, + 124,0,106,5,124,3,95,10,124,3,124,0,95,4,100,0, + 124,1,95,11,122,10,124,3,124,1,95,12,87,0,110,20, + 4,0,116,3,107,10,114,176,1,0,1,0,1,0,89,0, + 110,2,88,0,124,2,115,198,116,0,124,1,100,3,100,0, + 131,3,100,0,107,8,114,232,122,12,124,0,106,13,124,1, + 95,14,87,0,110,20,4,0,116,3,107,10,114,230,1,0, + 1,0,1,0,89,0,110,2,88,0,122,10,124,0,124,1, + 95,15,87,0,110,22,4,0,116,3,107,10,144,1,114,8, + 1,0,1,0,1,0,89,0,110,2,88,0,124,2,144,1, + 115,34,116,0,124,1,100,4,100,0,131,3,100,0,107,8, + 144,1,114,82,124,0,106,5,100,0,107,9,144,1,114,82, + 122,12,124,0,106,5,124,1,95,16,87,0,110,22,4,0, + 116,3,107,10,144,1,114,80,1,0,1,0,1,0,89,0, + 110,2,88,0,124,0,106,17,144,1,114,222,124,2,144,1, + 115,114,116,0,124,1,100,5,100,0,131,3,100,0,107,8, + 144,1,114,150,122,12,124,0,106,18,124,1,95,11,87,0, + 110,22,4,0,116,3,107,10,144,1,114,148,1,0,1,0, + 1,0,89,0,110,2,88,0,124,2,144,1,115,174,116,0, + 124,1,100,6,100,0,131,3,100,0,107,8,144,1,114,222, + 124,0,106,19,100,0,107,9,144,1,114,222,122,12,124,0, + 106,19,124,1,95,20,87,0,110,22,4,0,116,3,107,10, + 144,1,114,220,1,0,1,0,1,0,89,0,110,2,88,0, + 124,1,83,0,41,7,78,114,1,0,0,0,114,98,0,0, + 0,218,11,95,95,112,97,99,107,97,103,101,95,95,114,145, + 0,0,0,114,110,0,0,0,114,143,0,0,0,41,21,114, + 6,0,0,0,114,17,0,0,0,114,1,0,0,0,114,108, + 0,0,0,114,111,0,0,0,114,118,0,0,0,114,130,0, + 0,0,114,131,0,0,0,218,16,95,78,97,109,101,115,112, + 97,99,101,76,111,97,100,101,114,218,7,95,95,110,101,119, + 95,95,90,5,95,112,97,116,104,114,110,0,0,0,114,98, + 0,0,0,114,134,0,0,0,114,149,0,0,0,114,107,0, + 0,0,114,145,0,0,0,114,127,0,0,0,114,115,0,0, + 0,114,126,0,0,0,114,143,0,0,0,41,5,114,95,0, + 0,0,114,96,0,0,0,114,148,0,0,0,114,111,0,0, + 0,114,150,0,0,0,114,10,0,0,0,114,10,0,0,0, + 114,11,0,0,0,218,18,95,105,110,105,116,95,109,111,100, + 117,108,101,95,97,116,116,114,115,220,1,0,0,115,96,0, + 0,0,0,4,20,1,2,1,12,1,14,1,6,2,20,1, + 6,1,8,2,10,1,8,1,4,1,6,2,10,1,8,1, + 6,11,6,1,2,1,10,1,14,1,6,2,20,1,2,1, + 12,1,14,1,6,2,2,1,10,1,16,1,6,2,24,1, + 12,1,2,1,12,1,16,1,6,2,8,1,24,1,2,1, + 12,1,16,1,6,2,24,1,12,1,2,1,12,1,16,1, + 6,1,114,152,0,0,0,99,1,0,0,0,0,0,0,0, + 0,0,0,0,2,0,0,0,3,0,0,0,67,0,0,0, + 115,82,0,0,0,100,1,125,1,116,0,124,0,106,1,100, + 2,131,2,114,30,124,0,106,1,160,2,124,0,161,1,125, + 1,110,20,116,0,124,0,106,1,100,3,131,2,114,50,116, + 3,100,4,131,1,130,1,124,1,100,1,107,8,114,68,116, + 4,124,0,106,5,131,1,125,1,116,6,124,0,124,1,131, + 2,1,0,124,1,83,0,41,5,122,43,67,114,101,97,116, + 101,32,97,32,109,111,100,117,108,101,32,98,97,115,101,100, + 32,111,110,32,116,104,101,32,112,114,111,118,105,100,101,100, + 32,115,112,101,99,46,78,218,13,99,114,101,97,116,101,95, + 109,111,100,117,108,101,218,11,101,120,101,99,95,109,111,100, + 117,108,101,122,66,108,111,97,100,101,114,115,32,116,104,97, + 116,32,100,101,102,105,110,101,32,101,120,101,99,95,109,111, + 100,117,108,101,40,41,32,109,117,115,116,32,97,108,115,111, + 32,100,101,102,105,110,101,32,99,114,101,97,116,101,95,109, + 111,100,117,108,101,40,41,41,7,114,4,0,0,0,114,111, + 0,0,0,114,153,0,0,0,114,79,0,0,0,114,18,0, + 0,0,114,17,0,0,0,114,152,0,0,0,169,2,114,95, + 0,0,0,114,96,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,218,16,109,111,100,117,108,101,95, + 102,114,111,109,95,115,112,101,99,36,2,0,0,115,18,0, + 0,0,0,3,4,1,12,3,14,1,12,1,8,2,8,1, + 10,1,10,1,114,156,0,0,0,99,1,0,0,0,0,0, + 0,0,0,0,0,0,2,0,0,0,5,0,0,0,67,0, + 0,0,115,126,0,0,0,124,0,106,0,100,1,107,8,114, + 14,100,2,110,4,124,0,106,0,125,1,124,0,106,1,100, + 1,107,8,114,74,124,0,106,2,100,1,107,8,114,52,100, + 3,124,1,155,2,100,4,157,3,83,0,100,3,124,1,155, + 2,100,5,124,0,106,2,155,2,100,6,157,5,83,0,110, + 48,124,0,106,3,114,100,100,3,124,1,155,2,100,7,124, + 0,106,1,155,2,100,4,157,5,83,0,100,3,124,0,106, + 0,155,2,100,5,124,0,106,1,155,0,100,6,157,5,83, + 0,100,1,83,0,41,8,122,38,82,101,116,117,114,110,32, + 116,104,101,32,114,101,112,114,32,116,111,32,117,115,101,32, + 102,111,114,32,116,104,101,32,109,111,100,117,108,101,46,78, + 114,100,0,0,0,114,101,0,0,0,114,102,0,0,0,114, + 103,0,0,0,114,104,0,0,0,114,105,0,0,0,41,4, + 114,17,0,0,0,114,115,0,0,0,114,111,0,0,0,114, + 127,0,0,0,41,2,114,95,0,0,0,114,17,0,0,0, + 114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114, + 109,0,0,0,53,2,0,0,115,16,0,0,0,0,3,20, + 1,10,1,10,1,12,2,22,2,6,1,20,2,114,109,0, + 0,0,99,2,0,0,0,0,0,0,0,0,0,0,0,4, + 0,0,0,10,0,0,0,67,0,0,0,115,206,0,0,0, + 124,0,106,0,125,2,116,1,124,2,131,1,143,182,1,0, + 116,2,106,3,160,4,124,2,161,1,124,1,107,9,114,56, + 100,1,124,2,155,2,100,2,157,3,125,3,116,5,124,3, + 124,2,100,3,141,2,130,1,122,106,124,0,106,7,100,4, + 107,8,114,108,124,0,106,8,100,4,107,8,114,92,116,5, + 100,5,124,0,106,0,100,3,141,2,130,1,116,9,124,0, + 124,1,100,6,100,7,141,3,1,0,110,52,116,9,124,0, + 124,1,100,6,100,7,141,3,1,0,116,10,124,0,106,7, + 100,8,131,2,115,148,124,0,106,7,160,11,124,2,161,1, + 1,0,110,12,124,0,106,7,160,12,124,1,161,1,1,0, + 87,0,53,0,116,2,106,3,160,6,124,0,106,0,161,1, + 125,1,124,1,116,2,106,3,124,0,106,0,60,0,88,0, + 87,0,53,0,81,0,82,0,88,0,124,1,83,0,41,9, + 122,70,69,120,101,99,117,116,101,32,116,104,101,32,115,112, + 101,99,39,115,32,115,112,101,99,105,102,105,101,100,32,109, + 111,100,117,108,101,32,105,110,32,97,110,32,101,120,105,115, + 116,105,110,103,32,109,111,100,117,108,101,39,115,32,110,97, + 109,101,115,112,97,99,101,46,122,7,109,111,100,117,108,101, + 32,122,19,32,110,111,116,32,105,110,32,115,121,115,46,109, + 111,100,117,108,101,115,114,16,0,0,0,78,250,14,109,105, + 115,115,105,110,103,32,108,111,97,100,101,114,84,114,147,0, + 0,0,114,154,0,0,0,41,13,114,17,0,0,0,114,49, + 0,0,0,114,15,0,0,0,114,92,0,0,0,114,34,0, + 0,0,114,79,0,0,0,218,3,112,111,112,114,111,0,0, + 0,114,118,0,0,0,114,152,0,0,0,114,4,0,0,0, + 218,11,108,111,97,100,95,109,111,100,117,108,101,114,154,0, + 0,0,41,4,114,95,0,0,0,114,96,0,0,0,114,17, + 0,0,0,218,3,109,115,103,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,114,93,0,0,0,70,2,0,0, + 115,34,0,0,0,0,2,6,1,10,1,16,1,12,1,12, + 1,2,1,10,1,10,1,14,2,16,2,14,1,12,4,14, + 2,16,4,14,1,24,1,114,93,0,0,0,99,1,0,0, + 0,0,0,0,0,0,0,0,0,2,0,0,0,8,0,0, + 0,67,0,0,0,115,26,1,0,0,122,18,124,0,106,0, + 160,1,124,0,106,2,161,1,1,0,87,0,110,52,1,0, + 1,0,1,0,124,0,106,2,116,3,106,4,107,6,114,64, + 116,3,106,4,160,5,124,0,106,2,161,1,125,1,124,1, + 116,3,106,4,124,0,106,2,60,0,130,0,89,0,110,2, + 88,0,116,3,106,4,160,5,124,0,106,2,161,1,125,1, + 124,1,116,3,106,4,124,0,106,2,60,0,116,6,124,1, + 100,1,100,0,131,3,100,0,107,8,114,148,122,12,124,0, + 106,0,124,1,95,7,87,0,110,20,4,0,116,8,107,10, + 114,146,1,0,1,0,1,0,89,0,110,2,88,0,116,6, + 124,1,100,2,100,0,131,3,100,0,107,8,114,226,122,40, + 124,1,106,9,124,1,95,10,116,11,124,1,100,3,131,2, + 115,202,124,0,106,2,160,12,100,4,161,1,100,5,25,0, + 124,1,95,10,87,0,110,20,4,0,116,8,107,10,114,224, + 1,0,1,0,1,0,89,0,110,2,88,0,116,6,124,1, + 100,6,100,0,131,3,100,0,107,8,144,1,114,22,122,10, + 124,0,124,1,95,13,87,0,110,22,4,0,116,8,107,10, + 144,1,114,20,1,0,1,0,1,0,89,0,110,2,88,0, + 124,1,83,0,41,7,78,114,98,0,0,0,114,149,0,0, + 0,114,145,0,0,0,114,132,0,0,0,114,22,0,0,0, + 114,107,0,0,0,41,14,114,111,0,0,0,114,159,0,0, + 0,114,17,0,0,0,114,15,0,0,0,114,92,0,0,0, + 114,158,0,0,0,114,6,0,0,0,114,98,0,0,0,114, + 108,0,0,0,114,1,0,0,0,114,149,0,0,0,114,4, + 0,0,0,114,133,0,0,0,114,107,0,0,0,114,155,0, + 0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0, + 0,218,25,95,108,111,97,100,95,98,97,99,107,119,97,114, + 100,95,99,111,109,112,97,116,105,98,108,101,100,2,0,0, + 115,54,0,0,0,0,4,2,1,18,1,6,1,12,1,14, + 1,12,1,8,3,14,1,12,1,16,1,2,1,12,1,14, + 1,6,1,16,1,2,4,8,1,10,1,22,1,14,1,6, + 1,18,1,2,1,10,1,16,1,6,1,114,161,0,0,0, + 99,1,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,11,0,0,0,67,0,0,0,115,220,0,0,0,124,0, + 106,0,100,0,107,9,114,30,116,1,124,0,106,0,100,1, + 131,2,115,30,116,2,124,0,131,1,83,0,116,3,124,0, + 131,1,125,1,100,2,124,0,95,4,122,162,124,1,116,5, + 106,6,124,0,106,7,60,0,122,52,124,0,106,0,100,0, + 107,8,114,96,124,0,106,8,100,0,107,8,114,108,116,9, + 100,4,124,0,106,7,100,5,141,2,130,1,110,12,124,0, + 106,0,160,10,124,1,161,1,1,0,87,0,110,50,1,0, + 1,0,1,0,122,14,116,5,106,6,124,0,106,7,61,0, + 87,0,110,20,4,0,116,11,107,10,114,152,1,0,1,0, + 1,0,89,0,110,2,88,0,130,0,89,0,110,2,88,0, + 116,5,106,6,160,12,124,0,106,7,161,1,125,1,124,1, + 116,5,106,6,124,0,106,7,60,0,116,13,100,6,124,0, + 106,7,124,0,106,0,131,3,1,0,87,0,53,0,100,3, + 124,0,95,4,88,0,124,1,83,0,41,7,78,114,154,0, + 0,0,84,70,114,157,0,0,0,114,16,0,0,0,122,18, + 105,109,112,111,114,116,32,123,33,114,125,32,35,32,123,33, + 114,125,41,14,114,111,0,0,0,114,4,0,0,0,114,161, + 0,0,0,114,156,0,0,0,90,13,95,105,110,105,116,105, + 97,108,105,122,105,110,103,114,15,0,0,0,114,92,0,0, + 0,114,17,0,0,0,114,118,0,0,0,114,79,0,0,0, + 114,154,0,0,0,114,62,0,0,0,114,158,0,0,0,114, + 76,0,0,0,114,155,0,0,0,114,10,0,0,0,114,10, + 0,0,0,114,11,0,0,0,218,14,95,108,111,97,100,95, + 117,110,108,111,99,107,101,100,137,2,0,0,115,46,0,0, + 0,0,2,10,2,12,1,8,2,8,5,6,1,2,1,12, + 1,2,1,10,1,10,1,16,3,16,1,6,1,2,1,14, + 1,14,1,6,1,8,5,14,1,12,1,20,2,8,2,114, + 162,0,0,0,99,1,0,0,0,0,0,0,0,0,0,0, + 0,1,0,0,0,10,0,0,0,67,0,0,0,115,42,0, + 0,0,116,0,124,0,106,1,131,1,143,22,1,0,116,2, + 124,0,131,1,87,0,2,0,53,0,81,0,82,0,163,0, + 83,0,81,0,82,0,88,0,100,1,83,0,41,2,122,191, + 82,101,116,117,114,110,32,97,32,110,101,119,32,109,111,100, + 117,108,101,32,111,98,106,101,99,116,44,32,108,111,97,100, + 101,100,32,98,121,32,116,104,101,32,115,112,101,99,39,115, + 32,108,111,97,100,101,114,46,10,10,32,32,32,32,84,104, + 101,32,109,111,100,117,108,101,32,105,115,32,110,111,116,32, + 97,100,100,101,100,32,116,111,32,105,116,115,32,112,97,114, + 101,110,116,46,10,10,32,32,32,32,73,102,32,97,32,109, + 111,100,117,108,101,32,105,115,32,97,108,114,101,97,100,121, + 32,105,110,32,115,121,115,46,109,111,100,117,108,101,115,44, + 32,116,104,97,116,32,101,120,105,115,116,105,110,103,32,109, + 111,100,117,108,101,32,103,101,116,115,10,32,32,32,32,99, + 108,111,98,98,101,114,101,100,46,10,10,32,32,32,32,78, + 41,3,114,49,0,0,0,114,17,0,0,0,114,162,0,0, + 0,41,1,114,95,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,114,94,0,0,0,179,2,0,0, + 115,4,0,0,0,0,9,12,1,114,94,0,0,0,99,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, + 0,0,0,64,0,0,0,115,140,0,0,0,101,0,90,1, + 100,0,90,2,100,1,90,3,100,2,90,4,101,5,100,3, + 100,4,132,0,131,1,90,6,101,7,100,20,100,6,100,7, + 132,1,131,1,90,8,101,7,100,21,100,8,100,9,132,1, + 131,1,90,9,101,7,100,10,100,11,132,0,131,1,90,10, + 101,7,100,12,100,13,132,0,131,1,90,11,101,7,101,12, + 100,14,100,15,132,0,131,1,131,1,90,13,101,7,101,12, + 100,16,100,17,132,0,131,1,131,1,90,14,101,7,101,12, + 100,18,100,19,132,0,131,1,131,1,90,15,101,7,101,16, + 131,1,90,17,100,5,83,0,41,22,218,15,66,117,105,108, + 116,105,110,73,109,112,111,114,116,101,114,122,144,77,101,116, + 97,32,112,97,116,104,32,105,109,112,111,114,116,32,102,111, + 114,32,98,117,105,108,116,45,105,110,32,109,111,100,117,108, + 101,115,46,10,10,32,32,32,32,65,108,108,32,109,101,116, + 104,111,100,115,32,97,114,101,32,101,105,116,104,101,114,32, + 99,108,97,115,115,32,111,114,32,115,116,97,116,105,99,32, + 109,101,116,104,111,100,115,32,116,111,32,97,118,111,105,100, + 32,116,104,101,32,110,101,101,100,32,116,111,10,32,32,32, + 32,105,110,115,116,97,110,116,105,97,116,101,32,116,104,101, + 32,99,108,97,115,115,46,10,10,32,32,32,32,122,8,98, + 117,105,108,116,45,105,110,99,1,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0,5,0,0,0,67,0,0,0, + 115,22,0,0,0,100,1,124,0,106,0,155,2,100,2,116, + 1,106,2,155,0,100,3,157,5,83,0,169,4,122,115,82, + 101,116,117,114,110,32,114,101,112,114,32,102,111,114,32,116, + 104,101,32,109,111,100,117,108,101,46,10,10,32,32,32,32, + 32,32,32,32,84,104,101,32,109,101,116,104,111,100,32,105, + 115,32,100,101,112,114,101,99,97,116,101,100,46,32,32,84, + 104,101,32,105,109,112,111,114,116,32,109,97,99,104,105,110, + 101,114,121,32,100,111,101,115,32,116,104,101,32,106,111,98, + 32,105,116,115,101,108,102,46,10,10,32,32,32,32,32,32, + 32,32,114,101,0,0,0,114,103,0,0,0,114,104,0,0, + 0,41,3,114,1,0,0,0,114,163,0,0,0,114,142,0, + 0,0,41,1,114,96,0,0,0,114,10,0,0,0,114,10, + 0,0,0,114,11,0,0,0,114,99,0,0,0,205,2,0, + 0,115,2,0,0,0,0,7,122,27,66,117,105,108,116,105, + 110,73,109,112,111,114,116,101,114,46,109,111,100,117,108,101, + 95,114,101,112,114,78,99,4,0,0,0,0,0,0,0,0, + 0,0,0,4,0,0,0,5,0,0,0,67,0,0,0,115, + 46,0,0,0,124,2,100,0,107,9,114,12,100,0,83,0, + 116,0,160,1,124,1,161,1,114,38,116,2,124,1,124,0, + 124,0,106,3,100,1,141,3,83,0,100,0,83,0,100,0, + 83,0,169,2,78,114,141,0,0,0,41,4,114,56,0,0, + 0,90,10,105,115,95,98,117,105,108,116,105,110,114,91,0, + 0,0,114,142,0,0,0,169,4,218,3,99,108,115,114,81, + 0,0,0,218,4,112,97,116,104,218,6,116,97,114,103,101, + 116,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0, + 218,9,102,105,110,100,95,115,112,101,99,214,2,0,0,115, + 10,0,0,0,0,2,8,1,4,1,10,1,16,2,122,25, + 66,117,105,108,116,105,110,73,109,112,111,114,116,101,114,46, + 102,105,110,100,95,115,112,101,99,99,3,0,0,0,0,0, + 0,0,0,0,0,0,4,0,0,0,4,0,0,0,67,0, + 0,0,115,30,0,0,0,124,0,160,0,124,1,124,2,161, + 2,125,3,124,3,100,1,107,9,114,26,124,3,106,1,83, + 0,100,1,83,0,41,2,122,175,70,105,110,100,32,116,104, + 101,32,98,117,105,108,116,45,105,110,32,109,111,100,117,108, + 101,46,10,10,32,32,32,32,32,32,32,32,73,102,32,39, + 112,97,116,104,39,32,105,115,32,101,118,101,114,32,115,112, + 101,99,105,102,105,101,100,32,116,104,101,110,32,116,104,101, + 32,115,101,97,114,99,104,32,105,115,32,99,111,110,115,105, + 100,101,114,101,100,32,97,32,102,97,105,108,117,114,101,46, + 10,10,32,32,32,32,32,32,32,32,84,104,105,115,32,109, + 101,116,104,111,100,32,105,115,32,100,101,112,114,101,99,97, + 116,101,100,46,32,32,85,115,101,32,102,105,110,100,95,115, + 112,101,99,40,41,32,105,110,115,116,101,97,100,46,10,10, + 32,32,32,32,32,32,32,32,78,41,2,114,170,0,0,0, + 114,111,0,0,0,41,4,114,167,0,0,0,114,81,0,0, + 0,114,168,0,0,0,114,95,0,0,0,114,10,0,0,0, + 114,10,0,0,0,114,11,0,0,0,218,11,102,105,110,100, + 95,109,111,100,117,108,101,223,2,0,0,115,4,0,0,0, + 0,9,12,1,122,27,66,117,105,108,116,105,110,73,109,112, + 111,114,116,101,114,46,102,105,110,100,95,109,111,100,117,108, + 101,99,2,0,0,0,0,0,0,0,0,0,0,0,2,0, + 0,0,4,0,0,0,67,0,0,0,115,46,0,0,0,124, + 1,106,0,116,1,106,2,107,7,114,34,116,3,124,1,106, + 0,155,2,100,1,157,2,124,1,106,0,100,2,141,2,130, + 1,116,4,116,5,106,6,124,1,131,2,83,0,41,3,122, + 24,67,114,101,97,116,101,32,97,32,98,117,105,108,116,45, + 105,110,32,109,111,100,117,108,101,114,77,0,0,0,114,16, + 0,0,0,41,7,114,17,0,0,0,114,15,0,0,0,114, + 78,0,0,0,114,79,0,0,0,114,66,0,0,0,114,56, + 0,0,0,90,14,99,114,101,97,116,101,95,98,117,105,108, + 116,105,110,41,2,114,30,0,0,0,114,95,0,0,0,114, + 10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,153, + 0,0,0,235,2,0,0,115,10,0,0,0,0,3,12,1, + 12,1,4,255,6,2,122,29,66,117,105,108,116,105,110,73, + 109,112,111,114,116,101,114,46,99,114,101,97,116,101,95,109, + 111,100,117,108,101,99,2,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,3,0,0,0,67,0,0,0,115,16, + 0,0,0,116,0,116,1,106,2,124,1,131,2,1,0,100, + 1,83,0,41,2,122,22,69,120,101,99,32,97,32,98,117, + 105,108,116,45,105,110,32,109,111,100,117,108,101,78,41,3, + 114,66,0,0,0,114,56,0,0,0,90,12,101,120,101,99, + 95,98,117,105,108,116,105,110,41,2,114,30,0,0,0,114, + 96,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11, + 0,0,0,114,154,0,0,0,243,2,0,0,115,2,0,0, + 0,0,3,122,27,66,117,105,108,116,105,110,73,109,112,111, + 114,116,101,114,46,101,120,101,99,95,109,111,100,117,108,101, + 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,1,0,0,0,67,0,0,0,115,4,0,0,0,100,1, + 83,0,41,2,122,57,82,101,116,117,114,110,32,78,111,110, + 101,32,97,115,32,98,117,105,108,116,45,105,110,32,109,111, + 100,117,108,101,115,32,100,111,32,110,111,116,32,104,97,118, + 101,32,99,111,100,101,32,111,98,106,101,99,116,115,46,78, + 114,10,0,0,0,169,2,114,167,0,0,0,114,81,0,0, + 0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0, + 218,8,103,101,116,95,99,111,100,101,248,2,0,0,115,2, + 0,0,0,0,4,122,24,66,117,105,108,116,105,110,73,109, + 112,111,114,116,101,114,46,103,101,116,95,99,111,100,101,99, + 2,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, + 1,0,0,0,67,0,0,0,115,4,0,0,0,100,1,83, + 0,41,2,122,56,82,101,116,117,114,110,32,78,111,110,101, + 32,97,115,32,98,117,105,108,116,45,105,110,32,109,111,100, + 117,108,101,115,32,100,111,32,110,111,116,32,104,97,118,101, + 32,115,111,117,114,99,101,32,99,111,100,101,46,78,114,10, + 0,0,0,114,172,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,218,10,103,101,116,95,115,111,117, + 114,99,101,254,2,0,0,115,2,0,0,0,0,4,122,26, + 66,117,105,108,116,105,110,73,109,112,111,114,116,101,114,46, + 103,101,116,95,115,111,117,114,99,101,99,2,0,0,0,0, + 0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,67, + 0,0,0,115,4,0,0,0,100,1,83,0,41,2,122,52, + 82,101,116,117,114,110,32,70,97,108,115,101,32,97,115,32, + 98,117,105,108,116,45,105,110,32,109,111,100,117,108,101,115, + 32,97,114,101,32,110,101,118,101,114,32,112,97,99,107,97, + 103,101,115,46,70,114,10,0,0,0,114,172,0,0,0,114, + 10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,117, + 0,0,0,4,3,0,0,115,2,0,0,0,0,4,122,26, + 66,117,105,108,116,105,110,73,109,112,111,114,116,101,114,46, + 105,115,95,112,97,99,107,97,103,101,41,2,78,78,41,1, + 78,41,18,114,1,0,0,0,114,0,0,0,0,114,2,0, + 0,0,114,3,0,0,0,114,142,0,0,0,218,12,115,116, + 97,116,105,99,109,101,116,104,111,100,114,99,0,0,0,218, + 11,99,108,97,115,115,109,101,116,104,111,100,114,170,0,0, + 0,114,171,0,0,0,114,153,0,0,0,114,154,0,0,0, + 114,86,0,0,0,114,173,0,0,0,114,174,0,0,0,114, + 117,0,0,0,114,97,0,0,0,114,159,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,114,163,0,0,0,194,2,0,0,115,44,0,0,0, + 8,2,4,7,4,2,2,1,10,8,2,1,12,8,2,1, + 12,11,2,1,10,7,2,1,10,4,2,1,2,1,12,4, + 2,1,2,1,12,4,2,1,2,1,12,4,114,163,0,0, + 0,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,4,0,0,0,64,0,0,0,115,144,0,0,0,101, + 0,90,1,100,0,90,2,100,1,90,3,100,2,90,4,101, + 5,100,3,100,4,132,0,131,1,90,6,101,7,100,22,100, + 6,100,7,132,1,131,1,90,8,101,7,100,23,100,8,100, + 9,132,1,131,1,90,9,101,7,100,10,100,11,132,0,131, + 1,90,10,101,5,100,12,100,13,132,0,131,1,90,11,101, + 7,100,14,100,15,132,0,131,1,90,12,101,7,101,13,100, + 16,100,17,132,0,131,1,131,1,90,14,101,7,101,13,100, + 18,100,19,132,0,131,1,131,1,90,15,101,7,101,13,100, + 20,100,21,132,0,131,1,131,1,90,16,100,5,83,0,41, + 24,218,14,70,114,111,122,101,110,73,109,112,111,114,116,101, + 114,122,142,77,101,116,97,32,112,97,116,104,32,105,109,112, + 111,114,116,32,102,111,114,32,102,114,111,122,101,110,32,109, + 111,100,117,108,101,115,46,10,10,32,32,32,32,65,108,108, + 32,109,101,116,104,111,100,115,32,97,114,101,32,101,105,116, + 104,101,114,32,99,108,97,115,115,32,111,114,32,115,116,97, + 116,105,99,32,109,101,116,104,111,100,115,32,116,111,32,97, + 118,111,105,100,32,116,104,101,32,110,101,101,100,32,116,111, + 10,32,32,32,32,105,110,115,116,97,110,116,105,97,116,101, + 32,116,104,101,32,99,108,97,115,115,46,10,10,32,32,32, + 32,90,6,102,114,111,122,101,110,99,1,0,0,0,0,0, + 0,0,0,0,0,0,1,0,0,0,5,0,0,0,67,0, + 0,0,115,22,0,0,0,100,1,124,0,106,0,155,2,100, + 2,116,1,106,2,155,0,100,3,157,5,83,0,114,164,0, + 0,0,41,3,114,1,0,0,0,114,177,0,0,0,114,142, + 0,0,0,41,1,218,1,109,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,114,99,0,0,0,24,3,0,0, + 115,2,0,0,0,0,7,122,26,70,114,111,122,101,110,73, + 109,112,111,114,116,101,114,46,109,111,100,117,108,101,95,114, + 101,112,114,78,99,4,0,0,0,0,0,0,0,0,0,0, + 0,4,0,0,0,5,0,0,0,67,0,0,0,115,34,0, + 0,0,116,0,160,1,124,1,161,1,114,26,116,2,124,1, + 124,0,124,0,106,3,100,1,141,3,83,0,100,0,83,0, + 100,0,83,0,114,165,0,0,0,41,4,114,56,0,0,0, + 114,88,0,0,0,114,91,0,0,0,114,142,0,0,0,114, + 166,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11, + 0,0,0,114,170,0,0,0,33,3,0,0,115,6,0,0, + 0,0,2,10,1,16,2,122,24,70,114,111,122,101,110,73, + 109,112,111,114,116,101,114,46,102,105,110,100,95,115,112,101, + 99,99,3,0,0,0,0,0,0,0,0,0,0,0,3,0, + 0,0,3,0,0,0,67,0,0,0,115,18,0,0,0,116, + 0,160,1,124,1,161,1,114,14,124,0,83,0,100,1,83, + 0,41,2,122,93,70,105,110,100,32,97,32,102,114,111,122, + 101,110,32,109,111,100,117,108,101,46,10,10,32,32,32,32, + 32,32,32,32,84,104,105,115,32,109,101,116,104,111,100,32, + 105,115,32,100,101,112,114,101,99,97,116,101,100,46,32,32, + 85,115,101,32,102,105,110,100,95,115,112,101,99,40,41,32, + 105,110,115,116,101,97,100,46,10,10,32,32,32,32,32,32, + 32,32,78,41,2,114,56,0,0,0,114,88,0,0,0,41, + 3,114,167,0,0,0,114,81,0,0,0,114,168,0,0,0, + 114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114, + 171,0,0,0,40,3,0,0,115,2,0,0,0,0,7,122, + 26,70,114,111,122,101,110,73,109,112,111,114,116,101,114,46, + 102,105,110,100,95,109,111,100,117,108,101,99,2,0,0,0, + 0,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0, + 67,0,0,0,115,4,0,0,0,100,1,83,0,41,2,122, + 42,85,115,101,32,100,101,102,97,117,108,116,32,115,101,109, + 97,110,116,105,99,115,32,102,111,114,32,109,111,100,117,108, + 101,32,99,114,101,97,116,105,111,110,46,78,114,10,0,0, + 0,41,2,114,167,0,0,0,114,95,0,0,0,114,10,0, + 0,0,114,10,0,0,0,114,11,0,0,0,114,153,0,0, + 0,49,3,0,0,115,2,0,0,0,0,2,122,28,70,114, + 111,122,101,110,73,109,112,111,114,116,101,114,46,99,114,101, + 97,116,101,95,109,111,100,117,108,101,99,1,0,0,0,0, + 0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,67, + 0,0,0,115,64,0,0,0,124,0,106,0,106,1,125,1, + 116,2,160,3,124,1,161,1,115,36,116,4,124,1,155,2, + 100,1,157,2,124,1,100,2,141,2,130,1,116,5,116,2, + 106,6,124,1,131,2,125,2,116,7,124,2,124,0,106,8, + 131,2,1,0,100,0,83,0,114,87,0,0,0,41,9,114, + 107,0,0,0,114,17,0,0,0,114,56,0,0,0,114,88, + 0,0,0,114,79,0,0,0,114,66,0,0,0,218,17,103, + 101,116,95,102,114,111,122,101,110,95,111,98,106,101,99,116, + 218,4,101,120,101,99,114,7,0,0,0,41,3,114,96,0, + 0,0,114,17,0,0,0,218,4,99,111,100,101,114,10,0, + 0,0,114,10,0,0,0,114,11,0,0,0,114,154,0,0, + 0,53,3,0,0,115,10,0,0,0,0,2,8,1,10,1, + 18,1,12,1,122,26,70,114,111,122,101,110,73,109,112,111, + 114,116,101,114,46,101,120,101,99,95,109,111,100,117,108,101, + 99,2,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,3,0,0,0,67,0,0,0,115,10,0,0,0,116,0, + 124,0,124,1,131,2,83,0,41,1,122,95,76,111,97,100, + 32,97,32,102,114,111,122,101,110,32,109,111,100,117,108,101, + 46,10,10,32,32,32,32,32,32,32,32,84,104,105,115,32, + 109,101,116,104,111,100,32,105,115,32,100,101,112,114,101,99, + 97,116,101,100,46,32,32,85,115,101,32,101,120,101,99,95, + 109,111,100,117,108,101,40,41,32,105,110,115,116,101,97,100, + 46,10,10,32,32,32,32,32,32,32,32,41,1,114,97,0, + 0,0,114,172,0,0,0,114,10,0,0,0,114,10,0,0, + 0,114,11,0,0,0,114,159,0,0,0,61,3,0,0,115, + 2,0,0,0,0,7,122,26,70,114,111,122,101,110,73,109, + 112,111,114,116,101,114,46,108,111,97,100,95,109,111,100,117, + 108,101,99,2,0,0,0,0,0,0,0,0,0,0,0,2, + 0,0,0,3,0,0,0,67,0,0,0,115,10,0,0,0, + 116,0,160,1,124,1,161,1,83,0,41,1,122,45,82,101, + 116,117,114,110,32,116,104,101,32,99,111,100,101,32,111,98, + 106,101,99,116,32,102,111,114,32,116,104,101,32,102,114,111, + 122,101,110,32,109,111,100,117,108,101,46,41,2,114,56,0, + 0,0,114,179,0,0,0,114,172,0,0,0,114,10,0,0, + 0,114,10,0,0,0,114,11,0,0,0,114,173,0,0,0, + 70,3,0,0,115,2,0,0,0,0,4,122,23,70,114,111, + 122,101,110,73,109,112,111,114,116,101,114,46,103,101,116,95, + 99,111,100,101,99,2,0,0,0,0,0,0,0,0,0,0, + 0,2,0,0,0,1,0,0,0,67,0,0,0,115,4,0, + 0,0,100,1,83,0,41,2,122,54,82,101,116,117,114,110, + 32,78,111,110,101,32,97,115,32,102,114,111,122,101,110,32, + 109,111,100,117,108,101,115,32,100,111,32,110,111,116,32,104, + 97,118,101,32,115,111,117,114,99,101,32,99,111,100,101,46, + 78,114,10,0,0,0,114,172,0,0,0,114,10,0,0,0, + 114,10,0,0,0,114,11,0,0,0,114,174,0,0,0,76, + 3,0,0,115,2,0,0,0,0,4,122,25,70,114,111,122, + 101,110,73,109,112,111,114,116,101,114,46,103,101,116,95,115, + 111,117,114,99,101,99,2,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,3,0,0,0,67,0,0,0,115,10, + 0,0,0,116,0,160,1,124,1,161,1,83,0,41,1,122, + 46,82,101,116,117,114,110,32,84,114,117,101,32,105,102,32, + 116,104,101,32,102,114,111,122,101,110,32,109,111,100,117,108, + 101,32,105,115,32,97,32,112,97,99,107,97,103,101,46,41, + 2,114,56,0,0,0,90,17,105,115,95,102,114,111,122,101, + 110,95,112,97,99,107,97,103,101,114,172,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,11,0,0,0,114,117,0, + 0,0,82,3,0,0,115,2,0,0,0,0,4,122,25,70, + 114,111,122,101,110,73,109,112,111,114,116,101,114,46,105,115, + 95,112,97,99,107,97,103,101,41,2,78,78,41,1,78,41, + 17,114,1,0,0,0,114,0,0,0,0,114,2,0,0,0, + 114,3,0,0,0,114,142,0,0,0,114,175,0,0,0,114, + 99,0,0,0,114,176,0,0,0,114,170,0,0,0,114,171, + 0,0,0,114,153,0,0,0,114,154,0,0,0,114,159,0, + 0,0,114,90,0,0,0,114,173,0,0,0,114,174,0,0, + 0,114,117,0,0,0,114,10,0,0,0,114,10,0,0,0, + 114,10,0,0,0,114,11,0,0,0,114,177,0,0,0,13, + 3,0,0,115,46,0,0,0,8,2,4,7,4,2,2,1, + 10,8,2,1,12,6,2,1,12,8,2,1,10,3,2,1, + 10,7,2,1,10,8,2,1,2,1,12,4,2,1,2,1, + 12,4,2,1,2,1,114,177,0,0,0,99,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, + 64,0,0,0,115,32,0,0,0,101,0,90,1,100,0,90, + 2,100,1,90,3,100,2,100,3,132,0,90,4,100,4,100, + 5,132,0,90,5,100,6,83,0,41,7,218,18,95,73,109, + 112,111,114,116,76,111,99,107,67,111,110,116,101,120,116,122, + 36,67,111,110,116,101,120,116,32,109,97,110,97,103,101,114, + 32,102,111,114,32,116,104,101,32,105,109,112,111,114,116,32, + 108,111,99,107,46,99,1,0,0,0,0,0,0,0,0,0, + 0,0,1,0,0,0,2,0,0,0,67,0,0,0,115,12, + 0,0,0,116,0,160,1,161,0,1,0,100,1,83,0,41, + 2,122,24,65,99,113,117,105,114,101,32,116,104,101,32,105, + 109,112,111,114,116,32,108,111,99,107,46,78,41,2,114,56, + 0,0,0,114,57,0,0,0,114,46,0,0,0,114,10,0, + 0,0,114,10,0,0,0,114,11,0,0,0,114,53,0,0, + 0,95,3,0,0,115,2,0,0,0,0,2,122,28,95,73, + 109,112,111,114,116,76,111,99,107,67,111,110,116,101,120,116, + 46,95,95,101,110,116,101,114,95,95,99,4,0,0,0,0, + 0,0,0,0,0,0,0,4,0,0,0,2,0,0,0,67, + 0,0,0,115,12,0,0,0,116,0,160,1,161,0,1,0, + 100,1,83,0,41,2,122,60,82,101,108,101,97,115,101,32, + 116,104,101,32,105,109,112,111,114,116,32,108,111,99,107,32, + 114,101,103,97,114,100,108,101,115,115,32,111,102,32,97,110, + 121,32,114,97,105,115,101,100,32,101,120,99,101,112,116,105, + 111,110,115,46,78,41,2,114,56,0,0,0,114,58,0,0, + 0,41,4,114,30,0,0,0,218,8,101,120,99,95,116,121, + 112,101,218,9,101,120,99,95,118,97,108,117,101,218,13,101, + 120,99,95,116,114,97,99,101,98,97,99,107,114,10,0,0, + 0,114,10,0,0,0,114,11,0,0,0,114,55,0,0,0, + 99,3,0,0,115,2,0,0,0,0,2,122,27,95,73,109, + 112,111,114,116,76,111,99,107,67,111,110,116,101,120,116,46, + 95,95,101,120,105,116,95,95,78,41,6,114,1,0,0,0, + 114,0,0,0,0,114,2,0,0,0,114,3,0,0,0,114, + 53,0,0,0,114,55,0,0,0,114,10,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,11,0,0,0,114,182,0, + 0,0,91,3,0,0,115,6,0,0,0,8,2,4,2,8, + 4,114,182,0,0,0,99,3,0,0,0,0,0,0,0,0, + 0,0,0,5,0,0,0,5,0,0,0,67,0,0,0,115, + 66,0,0,0,124,1,160,0,100,1,124,2,100,2,24,0, + 161,2,125,3,116,1,124,3,131,1,124,2,107,0,114,36, + 116,2,100,3,131,1,130,1,124,3,100,4,25,0,125,4, + 124,0,114,62,124,4,155,0,100,1,124,0,155,0,157,3, + 83,0,124,4,83,0,41,5,122,50,82,101,115,111,108,118, + 101,32,97,32,114,101,108,97,116,105,118,101,32,109,111,100, + 117,108,101,32,110,97,109,101,32,116,111,32,97,110,32,97, + 98,115,111,108,117,116,101,32,111,110,101,46,114,132,0,0, + 0,114,37,0,0,0,122,50,97,116,116,101,109,112,116,101, + 100,32,114,101,108,97,116,105,118,101,32,105,109,112,111,114, + 116,32,98,101,121,111,110,100,32,116,111,112,45,108,101,118, + 101,108,32,112,97,99,107,97,103,101,114,22,0,0,0,41, + 3,218,6,114,115,112,108,105,116,218,3,108,101,110,114,79, + 0,0,0,41,5,114,17,0,0,0,218,7,112,97,99,107, + 97,103,101,218,5,108,101,118,101,108,90,4,98,105,116,115, + 90,4,98,97,115,101,114,10,0,0,0,114,10,0,0,0, + 114,11,0,0,0,218,13,95,114,101,115,111,108,118,101,95, + 110,97,109,101,104,3,0,0,115,10,0,0,0,0,2,16, + 1,12,1,8,1,8,1,114,190,0,0,0,99,3,0,0, + 0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0, + 0,67,0,0,0,115,34,0,0,0,124,0,160,0,124,1, + 124,2,161,2,125,3,124,3,100,0,107,8,114,24,100,0, + 83,0,116,1,124,1,124,3,131,2,83,0,114,13,0,0, + 0,41,2,114,171,0,0,0,114,91,0,0,0,41,4,218, + 6,102,105,110,100,101,114,114,17,0,0,0,114,168,0,0, + 0,114,111,0,0,0,114,10,0,0,0,114,10,0,0,0, + 114,11,0,0,0,218,17,95,102,105,110,100,95,115,112,101, + 99,95,108,101,103,97,99,121,113,3,0,0,115,8,0,0, + 0,0,3,12,1,8,1,4,1,114,192,0,0,0,99,3, + 0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,10, + 0,0,0,67,0,0,0,115,12,1,0,0,116,0,106,1, + 125,3,124,3,100,1,107,8,114,22,116,2,100,2,131,1, + 130,1,124,3,115,38,116,3,160,4,100,3,116,5,161,2, + 1,0,124,0,116,0,106,6,107,6,125,4,124,3,68,0, + 93,210,125,5,116,7,131,0,143,84,1,0,122,10,124,5, + 106,8,125,6,87,0,110,54,4,0,116,9,107,10,114,128, + 1,0,1,0,1,0,116,10,124,5,124,0,124,1,131,3, + 125,7,124,7,100,1,107,8,114,124,89,0,87,0,53,0, + 81,0,82,0,163,0,113,52,89,0,110,14,88,0,124,6, + 124,0,124,1,124,2,131,3,125,7,87,0,53,0,81,0, + 82,0,88,0,124,7,100,1,107,9,114,52,124,4,144,0, + 115,254,124,0,116,0,106,6,107,6,144,0,114,254,116,0, + 106,6,124,0,25,0,125,8,122,10,124,8,106,11,125,9, + 87,0,110,28,4,0,116,9,107,10,114,226,1,0,1,0, + 1,0,124,7,6,0,89,0,2,0,1,0,83,0,88,0, + 124,9,100,1,107,8,114,244,124,7,2,0,1,0,83,0, + 124,9,2,0,1,0,83,0,113,52,124,7,2,0,1,0, + 83,0,113,52,100,1,83,0,41,4,122,21,70,105,110,100, + 32,97,32,109,111,100,117,108,101,39,115,32,115,112,101,99, + 46,78,122,53,115,121,115,46,109,101,116,97,95,112,97,116, + 104,32,105,115,32,78,111,110,101,44,32,80,121,116,104,111, + 110,32,105,115,32,108,105,107,101,108,121,32,115,104,117,116, + 116,105,110,103,32,100,111,119,110,122,22,115,121,115,46,109, + 101,116,97,95,112,97,116,104,32,105,115,32,101,109,112,116, + 121,41,12,114,15,0,0,0,218,9,109,101,116,97,95,112, + 97,116,104,114,79,0,0,0,218,9,95,119,97,114,110,105, + 110,103,115,218,4,119,97,114,110,218,13,73,109,112,111,114, + 116,87,97,114,110,105,110,103,114,92,0,0,0,114,182,0, + 0,0,114,170,0,0,0,114,108,0,0,0,114,192,0,0, + 0,114,107,0,0,0,41,10,114,17,0,0,0,114,168,0, + 0,0,114,169,0,0,0,114,193,0,0,0,90,9,105,115, + 95,114,101,108,111,97,100,114,191,0,0,0,114,170,0,0, + 0,114,95,0,0,0,114,96,0,0,0,114,107,0,0,0, + 114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,218, + 10,95,102,105,110,100,95,115,112,101,99,122,3,0,0,115, + 54,0,0,0,0,2,6,1,8,2,8,3,4,1,12,5, + 10,1,8,1,8,1,2,1,10,1,14,1,12,1,8,1, + 20,2,22,1,8,2,18,1,10,1,2,1,10,1,14,4, + 14,2,8,1,8,2,10,2,10,2,114,197,0,0,0,99, + 3,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0, + 4,0,0,0,67,0,0,0,115,108,0,0,0,116,0,124, + 0,116,1,131,2,115,28,116,2,100,1,116,3,124,0,131, + 1,155,0,157,2,131,1,130,1,124,2,100,2,107,0,114, + 44,116,4,100,3,131,1,130,1,124,2,100,2,107,4,114, + 84,116,0,124,1,116,1,131,2,115,72,116,2,100,4,131, + 1,130,1,110,12,124,1,115,84,116,5,100,5,131,1,130, + 1,124,0,115,104,124,2,100,2,107,2,114,104,116,4,100, + 6,131,1,130,1,100,7,83,0,41,8,122,28,86,101,114, + 105,102,121,32,97,114,103,117,109,101,110,116,115,32,97,114, + 101,32,34,115,97,110,101,34,46,122,29,109,111,100,117,108, + 101,32,110,97,109,101,32,109,117,115,116,32,98,101,32,115, + 116,114,44,32,110,111,116,32,114,22,0,0,0,122,18,108, + 101,118,101,108,32,109,117,115,116,32,98,101,32,62,61,32, + 48,122,31,95,95,112,97,99,107,97,103,101,95,95,32,110, + 111,116,32,115,101,116,32,116,111,32,97,32,115,116,114,105, + 110,103,122,54,97,116,116,101,109,112,116,101,100,32,114,101, + 108,97,116,105,118,101,32,105,109,112,111,114,116,32,119,105, + 116,104,32,110,111,32,107,110,111,119,110,32,112,97,114,101, + 110,116,32,112,97,99,107,97,103,101,122,17,69,109,112,116, + 121,32,109,111,100,117,108,101,32,110,97,109,101,78,41,6, + 218,10,105,115,105,110,115,116,97,110,99,101,218,3,115,116, + 114,218,9,84,121,112,101,69,114,114,111,114,114,14,0,0, + 0,218,10,86,97,108,117,101,69,114,114,111,114,114,79,0, + 0,0,169,3,114,17,0,0,0,114,188,0,0,0,114,189, + 0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,218,13,95,115,97,110,105,116,121,95,99,104,101,99, + 107,169,3,0,0,115,22,0,0,0,0,2,10,1,18,1, + 8,1,8,1,8,1,10,1,10,1,4,1,8,2,12,1, + 114,203,0,0,0,250,16,78,111,32,109,111,100,117,108,101, + 32,110,97,109,101,100,32,122,4,123,33,114,125,99,2,0, + 0,0,0,0,0,0,0,0,0,0,8,0,0,0,8,0, + 0,0,67,0,0,0,115,222,0,0,0,100,0,125,2,124, + 0,160,0,100,1,161,1,100,2,25,0,125,3,124,3,114, + 136,124,3,116,1,106,2,107,7,114,42,116,3,124,1,124, + 3,131,2,1,0,124,0,116,1,106,2,107,6,114,62,116, + 1,106,2,124,0,25,0,83,0,116,1,106,2,124,3,25, + 0,125,4,122,10,124,4,106,4,125,2,87,0,110,52,4, + 0,116,5,107,10,114,134,1,0,1,0,1,0,100,3,124, + 0,155,2,100,4,124,3,155,2,100,5,157,5,125,5,116, + 6,124,5,124,0,100,6,141,2,100,0,130,2,89,0,110, + 2,88,0,116,7,124,0,124,2,131,2,125,6,124,6,100, + 0,107,8,114,174,116,6,100,3,124,0,155,2,157,2,124, + 0,100,6,141,2,130,1,110,8,116,8,124,6,131,1,125, + 7,124,3,114,218,116,1,106,2,124,3,25,0,125,4,116, + 9,124,4,124,0,160,0,100,1,161,1,100,7,25,0,124, + 7,131,3,1,0,124,7,83,0,41,8,78,114,132,0,0, + 0,114,22,0,0,0,114,204,0,0,0,122,2,59,32,122, + 17,32,105,115,32,110,111,116,32,97,32,112,97,99,107,97, + 103,101,114,16,0,0,0,233,2,0,0,0,41,10,114,133, + 0,0,0,114,15,0,0,0,114,92,0,0,0,114,66,0, + 0,0,114,145,0,0,0,114,108,0,0,0,218,19,77,111, + 100,117,108,101,78,111,116,70,111,117,110,100,69,114,114,111, + 114,114,197,0,0,0,114,162,0,0,0,114,5,0,0,0, + 41,8,114,17,0,0,0,218,7,105,109,112,111,114,116,95, + 114,168,0,0,0,114,134,0,0,0,90,13,112,97,114,101, + 110,116,95,109,111,100,117,108,101,114,160,0,0,0,114,95, + 0,0,0,114,96,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,218,23,95,102,105,110,100,95,97, + 110,100,95,108,111,97,100,95,117,110,108,111,99,107,101,100, + 188,3,0,0,115,42,0,0,0,0,1,4,1,14,1,4, + 1,10,1,10,2,10,1,10,1,10,1,2,1,10,1,14, + 1,18,1,20,1,10,1,8,1,20,2,8,1,4,2,10, + 1,22,1,114,208,0,0,0,99,2,0,0,0,0,0,0, + 0,0,0,0,0,4,0,0,0,10,0,0,0,67,0,0, + 0,115,108,0,0,0,116,0,124,0,131,1,143,50,1,0, + 116,1,106,2,160,3,124,0,116,4,161,2,125,2,124,2, + 116,4,107,8,114,54,116,5,124,0,124,1,131,2,87,0, + 2,0,53,0,81,0,82,0,163,0,83,0,87,0,53,0, + 81,0,82,0,88,0,124,2,100,1,107,8,114,96,100,2, + 124,0,155,0,100,3,157,3,125,3,116,6,124,3,124,0, + 100,4,141,2,130,1,116,7,124,0,131,1,1,0,124,2, + 83,0,41,5,122,25,70,105,110,100,32,97,110,100,32,108, + 111,97,100,32,116,104,101,32,109,111,100,117,108,101,46,78, + 122,10,105,109,112,111,114,116,32,111,102,32,122,28,32,104, + 97,108,116,101,100,59,32,78,111,110,101,32,105,110,32,115, + 121,115,46,109,111,100,117,108,101,115,114,16,0,0,0,41, + 8,114,49,0,0,0,114,15,0,0,0,114,92,0,0,0, + 114,34,0,0,0,218,14,95,78,69,69,68,83,95,76,79, + 65,68,73,78,71,114,208,0,0,0,114,206,0,0,0,114, + 64,0,0,0,41,4,114,17,0,0,0,114,207,0,0,0, + 114,96,0,0,0,114,75,0,0,0,114,10,0,0,0,114, + 10,0,0,0,114,11,0,0,0,218,14,95,102,105,110,100, + 95,97,110,100,95,108,111,97,100,218,3,0,0,115,18,0, + 0,0,0,2,10,1,14,1,8,1,32,2,8,1,12,2, + 12,2,8,1,114,210,0,0,0,114,22,0,0,0,99,3, + 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,4, + 0,0,0,67,0,0,0,115,42,0,0,0,116,0,124,0, + 124,1,124,2,131,3,1,0,124,2,100,1,107,4,114,32, + 116,1,124,0,124,1,124,2,131,3,125,0,116,2,124,0, + 116,3,131,2,83,0,41,2,97,50,1,0,0,73,109,112, + 111,114,116,32,97,110,100,32,114,101,116,117,114,110,32,116, + 104,101,32,109,111,100,117,108,101,32,98,97,115,101,100,32, + 111,110,32,105,116,115,32,110,97,109,101,44,32,116,104,101, + 32,112,97,99,107,97,103,101,32,116,104,101,32,99,97,108, + 108,32,105,115,10,32,32,32,32,98,101,105,110,103,32,109, + 97,100,101,32,102,114,111,109,44,32,97,110,100,32,116,104, + 101,32,108,101,118,101,108,32,97,100,106,117,115,116,109,101, + 110,116,46,10,10,32,32,32,32,84,104,105,115,32,102,117, + 110,99,116,105,111,110,32,114,101,112,114,101,115,101,110,116, + 115,32,116,104,101,32,103,114,101,97,116,101,115,116,32,99, + 111,109,109,111,110,32,100,101,110,111,109,105,110,97,116,111, + 114,32,111,102,32,102,117,110,99,116,105,111,110,97,108,105, + 116,121,10,32,32,32,32,98,101,116,119,101,101,110,32,105, + 109,112,111,114,116,95,109,111,100,117,108,101,32,97,110,100, + 32,95,95,105,109,112,111,114,116,95,95,46,32,84,104,105, + 115,32,105,110,99,108,117,100,101,115,32,115,101,116,116,105, + 110,103,32,95,95,112,97,99,107,97,103,101,95,95,32,105, + 102,10,32,32,32,32,116,104,101,32,108,111,97,100,101,114, + 32,100,105,100,32,110,111,116,46,10,10,32,32,32,32,114, + 22,0,0,0,41,4,114,203,0,0,0,114,190,0,0,0, + 114,210,0,0,0,218,11,95,103,99,100,95,105,109,112,111, + 114,116,114,202,0,0,0,114,10,0,0,0,114,10,0,0, + 0,114,11,0,0,0,114,211,0,0,0,234,3,0,0,115, + 8,0,0,0,0,9,12,1,8,1,12,1,114,211,0,0, + 0,169,1,218,9,114,101,99,117,114,115,105,118,101,99,3, + 0,0,0,0,0,0,0,1,0,0,0,8,0,0,0,11, + 0,0,0,67,0,0,0,115,228,0,0,0,124,1,68,0, + 93,218,125,4,116,0,124,4,116,1,131,2,115,66,124,3, + 114,34,124,0,106,2,100,1,23,0,125,5,110,4,100,2, + 125,5,116,3,100,3,124,5,155,0,100,4,116,4,124,4, + 131,1,106,2,155,0,157,4,131,1,130,1,113,4,124,4, + 100,5,107,2,114,108,124,3,115,222,116,5,124,0,100,6, + 131,2,114,222,116,6,124,0,124,0,106,7,124,2,100,7, + 100,8,141,4,1,0,113,4,116,5,124,0,124,4,131,2, + 115,4,124,0,106,2,155,0,100,9,124,4,155,0,157,3, + 125,6,122,14,116,8,124,2,124,6,131,2,1,0,87,0, + 113,4,4,0,116,9,107,10,114,220,1,0,125,7,1,0, + 122,42,124,7,106,10,124,6,107,2,114,202,116,11,106,12, + 160,13,124,6,116,14,161,2,100,10,107,9,114,202,87,0, + 89,0,162,8,113,4,130,0,87,0,53,0,100,10,125,7, + 126,7,88,0,89,0,113,4,88,0,113,4,124,0,83,0, + 41,11,122,238,70,105,103,117,114,101,32,111,117,116,32,119, + 104,97,116,32,95,95,105,109,112,111,114,116,95,95,32,115, + 104,111,117,108,100,32,114,101,116,117,114,110,46,10,10,32, + 32,32,32,84,104,101,32,105,109,112,111,114,116,95,32,112, + 97,114,97,109,101,116,101,114,32,105,115,32,97,32,99,97, + 108,108,97,98,108,101,32,119,104,105,99,104,32,116,97,107, + 101,115,32,116,104,101,32,110,97,109,101,32,111,102,32,109, + 111,100,117,108,101,32,116,111,10,32,32,32,32,105,109,112, + 111,114,116,46,32,73,116,32,105,115,32,114,101,113,117,105, + 114,101,100,32,116,111,32,100,101,99,111,117,112,108,101,32, + 116,104,101,32,102,117,110,99,116,105,111,110,32,102,114,111, + 109,32,97,115,115,117,109,105,110,103,32,105,109,112,111,114, + 116,108,105,98,39,115,10,32,32,32,32,105,109,112,111,114, + 116,32,105,109,112,108,101,109,101,110,116,97,116,105,111,110, + 32,105,115,32,100,101,115,105,114,101,100,46,10,10,32,32, + 32,32,122,8,46,95,95,97,108,108,95,95,122,13,96,96, + 102,114,111,109,32,108,105,115,116,39,39,122,8,73,116,101, + 109,32,105,110,32,122,18,32,109,117,115,116,32,98,101,32, + 115,116,114,44,32,110,111,116,32,250,1,42,218,7,95,95, + 97,108,108,95,95,84,114,212,0,0,0,114,132,0,0,0, + 78,41,15,114,198,0,0,0,114,199,0,0,0,114,1,0, + 0,0,114,200,0,0,0,114,14,0,0,0,114,4,0,0, + 0,218,16,95,104,97,110,100,108,101,95,102,114,111,109,108, + 105,115,116,114,215,0,0,0,114,66,0,0,0,114,206,0, + 0,0,114,17,0,0,0,114,15,0,0,0,114,92,0,0, + 0,114,34,0,0,0,114,209,0,0,0,41,8,114,96,0, + 0,0,218,8,102,114,111,109,108,105,115,116,114,207,0,0, + 0,114,213,0,0,0,218,1,120,90,5,119,104,101,114,101, + 90,9,102,114,111,109,95,110,97,109,101,90,3,101,120,99, + 114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114, + 216,0,0,0,249,3,0,0,115,44,0,0,0,0,10,8, + 1,10,1,4,1,12,2,4,1,28,2,8,1,14,1,10, + 1,2,255,8,2,10,1,16,1,2,1,14,1,16,4,10, + 1,16,255,2,2,8,1,22,1,114,216,0,0,0,99,1, + 0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,6, + 0,0,0,67,0,0,0,115,146,0,0,0,124,0,160,0, + 100,1,161,1,125,1,124,0,160,0,100,2,161,1,125,2, + 124,1,100,3,107,9,114,82,124,2,100,3,107,9,114,78, + 124,1,124,2,106,1,107,3,114,78,116,2,106,3,100,4, + 124,1,155,2,100,5,124,2,106,1,155,2,100,6,157,5, + 116,4,100,7,100,8,141,3,1,0,124,1,83,0,124,2, + 100,3,107,9,114,96,124,2,106,1,83,0,116,2,106,3, + 100,9,116,4,100,7,100,8,141,3,1,0,124,0,100,10, + 25,0,125,1,100,11,124,0,107,7,114,142,124,1,160,5, + 100,12,161,1,100,13,25,0,125,1,124,1,83,0,41,14, + 122,167,67,97,108,99,117,108,97,116,101,32,119,104,97,116, + 32,95,95,112,97,99,107,97,103,101,95,95,32,115,104,111, + 117,108,100,32,98,101,46,10,10,32,32,32,32,95,95,112, + 97,99,107,97,103,101,95,95,32,105,115,32,110,111,116,32, + 103,117,97,114,97,110,116,101,101,100,32,116,111,32,98,101, + 32,100,101,102,105,110,101,100,32,111,114,32,99,111,117,108, + 100,32,98,101,32,115,101,116,32,116,111,32,78,111,110,101, + 10,32,32,32,32,116,111,32,114,101,112,114,101,115,101,110, + 116,32,116,104,97,116,32,105,116,115,32,112,114,111,112,101, + 114,32,118,97,108,117,101,32,105,115,32,117,110,107,110,111, + 119,110,46,10,10,32,32,32,32,114,149,0,0,0,114,107, + 0,0,0,78,122,32,95,95,112,97,99,107,97,103,101,95, + 95,32,33,61,32,95,95,115,112,101,99,95,95,46,112,97, + 114,101,110,116,32,40,122,4,32,33,61,32,114,122,0,0, + 0,233,3,0,0,0,41,1,90,10,115,116,97,99,107,108, + 101,118,101,108,122,89,99,97,110,39,116,32,114,101,115,111, + 108,118,101,32,112,97,99,107,97,103,101,32,102,114,111,109, + 32,95,95,115,112,101,99,95,95,32,111,114,32,95,95,112, + 97,99,107,97,103,101,95,95,44,32,102,97,108,108,105,110, + 103,32,98,97,99,107,32,111,110,32,95,95,110,97,109,101, + 95,95,32,97,110,100,32,95,95,112,97,116,104,95,95,114, + 1,0,0,0,114,145,0,0,0,114,132,0,0,0,114,22, + 0,0,0,41,6,114,34,0,0,0,114,134,0,0,0,114, + 194,0,0,0,114,195,0,0,0,114,196,0,0,0,114,133, + 0,0,0,41,3,218,7,103,108,111,98,97,108,115,114,188, + 0,0,0,114,95,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,11,0,0,0,218,17,95,99,97,108,99,95,95, + 95,112,97,99,107,97,103,101,95,95,30,4,0,0,115,38, + 0,0,0,0,7,10,1,10,1,8,1,18,1,22,2,2, + 0,2,254,6,3,4,1,8,1,6,2,6,2,2,0,2, + 254,6,3,8,1,8,1,14,1,114,221,0,0,0,114,10, + 0,0,0,99,5,0,0,0,0,0,0,0,0,0,0,0, + 9,0,0,0,5,0,0,0,67,0,0,0,115,180,0,0, + 0,124,4,100,1,107,2,114,18,116,0,124,0,131,1,125, + 5,110,36,124,1,100,2,107,9,114,30,124,1,110,2,105, + 0,125,6,116,1,124,6,131,1,125,7,116,0,124,0,124, + 7,124,4,131,3,125,5,124,3,115,150,124,4,100,1,107, + 2,114,84,116,0,124,0,160,2,100,3,161,1,100,1,25, + 0,131,1,83,0,124,0,115,92,124,5,83,0,116,3,124, + 0,131,1,116,3,124,0,160,2,100,3,161,1,100,1,25, + 0,131,1,24,0,125,8,116,4,106,5,124,5,106,6,100, + 2,116,3,124,5,106,6,131,1,124,8,24,0,133,2,25, + 0,25,0,83,0,110,26,116,7,124,5,100,4,131,2,114, + 172,116,8,124,5,124,3,116,0,131,3,83,0,124,5,83, + 0,100,2,83,0,41,5,97,215,1,0,0,73,109,112,111, + 114,116,32,97,32,109,111,100,117,108,101,46,10,10,32,32, + 32,32,84,104,101,32,39,103,108,111,98,97,108,115,39,32, + 97,114,103,117,109,101,110,116,32,105,115,32,117,115,101,100, + 32,116,111,32,105,110,102,101,114,32,119,104,101,114,101,32, + 116,104,101,32,105,109,112,111,114,116,32,105,115,32,111,99, + 99,117,114,114,105,110,103,32,102,114,111,109,10,32,32,32, + 32,116,111,32,104,97,110,100,108,101,32,114,101,108,97,116, + 105,118,101,32,105,109,112,111,114,116,115,46,32,84,104,101, + 32,39,108,111,99,97,108,115,39,32,97,114,103,117,109,101, + 110,116,32,105,115,32,105,103,110,111,114,101,100,46,32,84, + 104,101,10,32,32,32,32,39,102,114,111,109,108,105,115,116, + 39,32,97,114,103,117,109,101,110,116,32,115,112,101,99,105, + 102,105,101,115,32,119,104,97,116,32,115,104,111,117,108,100, + 32,101,120,105,115,116,32,97,115,32,97,116,116,114,105,98, + 117,116,101,115,32,111,110,32,116,104,101,32,109,111,100,117, + 108,101,10,32,32,32,32,98,101,105,110,103,32,105,109,112, + 111,114,116,101,100,32,40,101,46,103,46,32,96,96,102,114, + 111,109,32,109,111,100,117,108,101,32,105,109,112,111,114,116, + 32,60,102,114,111,109,108,105,115,116,62,96,96,41,46,32, + 32,84,104,101,32,39,108,101,118,101,108,39,10,32,32,32, + 32,97,114,103,117,109,101,110,116,32,114,101,112,114,101,115, + 101,110,116,115,32,116,104,101,32,112,97,99,107,97,103,101, + 32,108,111,99,97,116,105,111,110,32,116,111,32,105,109,112, + 111,114,116,32,102,114,111,109,32,105,110,32,97,32,114,101, + 108,97,116,105,118,101,10,32,32,32,32,105,109,112,111,114, + 116,32,40,101,46,103,46,32,96,96,102,114,111,109,32,46, + 46,112,107,103,32,105,109,112,111,114,116,32,109,111,100,96, + 96,32,119,111,117,108,100,32,104,97,118,101,32,97,32,39, + 108,101,118,101,108,39,32,111,102,32,50,41,46,10,10,32, + 32,32,32,114,22,0,0,0,78,114,132,0,0,0,114,145, + 0,0,0,41,9,114,211,0,0,0,114,221,0,0,0,218, + 9,112,97,114,116,105,116,105,111,110,114,187,0,0,0,114, + 15,0,0,0,114,92,0,0,0,114,1,0,0,0,114,4, + 0,0,0,114,216,0,0,0,41,9,114,17,0,0,0,114, + 220,0,0,0,218,6,108,111,99,97,108,115,114,217,0,0, + 0,114,189,0,0,0,114,96,0,0,0,90,8,103,108,111, + 98,97,108,115,95,114,188,0,0,0,90,7,99,117,116,95, + 111,102,102,114,10,0,0,0,114,10,0,0,0,114,11,0, + 0,0,218,10,95,95,105,109,112,111,114,116,95,95,57,4, + 0,0,115,30,0,0,0,0,11,8,1,10,2,16,1,8, + 1,12,1,4,3,8,1,18,1,4,1,4,4,26,3,32, + 1,10,1,12,2,114,224,0,0,0,99,1,0,0,0,0, + 0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,67, + 0,0,0,115,38,0,0,0,116,0,160,1,124,0,161,1, + 125,1,124,1,100,0,107,8,114,30,116,2,100,1,124,0, + 23,0,131,1,130,1,116,3,124,1,131,1,83,0,41,2, + 78,122,25,110,111,32,98,117,105,108,116,45,105,110,32,109, + 111,100,117,108,101,32,110,97,109,101,100,32,41,4,114,163, + 0,0,0,114,170,0,0,0,114,79,0,0,0,114,162,0, + 0,0,41,2,114,17,0,0,0,114,95,0,0,0,114,10, + 0,0,0,114,10,0,0,0,114,11,0,0,0,218,18,95, + 98,117,105,108,116,105,110,95,102,114,111,109,95,110,97,109, + 101,94,4,0,0,115,8,0,0,0,0,1,10,1,8,1, + 12,1,114,225,0,0,0,99,2,0,0,0,0,0,0,0, + 0,0,0,0,10,0,0,0,5,0,0,0,67,0,0,0, + 115,166,0,0,0,124,1,97,0,124,0,97,1,116,2,116, + 1,131,1,125,2,116,1,106,3,160,4,161,0,68,0,93, + 72,92,2,125,3,125,4,116,5,124,4,124,2,131,2,114, + 26,124,3,116,1,106,6,107,6,114,60,116,7,125,5,110, + 18,116,0,160,8,124,3,161,1,114,26,116,9,125,5,110, + 2,113,26,116,10,124,4,124,5,131,2,125,6,116,11,124, + 6,124,4,131,2,1,0,113,26,116,1,106,3,116,12,25, + 0,125,7,100,1,68,0,93,46,125,8,124,8,116,1,106, + 3,107,7,114,138,116,13,124,8,131,1,125,9,110,10,116, + 1,106,3,124,8,25,0,125,9,116,14,124,7,124,8,124, + 9,131,3,1,0,113,114,100,2,83,0,41,3,122,250,83, + 101,116,117,112,32,105,109,112,111,114,116,108,105,98,32,98, + 121,32,105,109,112,111,114,116,105,110,103,32,110,101,101,100, + 101,100,32,98,117,105,108,116,45,105,110,32,109,111,100,117, + 108,101,115,32,97,110,100,32,105,110,106,101,99,116,105,110, + 103,32,116,104,101,109,10,32,32,32,32,105,110,116,111,32, + 116,104,101,32,103,108,111,98,97,108,32,110,97,109,101,115, + 112,97,99,101,46,10,10,32,32,32,32,65,115,32,115,121, + 115,32,105,115,32,110,101,101,100,101,100,32,102,111,114,32, + 115,121,115,46,109,111,100,117,108,101,115,32,97,99,99,101, + 115,115,32,97,110,100,32,95,105,109,112,32,105,115,32,110, + 101,101,100,101,100,32,116,111,32,108,111,97,100,32,98,117, + 105,108,116,45,105,110,10,32,32,32,32,109,111,100,117,108, + 101,115,44,32,116,104,111,115,101,32,116,119,111,32,109,111, + 100,117,108,101,115,32,109,117,115,116,32,98,101,32,101,120, + 112,108,105,99,105,116,108,121,32,112,97,115,115,101,100,32, + 105,110,46,10,10,32,32,32,32,41,3,114,23,0,0,0, + 114,194,0,0,0,114,63,0,0,0,78,41,15,114,56,0, + 0,0,114,15,0,0,0,114,14,0,0,0,114,92,0,0, + 0,218,5,105,116,101,109,115,114,198,0,0,0,114,78,0, + 0,0,114,163,0,0,0,114,88,0,0,0,114,177,0,0, + 0,114,146,0,0,0,114,152,0,0,0,114,1,0,0,0, + 114,225,0,0,0,114,5,0,0,0,41,10,218,10,115,121, + 115,95,109,111,100,117,108,101,218,11,95,105,109,112,95,109, + 111,100,117,108,101,90,11,109,111,100,117,108,101,95,116,121, + 112,101,114,17,0,0,0,114,96,0,0,0,114,111,0,0, + 0,114,95,0,0,0,90,11,115,101,108,102,95,109,111,100, + 117,108,101,90,12,98,117,105,108,116,105,110,95,110,97,109, + 101,90,14,98,117,105,108,116,105,110,95,109,111,100,117,108, + 101,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0, + 218,6,95,115,101,116,117,112,101,4,0,0,115,36,0,0, + 0,0,9,4,1,4,3,8,1,18,1,10,1,10,1,6, + 1,10,1,6,2,2,1,10,1,12,3,10,1,8,1,10, + 1,10,2,10,1,114,229,0,0,0,99,2,0,0,0,0, + 0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,67, + 0,0,0,115,38,0,0,0,116,0,124,0,124,1,131,2, + 1,0,116,1,106,2,160,3,116,4,161,1,1,0,116,1, + 106,2,160,3,116,5,161,1,1,0,100,1,83,0,41,2, + 122,48,73,110,115,116,97,108,108,32,105,109,112,111,114,116, + 101,114,115,32,102,111,114,32,98,117,105,108,116,105,110,32, + 97,110,100,32,102,114,111,122,101,110,32,109,111,100,117,108, + 101,115,78,41,6,114,229,0,0,0,114,15,0,0,0,114, + 193,0,0,0,114,123,0,0,0,114,163,0,0,0,114,177, + 0,0,0,41,2,114,227,0,0,0,114,228,0,0,0,114, + 10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,8, + 95,105,110,115,116,97,108,108,136,4,0,0,115,6,0,0, + 0,0,2,10,2,12,1,114,230,0,0,0,99,0,0,0, + 0,0,0,0,0,0,0,0,0,1,0,0,0,4,0,0, + 0,67,0,0,0,115,32,0,0,0,100,1,100,2,108,0, + 125,0,124,0,97,1,124,0,160,2,116,3,106,4,116,5, + 25,0,161,1,1,0,100,2,83,0,41,3,122,57,73,110, + 115,116,97,108,108,32,105,109,112,111,114,116,101,114,115,32, + 116,104,97,116,32,114,101,113,117,105,114,101,32,101,120,116, + 101,114,110,97,108,32,102,105,108,101,115,121,115,116,101,109, + 32,97,99,99,101,115,115,114,22,0,0,0,78,41,6,218, + 26,95,102,114,111,122,101,110,95,105,109,112,111,114,116,108, + 105,98,95,101,120,116,101,114,110,97,108,114,130,0,0,0, + 114,230,0,0,0,114,15,0,0,0,114,92,0,0,0,114, + 1,0,0,0,41,1,114,231,0,0,0,114,10,0,0,0, + 114,10,0,0,0,114,11,0,0,0,218,27,95,105,110,115, + 116,97,108,108,95,101,120,116,101,114,110,97,108,95,105,109, + 112,111,114,116,101,114,115,144,4,0,0,115,6,0,0,0, + 0,3,8,1,4,1,114,232,0,0,0,41,2,78,78,41, + 1,78,41,2,78,114,22,0,0,0,41,4,78,78,114,10, + 0,0,0,114,22,0,0,0,41,50,114,3,0,0,0,114, + 130,0,0,0,114,12,0,0,0,114,18,0,0,0,114,59, + 0,0,0,114,33,0,0,0,114,42,0,0,0,114,19,0, + 0,0,114,20,0,0,0,114,48,0,0,0,114,49,0,0, + 0,114,52,0,0,0,114,64,0,0,0,114,66,0,0,0, + 114,76,0,0,0,114,86,0,0,0,114,90,0,0,0,114, + 97,0,0,0,114,113,0,0,0,114,114,0,0,0,114,91, + 0,0,0,114,146,0,0,0,114,152,0,0,0,114,156,0, + 0,0,114,109,0,0,0,114,93,0,0,0,114,161,0,0, + 0,114,162,0,0,0,114,94,0,0,0,114,163,0,0,0, + 114,177,0,0,0,114,182,0,0,0,114,190,0,0,0,114, + 192,0,0,0,114,197,0,0,0,114,203,0,0,0,90,15, + 95,69,82,82,95,77,83,71,95,80,82,69,70,73,88,90, + 8,95,69,82,82,95,77,83,71,114,208,0,0,0,218,6, + 111,98,106,101,99,116,114,209,0,0,0,114,210,0,0,0, + 114,211,0,0,0,114,216,0,0,0,114,221,0,0,0,114, + 224,0,0,0,114,225,0,0,0,114,229,0,0,0,114,230, + 0,0,0,114,232,0,0,0,114,10,0,0,0,114,10,0, + 0,0,114,10,0,0,0,114,11,0,0,0,218,8,60,109, + 111,100,117,108,101,62,1,0,0,0,115,94,0,0,0,4, + 24,4,2,8,8,8,8,4,2,4,3,16,4,14,68,14, + 21,14,16,8,37,8,17,8,11,14,8,8,11,8,12,8, + 16,8,36,14,100,16,26,10,45,14,72,8,17,8,17,8, + 30,8,37,8,42,8,15,14,75,14,78,14,13,8,9,8, + 9,10,47,8,16,4,1,8,2,8,27,6,3,8,16,10, + 15,14,37,8,27,10,37,8,7,8,35,8,8, +}; From 4f9e61072ea2a28f004af7474995a2bdb7dc2d55 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 6 Oct 2022 17:08:00 -0700 Subject: [PATCH 077/151] GH-91052: Add C API for watching dictionaries (GH-31787) --- Doc/c-api/dict.rst | 51 +++++++ Include/cpython/dictobject.h | 23 +++ Include/internal/pycore_dict.h | 27 +++- Include/internal/pycore_interp.h | 2 + Lib/test/test_capi.py | 132 +++++++++++++++++ ...2-10-03-16-12-39.gh-issue-91052.MsYL9d.rst | 1 + Modules/_testcapimodule.c | 140 ++++++++++++++++++ Objects/dictobject.c | 119 +++++++++++++-- Python/ceval.c | 5 +- Python/pystate.c | 4 + 10 files changed, 487 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-03-16-12-39.gh-issue-91052.MsYL9d.rst diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 819168d48707c1..7bebea0c97de5a 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -238,3 +238,54 @@ Dictionary Objects for key, value in seq2: if override or key not in a: a[key] = value + +.. c:function:: int PyDict_AddWatcher(PyDict_WatchCallback callback) + + Register *callback* as a dictionary watcher. Return a non-negative integer + id which must be passed to future calls to :c:func:`PyDict_Watch`. In case + of error (e.g. no more watcher IDs available), return ``-1`` and set an + exception. + +.. c:function:: int PyDict_ClearWatcher(int watcher_id) + + Clear watcher identified by *watcher_id* previously returned from + :c:func:`PyDict_AddWatcher`. Return ``0`` on success, ``-1`` on error (e.g. + if the given *watcher_id* was never registered.) + +.. c:function:: int PyDict_Watch(int watcher_id, PyObject *dict) + + Mark dictionary *dict* as watched. The callback granted *watcher_id* by + :c:func:`PyDict_AddWatcher` will be called when *dict* is modified or + deallocated. + +.. c:type:: PyDict_WatchEvent + + Enumeration of possible dictionary watcher events: ``PyDict_EVENT_ADDED``, + ``PyDict_EVENT_MODIFIED``, ``PyDict_EVENT_DELETED``, ``PyDict_EVENT_CLONED``, + ``PyDict_EVENT_CLEARED``, or ``PyDict_EVENT_DEALLOCATED``. + +.. c:type:: int (*PyDict_WatchCallback)(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value) + + Type of a dict watcher callback function. + + If *event* is ``PyDict_EVENT_CLEARED`` or ``PyDict_EVENT_DEALLOCATED``, both + *key* and *new_value* will be ``NULL``. If *event* is ``PyDict_EVENT_ADDED`` + or ``PyDict_EVENT_MODIFIED``, *new_value* will be the new value for *key*. + If *event* is ``PyDict_EVENT_DELETED``, *key* is being deleted from the + dictionary and *new_value* will be ``NULL``. + + ``PyDict_EVENT_CLONED`` occurs when *dict* was previously empty and another + dict is merged into it. To maintain efficiency of this operation, per-key + ``PyDict_EVENT_ADDED`` events are not issued in this case; instead a + single ``PyDict_EVENT_CLONED`` is issued, and *key* will be the source + dictionary. + + The callback may inspect but must not modify *dict*; doing so could have + unpredictable effects, including infinite recursion. + + Callbacks occur before the notified modification to *dict* takes place, so + the prior state of *dict* can be inspected. + + If the callback returns with an exception set, it must return ``-1``; this + exception will be printed as an unraisable exception using + :c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``. diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 565ad791a6cb28..f8a74a597b0ea2 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -83,3 +83,26 @@ typedef struct { PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *); PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other); + +/* Dictionary watchers */ + +typedef enum { + PyDict_EVENT_ADDED, + PyDict_EVENT_MODIFIED, + PyDict_EVENT_DELETED, + PyDict_EVENT_CLONED, + PyDict_EVENT_CLEARED, + PyDict_EVENT_DEALLOCATED, +} PyDict_WatchEvent; + +// Callback to be invoked when a watched dict is cleared, dealloced, or modified. +// In clear/dealloc case, key and new_value will be NULL. Otherwise, new_value will be the +// new value for key, NULL if key is being deleted. +typedef int(*PyDict_WatchCallback)(PyDict_WatchEvent event, PyObject* dict, PyObject* key, PyObject* new_value); + +// Register/unregister a dict-watcher callback +PyAPI_FUNC(int) PyDict_AddWatcher(PyDict_WatchCallback callback); +PyAPI_FUNC(int) PyDict_ClearWatcher(int watcher_id); + +// Mark given dictionary as "watched" (callback will be called if it is modified) +PyAPI_FUNC(int) PyDict_Watch(int watcher_id, PyObject* dict); diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 464092996cae00..ae4094a095d879 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -154,7 +154,32 @@ struct _dictvalues { extern uint64_t _pydict_global_version; -#define DICT_NEXT_VERSION() (++_pydict_global_version) +#define DICT_MAX_WATCHERS 8 +#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS) +#define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1) + +#define DICT_NEXT_VERSION() (_pydict_global_version += DICT_VERSION_INCREMENT) + +void +_PyDict_SendEvent(int watcher_bits, + PyDict_WatchEvent event, + PyDictObject *mp, + PyObject *key, + PyObject *value); + +static inline uint64_t +_PyDict_NotifyEvent(PyDict_WatchEvent event, + PyDictObject *mp, + PyObject *key, + PyObject *value) +{ + int watcher_bits = mp->ma_version_tag & DICT_VERSION_MASK; + if (watcher_bits) { + _PyDict_SendEvent(watcher_bits, event, mp, key, value); + return DICT_NEXT_VERSION() | watcher_bits; + } + return DICT_NEXT_VERSION(); +} extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values); extern PyObject *_PyDict_FromItems( diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index b22cfb7121d549..812101c532c7f6 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -144,6 +144,8 @@ struct _is { // Initialized to _PyEval_EvalFrameDefault(). _PyFrameEvalFunction eval_frame; + PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS]; + Py_ssize_t co_extra_user_count; freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 2c6fe34d3b788c..cb90d55941cae7 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -2,6 +2,7 @@ # these are all functions _testcapi exports whose name begins with 'test_'. from collections import OrderedDict +from contextlib import contextmanager import _thread import importlib.machinery import importlib.util @@ -1393,5 +1394,136 @@ def func2(x=None): self.do_test(func2) +class TestDictWatchers(unittest.TestCase): + # types of watchers testcapimodule can add: + EVENTS = 0 # appends dict events as strings to global event list + ERROR = 1 # unconditionally sets and signals a RuntimeException + SECOND = 2 # always appends "second" to global event list + + def add_watcher(self, kind=EVENTS): + return _testcapi.add_dict_watcher(kind) + + def clear_watcher(self, watcher_id): + _testcapi.clear_dict_watcher(watcher_id) + + @contextmanager + def watcher(self, kind=EVENTS): + wid = self.add_watcher(kind) + try: + yield wid + finally: + self.clear_watcher(wid) + + def assert_events(self, expected): + actual = _testcapi.get_dict_watcher_events() + self.assertEqual(actual, expected) + + def watch(self, wid, d): + _testcapi.watch_dict(wid, d) + + def test_set_new_item(self): + d = {} + with self.watcher() as wid: + self.watch(wid, d) + d["foo"] = "bar" + self.assert_events(["new:foo:bar"]) + + def test_set_existing_item(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d["foo"] = "baz" + self.assert_events(["mod:foo:baz"]) + + def test_clone(self): + d = {} + d2 = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d.update(d2) + self.assert_events(["clone"]) + + def test_no_event_if_not_watched(self): + d = {} + with self.watcher() as wid: + d["foo"] = "bar" + self.assert_events([]) + + def test_del(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + del d["foo"] + self.assert_events(["del:foo"]) + + def test_pop(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d.pop("foo") + self.assert_events(["del:foo"]) + + def test_clear(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d.clear() + self.assert_events(["clear"]) + + def test_dealloc(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + del d + self.assert_events(["dealloc"]) + + def test_error(self): + d = {} + unraisables = [] + def unraisable_hook(unraisable): + unraisables.append(unraisable) + with self.watcher(kind=self.ERROR) as wid: + self.watch(wid, d) + orig_unraisable_hook = sys.unraisablehook + sys.unraisablehook = unraisable_hook + try: + d["foo"] = "bar" + finally: + sys.unraisablehook = orig_unraisable_hook + self.assert_events([]) + self.assertEqual(len(unraisables), 1) + unraisable = unraisables[0] + self.assertIs(unraisable.object, d) + self.assertEqual(str(unraisable.exc_value), "boom!") + + def test_two_watchers(self): + d1 = {} + d2 = {} + with self.watcher() as wid1: + with self.watcher(kind=self.SECOND) as wid2: + self.watch(wid1, d1) + self.watch(wid2, d2) + d1["foo"] = "bar" + d2["hmm"] = "baz" + self.assert_events(["new:foo:bar", "second"]) + + def test_watch_non_dict(self): + with self.watcher() as wid: + with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): + self.watch(wid, 1) + + def test_watch_out_of_range_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): + self.watch(-1, d) + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): + self.watch(8, d) # DICT_MAX_WATCHERS = 8 + + def test_unassigned_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): + self.watch(1, d) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-03-16-12-39.gh-issue-91052.MsYL9d.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-03-16-12-39.gh-issue-91052.MsYL9d.rst new file mode 100644 index 00000000000000..c7db4da494fe68 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-03-16-12-39.gh-issue-91052.MsYL9d.rst @@ -0,0 +1 @@ +Add API for subscribing to modification events on selected dictionaries. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index add89d12650d74..fd5734cedae9a9 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5169,6 +5169,142 @@ test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args)) } +// Test dict watching +static PyObject *g_dict_watch_events; +static int g_dict_watchers_installed; + +static int +dict_watch_callback(PyDict_WatchEvent event, + PyObject *dict, + PyObject *key, + PyObject *new_value) +{ + PyObject *msg; + switch(event) { + case PyDict_EVENT_CLEARED: + msg = PyUnicode_FromString("clear"); + break; + case PyDict_EVENT_DEALLOCATED: + msg = PyUnicode_FromString("dealloc"); + break; + case PyDict_EVENT_CLONED: + msg = PyUnicode_FromString("clone"); + break; + case PyDict_EVENT_ADDED: + msg = PyUnicode_FromFormat("new:%S:%S", key, new_value); + break; + case PyDict_EVENT_MODIFIED: + msg = PyUnicode_FromFormat("mod:%S:%S", key, new_value); + break; + case PyDict_EVENT_DELETED: + msg = PyUnicode_FromFormat("del:%S", key); + break; + default: + msg = PyUnicode_FromString("unknown"); + } + if (!msg) { + return -1; + } + assert(PyList_Check(g_dict_watch_events)); + if (PyList_Append(g_dict_watch_events, msg) < 0) { + Py_DECREF(msg); + return -1; + } + return 0; +} + +static int +dict_watch_callback_second(PyDict_WatchEvent event, + PyObject *dict, + PyObject *key, + PyObject *new_value) +{ + PyObject *msg = PyUnicode_FromString("second"); + if (!msg) { + return -1; + } + if (PyList_Append(g_dict_watch_events, msg) < 0) { + return -1; + } + return 0; +} + +static int +dict_watch_callback_error(PyDict_WatchEvent event, + PyObject *dict, + PyObject *key, + PyObject *new_value) +{ + PyErr_SetString(PyExc_RuntimeError, "boom!"); + return -1; +} + +static PyObject * +add_dict_watcher(PyObject *self, PyObject *kind) +{ + int watcher_id; + assert(PyLong_Check(kind)); + long kind_l = PyLong_AsLong(kind); + if (kind_l == 2) { + watcher_id = PyDict_AddWatcher(dict_watch_callback_second); + } else if (kind_l == 1) { + watcher_id = PyDict_AddWatcher(dict_watch_callback_error); + } else { + watcher_id = PyDict_AddWatcher(dict_watch_callback); + } + if (watcher_id < 0) { + return NULL; + } + if (!g_dict_watchers_installed) { + assert(!g_dict_watch_events); + if (!(g_dict_watch_events = PyList_New(0))) { + return NULL; + } + } + g_dict_watchers_installed++; + return PyLong_FromLong(watcher_id); +} + +static PyObject * +clear_dict_watcher(PyObject *self, PyObject *watcher_id) +{ + if (PyDict_ClearWatcher(PyLong_AsLong(watcher_id))) { + return NULL; + } + g_dict_watchers_installed--; + if (!g_dict_watchers_installed) { + assert(g_dict_watch_events); + Py_CLEAR(g_dict_watch_events); + } + Py_RETURN_NONE; +} + +static PyObject * +watch_dict(PyObject *self, PyObject *args) +{ + PyObject *dict; + int watcher_id; + if (!PyArg_ParseTuple(args, "iO", &watcher_id, &dict)) { + return NULL; + } + if (PyDict_Watch(watcher_id, dict)) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +get_dict_watcher_events(PyObject *self, PyObject *Py_UNUSED(args)) +{ + if (!g_dict_watch_events) { + PyErr_SetString(PyExc_RuntimeError, "no watchers active"); + return NULL; + } + Py_INCREF(g_dict_watch_events); + return g_dict_watch_events; +} + + // Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8() static PyObject * test_float_pack(PyObject *self, PyObject *args) @@ -5762,6 +5898,10 @@ static PyMethodDef TestMethods[] = { {"settrace_to_record", settrace_to_record, METH_O, NULL}, {"test_macros", test_macros, METH_NOARGS, NULL}, {"clear_managed_dict", clear_managed_dict, METH_O, NULL}, + {"add_dict_watcher", add_dict_watcher, METH_O, NULL}, + {"clear_dict_watcher", clear_dict_watcher, METH_O, NULL}, + {"watch_dict", watch_dict, METH_VARARGS, NULL}, + {"get_dict_watcher_events", get_dict_watcher_events, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index fecdfa86370193..6542b1803ffa2e 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1240,6 +1240,7 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) MAINTAIN_TRACKING(mp, key, value); if (ix == DKIX_EMPTY) { + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); /* Insert into new slot. */ mp->ma_keys->dk_version = 0; assert(old_value == NULL); @@ -1274,7 +1275,7 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) ep->me_value = value; } mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); @@ -1283,6 +1284,7 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) } if (old_value != value) { + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, mp, key, value); if (_PyDict_HasSplitTable(mp)) { mp->ma_values->values[ix] = value; if (old_value == NULL) { @@ -1299,7 +1301,7 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) DK_ENTRIES(mp->ma_keys)[ix].me_value = value; } } - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; } Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ ASSERT_CONSISTENT(mp); @@ -1320,6 +1322,8 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, { assert(mp->ma_keys == Py_EMPTY_KEYS); + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); + int unicode = PyUnicode_CheckExact(key); PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE, unicode); if (newkeys == NULL) { @@ -1347,7 +1351,7 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, ep->me_value = value; } mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; return 0; @@ -1910,7 +1914,7 @@ delete_index_from_values(PyDictValues *values, Py_ssize_t ix) static int delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, - PyObject *old_value) + PyObject *old_value, uint64_t new_version) { PyObject *old_key; @@ -1918,7 +1922,7 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, assert(hashpos >= 0); mp->ma_used--; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; if (mp->ma_values) { assert(old_value == mp->ma_values->values[ix]); mp->ma_values->values[ix] = NULL; @@ -1987,7 +1991,8 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) return -1; } - return delitem_common(mp, hash, ix, old_value); + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); + return delitem_common(mp, hash, ix, old_value, new_version); } /* This function promises that the predicate -> deletion sequence is atomic @@ -2028,10 +2033,12 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key, hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); - if (res > 0) - return delitem_common(mp, hashpos, ix, old_value); - else + if (res > 0) { + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); + return delitem_common(mp, hashpos, ix, old_value, new_version); + } else { return 0; + } } @@ -2052,11 +2059,12 @@ PyDict_Clear(PyObject *op) return; } /* Empty the dict... */ + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_CLEARED, mp, NULL, NULL); dictkeys_incref(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; mp->ma_values = NULL; mp->ma_used = 0; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; /* ...then clear the keys and values */ if (oldvalues != NULL) { n = oldkeys->dk_nentries; @@ -2196,7 +2204,8 @@ _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *d } assert(old_value != NULL); Py_INCREF(old_value); - delitem_common(mp, hash, ix, old_value); + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); + delitem_common(mp, hash, ix, old_value, new_version); ASSERT_CONSISTENT(mp); return old_value; @@ -2321,6 +2330,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) static void dict_dealloc(PyDictObject *mp) { + _PyDict_NotifyEvent(PyDict_EVENT_DEALLOCATED, mp, NULL, NULL); PyDictValues *values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; @@ -2809,6 +2819,7 @@ dict_merge(PyObject *a, PyObject *b, int override) other->ma_used == okeys->dk_nentries && (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)) { + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_CLONED, mp, b, NULL); PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; @@ -2822,7 +2833,7 @@ dict_merge(PyObject *a, PyObject *b, int override) } mp->ma_used = other->ma_used; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { @@ -3294,6 +3305,7 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) return NULL; if (ix == DKIX_EMPTY) { + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, defaultobj); mp->ma_keys->dk_version = 0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { @@ -3328,12 +3340,13 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); } else if (value == NULL) { + uint64_t new_version = _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, defaultobj); value = defaultobj; assert(_PyDict_HasSplitTable(mp)); assert(mp->ma_values->values[ix] == NULL); @@ -3342,7 +3355,7 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) mp->ma_values->values[ix] = value; _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); mp->ma_used++; - mp->ma_version_tag = DICT_NEXT_VERSION(); + mp->ma_version_tag = new_version; } ASSERT_CONSISTENT(mp); @@ -3415,6 +3428,7 @@ dict_popitem_impl(PyDictObject *self) { Py_ssize_t i, j; PyObject *res; + uint64_t new_version; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which @@ -3454,6 +3468,7 @@ dict_popitem_impl(PyDictObject *self) assert(i >= 0); key = ep0[i].me_key; + new_version = _PyDict_NotifyEvent(PyDict_EVENT_DELETED, self, key, NULL); hash = unicode_get_hash(key); value = ep0[i].me_value; ep0[i].me_key = NULL; @@ -3468,6 +3483,7 @@ dict_popitem_impl(PyDictObject *self) assert(i >= 0); key = ep0[i].me_key; + new_version = _PyDict_NotifyEvent(PyDict_EVENT_DELETED, self, key, NULL); hash = ep0[i].me_hash; value = ep0[i].me_value; ep0[i].me_key = NULL; @@ -3485,7 +3501,7 @@ dict_popitem_impl(PyDictObject *self) /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ self->ma_keys->dk_nentries = i; self->ma_used--; - self->ma_version_tag = DICT_NEXT_VERSION(); + self->ma_version_tag = new_version; ASSERT_CONSISTENT(self); return res; } @@ -5703,3 +5719,76 @@ uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictKeysObject *dictkeys) dictkeys->dk_version = v; return v; } + +int +PyDict_Watch(int watcher_id, PyObject* dict) +{ + if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_ValueError, "Cannot watch non-dictionary"); + return -1; + } + if (watcher_id < 0 || watcher_id >= DICT_MAX_WATCHERS) { + PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); + return -1; + } + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (!interp->dict_watchers[watcher_id]) { + PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); + return -1; + } + ((PyDictObject*)dict)->ma_version_tag |= (1LL << watcher_id); + return 0; +} + +int +PyDict_AddWatcher(PyDict_WatchCallback callback) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + + for (int i = 0; i < DICT_MAX_WATCHERS; i++) { + if (!interp->dict_watchers[i]) { + interp->dict_watchers[i] = callback; + return i; + } + } + + PyErr_SetString(PyExc_RuntimeError, "no more dict watcher IDs available"); + return -1; +} + +int +PyDict_ClearWatcher(int watcher_id) +{ + if (watcher_id < 0 || watcher_id >= DICT_MAX_WATCHERS) { + PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); + return -1; + } + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (!interp->dict_watchers[watcher_id]) { + PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); + return -1; + } + interp->dict_watchers[watcher_id] = NULL; + return 0; +} + +void +_PyDict_SendEvent(int watcher_bits, + PyDict_WatchEvent event, + PyDictObject *mp, + PyObject *key, + PyObject *value) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + for (int i = 0; i < DICT_MAX_WATCHERS; i++) { + if (watcher_bits & 1) { + PyDict_WatchCallback cb = interp->dict_watchers[i]; + if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) { + // some dict modification paths (e.g. PyDict_Clear) can't raise, so we + // can't propagate exceptions from dict watchers. + PyErr_WriteUnraisable((PyObject *)mp); + } + } + watcher_bits >>= 1; + } +} diff --git a/Python/ceval.c b/Python/ceval.c index c08c794005d1ab..ee1babaaf44425 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3252,6 +3252,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int uint16_t hint = cache->index; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); PyObject *value, *old_value; + uint64_t new_version; if (DK_IS_UNICODE(dict->ma_keys)) { PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; DEOPT_IF(ep->me_key != name, STORE_ATTR); @@ -3259,6 +3260,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DEOPT_IF(old_value == NULL, STORE_ATTR); STACK_SHRINK(1); value = POP(); + new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value); ep->me_value = value; } else { @@ -3268,6 +3270,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DEOPT_IF(old_value == NULL, STORE_ATTR); STACK_SHRINK(1); value = POP(); + new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value); ep->me_value = value; } Py_DECREF(old_value); @@ -3277,7 +3280,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyObject_GC_TRACK(dict); } /* PEP 509 */ - dict->ma_version_tag = DICT_NEXT_VERSION(); + dict->ma_version_tag = new_version; Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); DISPATCH(); diff --git a/Python/pystate.c b/Python/pystate.c index 04c8624cd35123..42a8d073f1bbd3 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -452,6 +452,10 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->sysdict); Py_CLEAR(interp->builtins); + for (int i=0; i < DICT_MAX_WATCHERS; i++) { + interp->dict_watchers[i] = NULL; + } + // XXX Once we have one allocator per interpreter (i.e. // per-interpreter GC) we must ensure that all of the interpreter's // objects have been cleaned up at the point. From 395bc9a08c5b2fe33966fb26977c39a009a1ea85 Mon Sep 17 00:00:00 2001 From: Tiger Date: Thu, 6 Oct 2022 19:11:59 -0500 Subject: [PATCH 078/151] bpo-35540 dataclasses.asdict now supports defaultdict fields (gh-32056) --- Lib/dataclasses.py | 8 +++++++ Lib/test/test_dataclasses.py | 21 +++++++++++++++++-- .../2022-03-22-18-28-55.bpo-35540.nyijX9.rst | 1 + 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-03-22-18-28-55.bpo-35540.nyijX9.rst diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 65fb8f251861f3..bf7f290af1622f 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1325,6 +1325,14 @@ def _asdict_inner(obj, dict_factory): # generator (which is not true for namedtuples, handled # above). return type(obj)(_asdict_inner(v, dict_factory) for v in obj) + elif isinstance(obj, dict) and hasattr(type(obj), 'default_factory'): + # obj is a defaultdict, which has a different constructor from + # dict as it requires the default_factory as its first arg. + # https://bugs.python.org/issue35540 + result = type(obj)(getattr(obj, 'default_factory')) + for k, v in obj.items(): + result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory) + return result elif isinstance(obj, dict): return type(obj)((_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 328dcdcb0bce78..637c456dd49e7a 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -12,9 +12,9 @@ import weakref import unittest from unittest.mock import Mock -from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol +from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict from typing import get_type_hints -from collections import deque, OrderedDict, namedtuple +from collections import deque, OrderedDict, namedtuple, defaultdict from functools import total_ordering import typing # Needed for the string "typing.ClassVar[int]" to work as an annotation. @@ -1677,6 +1677,23 @@ class C: self.assertIsNot(d['f'], t) self.assertEqual(d['f'].my_a(), 6) + def test_helper_asdict_defaultdict(self): + # Ensure asdict() does not throw exceptions when a + # defaultdict is a member of a dataclass + + @dataclass + class C: + mp: DefaultDict[str, List] + + + dd = defaultdict(list) + dd["x"].append(12) + c = C(mp=dd) + d = asdict(c) + + assert d == {"mp": {"x": [12]}} + assert d["mp"] is not c.mp # make sure defaultdict is copied + def test_helper_astuple(self): # Basic tests for astuple(), it should return a new tuple. @dataclass diff --git a/Misc/NEWS.d/next/Library/2022-03-22-18-28-55.bpo-35540.nyijX9.rst b/Misc/NEWS.d/next/Library/2022-03-22-18-28-55.bpo-35540.nyijX9.rst new file mode 100644 index 00000000000000..b7aeee6c8c8f78 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-22-18-28-55.bpo-35540.nyijX9.rst @@ -0,0 +1 @@ +Fix :func:`dataclasses.asdict` crash when :class:`collections.defaultdict` is present in the attributes. From 7eba427a6cd8cdf0cbde7aad382b13c2b473814f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 6 Oct 2022 17:30:27 -0700 Subject: [PATCH 079/151] GH-90985: Revert "Deprecate passing a message into cancel()" (#97999) Reason: we were too hasty in deprecating this. We shouldn't deprecate it before we have a replacement. --- Doc/library/asyncio-future.rst | 10 ---- Doc/library/asyncio-task.rst | 6 +-- Lib/asyncio/futures.py | 6 --- Lib/asyncio/tasks.py | 5 -- Lib/test/test_asyncio/test_futures.py | 12 +---- Lib/test/test_asyncio/test_tasks.py | 54 +++---------------- ...2-10-06-23-42-00.gh-issue-90985.s280JY.rst | 1 + Modules/_asynciomodule.c | 20 ------- 8 files changed, 12 insertions(+), 102 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-06-23-42-00.gh-issue-90985.s280JY.rst diff --git a/Doc/library/asyncio-future.rst b/Doc/library/asyncio-future.rst index 8e60877f0e4c7d..70cec9b2f90248 100644 --- a/Doc/library/asyncio-future.rst +++ b/Doc/library/asyncio-future.rst @@ -197,11 +197,6 @@ Future Object .. versionchanged:: 3.9 Added the *msg* parameter. - .. deprecated-removed:: 3.11 3.14 - *msg* parameter is ambiguous when multiple :meth:`cancel` - are called with different cancellation messages. - The argument will be removed. - .. method:: exception() Return the exception that was set on this Future. @@ -282,8 +277,3 @@ the Future has a result:: - :meth:`asyncio.Future.cancel` accepts an optional ``msg`` argument, but :func:`concurrent.futures.cancel` does not. - - .. deprecated-removed:: 3.11 3.14 - *msg* parameter is ambiguous when multiple :meth:`cancel` - are called with different cancellation messages. - The argument will be removed. diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index d922f614954fcd..9c17dc6397365f 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -1144,10 +1144,8 @@ Task Object .. versionchanged:: 3.9 Added the *msg* parameter. - .. deprecated-removed:: 3.11 3.14 - *msg* parameter is ambiguous when multiple :meth:`cancel` - are called with different cancellation messages. - The argument will be removed. + .. versionchanged:: 3.11 + The ``msg`` parameter is propagated from cancelled task to its awaiter. .. _asyncio_example_task_cancel: diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 39776e3c2cce48..3a6b44a0910869 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -8,7 +8,6 @@ import contextvars import logging import sys -import warnings from types import GenericAlias from . import base_futures @@ -151,11 +150,6 @@ def cancel(self, msg=None): change the future's state to cancelled, schedule the callbacks and return True. """ - if msg is not None: - warnings.warn("Passing 'msg' argument to Future.cancel() " - "is deprecated since Python 3.11, and " - "scheduled for removal in Python 3.14.", - DeprecationWarning, stacklevel=2) self.__log_traceback = False if self._state != _PENDING: return False diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 8d6dfcd81b7377..571013745aa03a 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -210,11 +210,6 @@ def cancel(self, msg=None): This also increases the task's count of cancellation requests. """ - if msg is not None: - warnings.warn("Passing 'msg' argument to Task.cancel() " - "is deprecated since Python 3.11, and " - "scheduled for removal in Python 3.14.", - DeprecationWarning, stacklevel=2) self._log_traceback = False if self.done(): return False diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 3dc6b658cfae8d..83ea01c2452521 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -229,22 +229,14 @@ def test_future_cancel_message_getter(self): self.assertTrue(hasattr(f, '_cancel_message')) self.assertEqual(f._cancel_message, None) - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - f.cancel('my message') + f.cancel('my message') with self.assertRaises(asyncio.CancelledError): self.loop.run_until_complete(f) self.assertEqual(f._cancel_message, 'my message') def test_future_cancel_message_setter(self): f = self._new_future(loop=self.loop) - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - f.cancel('my message') + f.cancel('my message') f._cancel_message = 'my new message' self.assertEqual(f._cancel_message, 'my new message') diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 2491285206bcd7..0e38e6a92e52b7 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -109,11 +109,7 @@ async def coro(): self.assertTrue(hasattr(t, '_cancel_message')) self.assertEqual(t._cancel_message, None) - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - t.cancel('my message') + t.cancel('my message') self.assertEqual(t._cancel_message, 'my message') with self.assertRaises(asyncio.CancelledError) as cm: @@ -125,11 +121,7 @@ def test_task_cancel_message_setter(self): async def coro(): pass t = self.new_task(self.loop, coro()) - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - t.cancel('my message') + t.cancel('my message') t._cancel_message = 'my new message' self.assertEqual(t._cancel_message, 'my new message') @@ -706,14 +698,7 @@ async def sleep(): async def coro(): task = self.new_task(loop, sleep()) await asyncio.sleep(0) - if cancel_args not in ((), (None,)): - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - task.cancel(*cancel_args) - else: - task.cancel(*cancel_args) + task.cancel(*cancel_args) done, pending = await asyncio.wait([task]) task.result() @@ -747,14 +732,7 @@ async def sleep(): async def coro(): task = self.new_task(loop, sleep()) await asyncio.sleep(0) - if cancel_args not in ((), (None,)): - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - task.cancel(*cancel_args) - else: - task.cancel(*cancel_args) + task.cancel(*cancel_args) done, pending = await asyncio.wait([task]) task.exception() @@ -777,17 +755,10 @@ async def sleep(): fut.set_result(None) await asyncio.sleep(10) - def cancel(task, msg): - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - task.cancel(msg) - async def coro(): inner_task = self.new_task(loop, sleep()) await fut - loop.call_soon(cancel, inner_task, 'msg') + loop.call_soon(inner_task.cancel, 'msg') try: await inner_task except asyncio.CancelledError as ex: @@ -813,11 +784,7 @@ async def sleep(): async def coro(): task = self.new_task(loop, sleep()) # We deliberately leave out the sleep here. - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - task.cancel('my message') + task.cancel('my message') done, pending = await asyncio.wait([task]) task.exception() @@ -2179,14 +2146,7 @@ async def test(): async def main(): qwe = self.new_task(loop, test()) await asyncio.sleep(0.2) - if cancel_args not in ((), (None,)): - with self.assertWarnsRegex( - DeprecationWarning, - "Passing 'msg' argument" - ): - qwe.cancel(*cancel_args) - else: - qwe.cancel(*cancel_args) + qwe.cancel(*cancel_args) await qwe try: diff --git a/Misc/NEWS.d/next/Library/2022-10-06-23-42-00.gh-issue-90985.s280JY.rst b/Misc/NEWS.d/next/Library/2022-10-06-23-42-00.gh-issue-90985.s280JY.rst new file mode 100644 index 00000000000000..964aa3986331a2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-06-23-42-00.gh-issue-90985.s280JY.rst @@ -0,0 +1 @@ +Earlier in 3.11 we deprecated ``asyncio.Task.cancel("message")``. We realized we were too harsh, and have undeprecated it. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index efa0d2d6906eab..ab6219c322f9fd 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1116,16 +1116,6 @@ static PyObject * _asyncio_Future_cancel_impl(FutureObj *self, PyObject *msg) /*[clinic end generated code: output=3edebbc668e5aba3 input=925eb545251f2c5a]*/ { - if (msg != Py_None) { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "Passing 'msg' argument to Future.cancel() " - "is deprecated since Python 3.11, and " - "scheduled for removal in Python 3.14.", - 2)) - { - return NULL; - } - } ENSURE_FUTURE_ALIVE(self) return future_cancel(self, msg); } @@ -2214,16 +2204,6 @@ static PyObject * _asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg) /*[clinic end generated code: output=c66b60d41c74f9f1 input=7bb51bf25974c783]*/ { - if (msg != Py_None) { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "Passing 'msg' argument to Task.cancel() " - "is deprecated since Python 3.11, and " - "scheduled for removal in Python 3.14.", - 2)) - { - return NULL; - } - } self->task_log_tb = 0; if (self->task_state != STATE_PENDING) { From e8967518c722ba68b178ffea23c56b459487db33 Mon Sep 17 00:00:00 2001 From: Xiao Chen Date: Fri, 7 Oct 2022 08:42:08 +0800 Subject: [PATCH 080/151] Remove extra spaces in custom openSSL documentation. (#93568) --- Doc/using/unix.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst index 3f9f1364c8ae87..061cfa5be88f29 100644 --- a/Doc/using/unix.rst +++ b/Doc/using/unix.rst @@ -158,16 +158,16 @@ Custom OpenSSL .. code-block:: shell-session $ curl -O https://www.openssl.org/source/openssl-VERSION.tar.gz - $ tar xzf openssl-VERSION - $ pushd openssl-VERSION - $ ./config \ - --prefix=/usr/local/custom-openssl \ - --libdir=lib \ - --openssldir=/etc/ssl - $ make -j1 depend - $ make -j8 - $ make install_sw - $ popd + $ tar xzf openssl-VERSION + $ pushd openssl-VERSION + $ ./config \ + --prefix=/usr/local/custom-openssl \ + --libdir=lib \ + --openssldir=/etc/ssl + $ make -j1 depend + $ make -j8 + $ make install_sw + $ popd 3. Build Python with custom OpenSSL (see the configure `--with-openssl` and `--with-openssl-rpath` options) From 04a52de6ecaf9bae728c31dc38a4367eb5496fab Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 03:57:10 +0300 Subject: [PATCH 081/151] gh-97850: Remove deprecated functions from `importlib.utils` (#97898) * gh-97850: Remove deprecated functions from `importlib.utils` * Rebase and remove `set_package` from diff --- Doc/library/importlib.rst | 54 +----- Lib/importlib/util.py | 87 --------- Lib/test/test_importlib/test_abc.py | 8 +- Lib/test/test_importlib/test_spec.py | 48 ----- Lib/test/test_importlib/test_util.py | 178 ------------------ ...2-10-05-11-40-02.gh-issue-97850.NzdREm.rst | 2 + 6 files changed, 4 insertions(+), 373 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index a7c067c06e8ec2..3fc1531c0cdf19 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -443,7 +443,7 @@ ABC hierarchy:: from the import. If the loader inserted a module and the load fails, it must be removed by the loader from :data:`sys.modules`; modules already in :data:`sys.modules` before the loader began execution should be left - alone (see :func:`importlib.util.module_for_loader`). + alone. The loader should set several attributes on the module (note that some of these attributes can change when a module is @@ -1326,58 +1326,6 @@ an :term:`importer`. .. versionadded:: 3.5 -.. decorator:: module_for_loader - - A :term:`decorator` for :meth:`importlib.abc.Loader.load_module` - to handle selecting the proper - module object to load with. The decorated method is expected to have a call - signature taking two positional arguments - (e.g. ``load_module(self, module)``) for which the second argument - will be the module **object** to be used by the loader. - Note that the decorator will not work on static methods because of the - assumption of two arguments. - - The decorated method will take in the **name** of the module to be loaded - as expected for a :term:`loader`. If the module is not found in - :data:`sys.modules` then a new one is constructed. Regardless of where the - module came from, :attr:`__loader__` set to **self** and :attr:`__package__` - is set based on what :meth:`importlib.abc.InspectLoader.is_package` returns - (if available). These attributes are set unconditionally to support - reloading. - - If an exception is raised by the decorated method and a module was added to - :data:`sys.modules`, then the module will be removed to prevent a partially - initialized module from being in left in :data:`sys.modules`. If the module - was already in :data:`sys.modules` then it is left alone. - - .. versionchanged:: 3.3 - :attr:`__loader__` and :attr:`__package__` are automatically set - (when possible). - - .. versionchanged:: 3.4 - Set :attr:`__name__`, :attr:`__loader__` :attr:`__package__` - unconditionally to support reloading. - - .. deprecated:: 3.4 - The import machinery now directly performs all the functionality - provided by this function. - -.. decorator:: set_loader - - A :term:`decorator` for :meth:`importlib.abc.Loader.load_module` - to set the :attr:`__loader__` - attribute on the returned module. If the attribute is already set the - decorator does nothing. It is assumed that the first positional argument to - the wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set - to. - - .. versionchanged:: 3.4 - Set ``__loader__`` if set to ``None``, as if the attribute does not - exist. - - .. deprecated:: 3.4 - The import machinery takes care of this automatically. - .. function:: spec_from_loader(name, loader, *, origin=None, is_package=None) A factory function for creating a :class:`~importlib.machinery.ModuleSpec` diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index 7f15b029b24050..9e29c581b1db33 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -11,12 +11,9 @@ from ._bootstrap_external import source_from_cache from ._bootstrap_external import spec_from_file_location -from contextlib import contextmanager import _imp -import functools import sys import types -import warnings def source_hash(source_bytes): @@ -115,90 +112,6 @@ def find_spec(name, package=None): return spec -@contextmanager -def _module_to_load(name): - is_reload = name in sys.modules - - module = sys.modules.get(name) - if not is_reload: - # This must be done before open() is called as the 'io' module - # implicitly imports 'locale' and would otherwise trigger an - # infinite loop. - module = type(sys)(name) - # This must be done before putting the module in sys.modules - # (otherwise an optimization shortcut in import.c becomes wrong) - module.__initializing__ = True - sys.modules[name] = module - try: - yield module - except Exception: - if not is_reload: - try: - del sys.modules[name] - except KeyError: - pass - finally: - module.__initializing__ = False - - -def set_loader(fxn): - """Set __loader__ on the returned module. - - This function is deprecated. - - """ - @functools.wraps(fxn) - def set_loader_wrapper(self, *args, **kwargs): - warnings.warn('The import system now takes care of this automatically; ' - 'this decorator is slated for removal in Python 3.12', - DeprecationWarning, stacklevel=2) - module = fxn(self, *args, **kwargs) - if getattr(module, '__loader__', None) is None: - module.__loader__ = self - return module - return set_loader_wrapper - - -def module_for_loader(fxn): - """Decorator to handle selecting the proper module for loaders. - - The decorated function is passed the module to use instead of the module - name. The module passed in to the function is either from sys.modules if - it already exists or is a new module. If the module is new, then __name__ - is set the first argument to the method, __loader__ is set to self, and - __package__ is set accordingly (if self.is_package() is defined) will be set - before it is passed to the decorated function (if self.is_package() does - not work for the module it will be set post-load). - - If an exception is raised and the decorator created the module it is - subsequently removed from sys.modules. - - The decorator assumes that the decorated function takes the module name as - the second argument. - - """ - warnings.warn('The import system now takes care of this automatically; ' - 'this decorator is slated for removal in Python 3.12', - DeprecationWarning, stacklevel=2) - @functools.wraps(fxn) - def module_for_loader_wrapper(self, fullname, *args, **kwargs): - with _module_to_load(fullname) as module: - module.__loader__ = self - try: - is_package = self.is_package(fullname) - except (ImportError, AttributeError): - pass - else: - if is_package: - module.__package__ = fullname - else: - module.__package__ = fullname.rpartition('.')[0] - # If __package__ was not set above, __import__() will do it later. - return fxn(self, module, *args, **kwargs) - - return module_for_loader_wrapper - - class _LazyModule(types.ModuleType): """A subclass of the module type which triggers loading upon attribute access.""" diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index 8641b6cc683052..3c9149c4e45a92 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -771,13 +771,7 @@ def verify_code(self, code_object): class SourceOnlyLoaderTests(SourceLoaderTestHarness): - - """Test importlib.abc.SourceLoader for source-only loading. - - Reload testing is subsumed by the tests for - importlib.util.module_for_loader. - - """ + """Test importlib.abc.SourceLoader for source-only loading.""" def test_get_source(self): # Verify the source code is returned as a string. diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py index f1ab16c7b2a9be..80aa3609c6f96e 100644 --- a/Lib/test/test_importlib/test_spec.py +++ b/Lib/test/test_importlib/test_spec.py @@ -47,21 +47,6 @@ def exec_module(self, module): module.eggs = self.EGGS -class LegacyLoader(TestLoader): - - HAM = -1 - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - - frozen_util = util['Frozen'] - - @frozen_util.module_for_loader - def load_module(self, module): - module.ham = self.HAM - return module - - class ModuleSpecTests: def setUp(self): @@ -302,26 +287,6 @@ def exec_module(self, module): loaded = self.bootstrap._load(self.spec) self.assertNotIn(self.spec.name, sys.modules) - def test_load_legacy(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - self.spec.loader = LegacyLoader() - with CleanImport(self.spec.name): - loaded = self.bootstrap._load(self.spec) - - self.assertEqual(loaded.ham, -1) - - def test_load_legacy_attributes(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - self.spec.loader = LegacyLoader() - with CleanImport(self.spec.name): - loaded = self.bootstrap._load(self.spec) - - self.assertIs(loaded.__loader__, self.spec.loader) - self.assertEqual(loaded.__package__, self.spec.parent) - self.assertIs(loaded.__spec__, self.spec) - def test_load_legacy_attributes_immutable(self): module = object() with warnings.catch_warnings(): @@ -387,19 +352,6 @@ def test_reload_init_module_attrs(self): self.assertFalse(hasattr(loaded, '__file__')) self.assertFalse(hasattr(loaded, '__cached__')) - def test_reload_legacy(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - self.spec.loader = LegacyLoader() - with CleanImport(self.spec.name): - loaded = self.bootstrap._load(self.spec) - reloaded = self.bootstrap._exec(self.spec, loaded) - installed = sys.modules[self.spec.name] - - self.assertEqual(loaded.ham, -1) - self.assertIs(reloaded, loaded) - self.assertIs(installed, loaded) - (Frozen_ModuleSpecMethodsTests, Source_ModuleSpecMethodsTests diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index e70971e9d3bc84..08a615ecf5288b 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -121,184 +121,6 @@ def test___cached__(self): util=importlib_util) -class ModuleForLoaderTests: - - """Tests for importlib.util.module_for_loader.""" - - @classmethod - def module_for_loader(cls, func): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - return cls.util.module_for_loader(func) - - def test_warning(self): - # Should raise a PendingDeprecationWarning when used. - with warnings.catch_warnings(): - warnings.simplefilter('error', DeprecationWarning) - with self.assertRaises(DeprecationWarning): - func = self.util.module_for_loader(lambda x: x) - - def return_module(self, name): - fxn = self.module_for_loader(lambda self, module: module) - return fxn(self, name) - - def raise_exception(self, name): - def to_wrap(self, module): - raise ImportError - fxn = self.module_for_loader(to_wrap) - try: - fxn(self, name) - except ImportError: - pass - - def test_new_module(self): - # Test that when no module exists in sys.modules a new module is - # created. - module_name = 'a.b.c' - with util.uncache(module_name): - module = self.return_module(module_name) - self.assertIn(module_name, sys.modules) - self.assertIsInstance(module, types.ModuleType) - self.assertEqual(module.__name__, module_name) - - def test_reload(self): - # Test that a module is reused if already in sys.modules. - class FakeLoader: - def is_package(self, name): - return True - @self.module_for_loader - def load_module(self, module): - return module - name = 'a.b.c' - module = types.ModuleType('a.b.c') - module.__loader__ = 42 - module.__package__ = 42 - with util.uncache(name): - sys.modules[name] = module - loader = FakeLoader() - returned_module = loader.load_module(name) - self.assertIs(returned_module, sys.modules[name]) - self.assertEqual(module.__loader__, loader) - self.assertEqual(module.__package__, name) - - def test_new_module_failure(self): - # Test that a module is removed from sys.modules if added but an - # exception is raised. - name = 'a.b.c' - with util.uncache(name): - self.raise_exception(name) - self.assertNotIn(name, sys.modules) - - def test_reload_failure(self): - # Test that a failure on reload leaves the module in-place. - name = 'a.b.c' - module = types.ModuleType(name) - with util.uncache(name): - sys.modules[name] = module - self.raise_exception(name) - self.assertIs(module, sys.modules[name]) - - def test_decorator_attrs(self): - def fxn(self, module): pass - wrapped = self.module_for_loader(fxn) - self.assertEqual(wrapped.__name__, fxn.__name__) - self.assertEqual(wrapped.__qualname__, fxn.__qualname__) - - def test_false_module(self): - # If for some odd reason a module is considered false, still return it - # from sys.modules. - class FalseModule(types.ModuleType): - def __bool__(self): return False - - name = 'mod' - module = FalseModule(name) - with util.uncache(name): - self.assertFalse(module) - sys.modules[name] = module - given = self.return_module(name) - self.assertIs(given, module) - - def test_attributes_set(self): - # __name__, __loader__, and __package__ should be set (when - # is_package() is defined; undefined implicitly tested elsewhere). - class FakeLoader: - def __init__(self, is_package): - self._pkg = is_package - def is_package(self, name): - return self._pkg - @self.module_for_loader - def load_module(self, module): - return module - - name = 'pkg.mod' - with util.uncache(name): - loader = FakeLoader(False) - module = loader.load_module(name) - self.assertEqual(module.__name__, name) - self.assertIs(module.__loader__, loader) - self.assertEqual(module.__package__, 'pkg') - - name = 'pkg.sub' - with util.uncache(name): - loader = FakeLoader(True) - module = loader.load_module(name) - self.assertEqual(module.__name__, name) - self.assertIs(module.__loader__, loader) - self.assertEqual(module.__package__, name) - - -(Frozen_ModuleForLoaderTests, - Source_ModuleForLoaderTests - ) = util.test_both(ModuleForLoaderTests, util=importlib_util) - - -class SetLoaderTests: - - """Tests importlib.util.set_loader().""" - - @property - def DummyLoader(self): - # Set DummyLoader on the class lazily. - class DummyLoader: - @self.util.set_loader - def load_module(self, module): - return self.module - self.__class__.DummyLoader = DummyLoader - return DummyLoader - - def test_no_attribute(self): - loader = self.DummyLoader() - loader.module = types.ModuleType('blah') - try: - del loader.module.__loader__ - except AttributeError: - pass - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - self.assertEqual(loader, loader.load_module('blah').__loader__) - - def test_attribute_is_None(self): - loader = self.DummyLoader() - loader.module = types.ModuleType('blah') - loader.module.__loader__ = None - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - self.assertEqual(loader, loader.load_module('blah').__loader__) - - def test_not_reset(self): - loader = self.DummyLoader() - loader.module = types.ModuleType('blah') - loader.module.__loader__ = 42 - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - self.assertEqual(42, loader.load_module('blah').__loader__) - - -(Frozen_SetLoaderTests, - Source_SetLoaderTests - ) = util.test_both(SetLoaderTests, util=importlib_util) - - class ResolveNameTests: """Tests importlib.util.resolve_name().""" diff --git a/Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst b/Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst new file mode 100644 index 00000000000000..5e759bc0995a04 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-05-11-40-02.gh-issue-97850.NzdREm.rst @@ -0,0 +1,2 @@ +Remove deprecated :func:`importlib.utils.set_loader` and +:func:`importlib.utils.module_for_loader` from :mod:`importlib.utils`. From bd03a34ceb9a80646fe3ba8395de2b889b36f80a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 6 Oct 2022 18:01:30 -0700 Subject: [PATCH 082/151] Docs: Fix backtick errors found by sphinx-lint (#97998) Co-authored-by: Ezio Melotti --- Doc/c-api/init.rst | 2 +- Doc/c-api/type.rst | 2 +- Doc/faq/design.rst | 2 +- Doc/howto/enum.rst | 2 +- Doc/howto/logging-cookbook.rst | 4 ++-- Doc/howto/logging.rst | 10 +++++----- Doc/howto/perf_profiling.rst | 2 +- Doc/install/index.rst | 2 +- Doc/library/asyncio-protocol.rst | 2 +- Doc/library/asyncio-task.rst | 10 +++++----- Doc/library/bdb.rst | 2 +- Doc/library/bz2.rst | 4 ++-- Doc/library/concurrent.futures.rst | 6 +++--- Doc/library/ctypes.rst | 2 +- Doc/library/curses.rst | 2 +- Doc/library/datetime.rst | 4 ++-- Doc/library/decimal.rst | 8 ++++---- Doc/library/dis.rst | 2 +- Doc/library/email.compat32-message.rst | 2 +- Doc/library/email.headerregistry.rst | 2 +- Doc/library/fractions.rst | 2 +- Doc/library/hashlib.rst | 2 +- Doc/library/io.rst | 6 +++--- Doc/library/lzma.rst | 2 +- Doc/library/os.rst | 4 ++-- Doc/library/select.rst | 2 +- Doc/library/socket.rst | 2 +- Doc/library/statistics.rst | 2 +- Doc/library/sys.rst | 6 +++--- Doc/library/unittest.mock-examples.rst | 2 +- Doc/library/xml.dom.minidom.rst | 4 ++-- Doc/library/xmlrpc.client.rst | 2 +- Doc/library/xmlrpc.server.rst | 2 +- Doc/reference/expressions.rst | 2 +- Doc/reference/import.rst | 2 +- Doc/reference/simple_stmts.rst | 8 -------- Doc/requirements.txt | 2 +- Doc/using/configure.rst | 14 +++++++------- Doc/using/unix.rst | 2 +- Doc/using/windows.rst | 2 +- Doc/whatsnew/2.6.rst | 6 +++--- Doc/whatsnew/2.7.rst | 4 ++-- Doc/whatsnew/3.10.rst | 2 +- Doc/whatsnew/3.11.rst | 2 +- Doc/whatsnew/3.12.rst | 2 +- Doc/whatsnew/3.2.rst | 4 ++-- Doc/whatsnew/3.3.rst | 4 ++-- Doc/whatsnew/3.5.rst | 4 ++-- Doc/whatsnew/3.6.rst | 6 +++--- Doc/whatsnew/3.7.rst | 2 +- Doc/whatsnew/3.9.rst | 2 +- .../2022-10-01-08-55-09.gh-issue-97591.pw6kkH.rst | 2 +- .../2022-09-01-17-03-04.gh-issue-96432.1EJ1-4.rst | 2 +- .../2022-05-09-21-31-41.gh-issue-92445.tJosdm.rst | 2 +- .../2022-07-06-14-57-33.gh-issue-94619.PRqKVX.rst | 2 +- 55 files changed, 90 insertions(+), 98 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 513ef93a384202..9b7383373e9841 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1929,7 +1929,7 @@ is not possible due to its implementation being opaque at build time. Free the given *key* allocated by :c:func:`PyThread_tss_alloc`, after first calling :c:func:`PyThread_tss_delete` to ensure any associated thread locals have been unassigned. This is a no-op if the *key* - argument is `NULL`. + argument is ``NULL``. .. note:: A freed key becomes a dangling pointer. You should reset the key to diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index deb5502a4dff9e..1dc05001adfa37 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -40,7 +40,7 @@ Type Objects .. c:function:: unsigned long PyType_GetFlags(PyTypeObject* type) Return the :c:member:`~PyTypeObject.tp_flags` member of *type*. This function is primarily - meant for use with `Py_LIMITED_API`; the individual flag bits are + meant for use with ``Py_LIMITED_API``; the individual flag bits are guaranteed to be stable across Python releases, but access to :c:member:`~PyTypeObject.tp_flags` itself is not part of the limited API. diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index 52388fca5cdbca..ccdc9bf02b1c7b 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -155,7 +155,7 @@ Why can't I use an assignment in an expression? Starting in Python 3.8, you can! -Assignment expressions using the walrus operator `:=` assign a variable in an +Assignment expressions using the walrus operator ``:=`` assign a variable in an expression:: while chunk := fp.read(200): diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 376934a0e79365..03f05657997cfd 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -1109,7 +1109,7 @@ Enum Classes The :class:`EnumType` metaclass is responsible for providing the :meth:`__contains__`, :meth:`__dir__`, :meth:`__iter__` and other methods that allow one to do things with an :class:`Enum` class that fail on a typical -class, such as `list(Color)` or `some_enum_var in Color`. :class:`EnumType` is +class, such as ``list(Color)`` or ``some_enum_var in Color``. :class:`EnumType` is responsible for ensuring that various other methods on the final :class:`Enum` class are correct (such as :meth:`__new__`, :meth:`__getnewargs__`, :meth:`__str__` and :meth:`__repr__`). diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index ff7ba0789608ff..913502eba76434 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -562,7 +562,7 @@ To run a logging listener in production, you may need to use a process-managemen such as `Supervisor `_. `Here `_ is a Gist which provides the bare-bones files to run the above functionality using Supervisor: you -will need to change the `/path/to/` parts in the Gist to reflect the actual paths you +will need to change the ``/path/to/`` parts in the Gist to reflect the actual paths you want to use. @@ -2774,7 +2774,7 @@ Formatting times using UTC (GMT) via configuration -------------------------------------------------- Sometimes you want to format times using UTC, which can be done using a class -such as `UTCFormatter`, shown below:: +such as ``UTCFormatter``, shown below:: import logging import time diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 0caff13bd15d82..145449b2dfbd9f 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -555,14 +555,14 @@ raw message. If there is no date format string, the default date format is: %Y-%m-%d %H:%M:%S -with the milliseconds tacked on at the end. The ``style`` is one of `%`, '{' -or '$'. If one of these is not specified, then '%' will be used. +with the milliseconds tacked on at the end. The ``style`` is one of ``'%'``, +``'{'``, or ``'$'``. If one of these is not specified, then ``'%'`` will be used. -If the ``style`` is '%', the message format string uses +If the ``style`` is ``'%'``, the message format string uses ``%()s`` styled string substitution; the possible keys are -documented in :ref:`logrecord-attributes`. If the style is '{', the message +documented in :ref:`logrecord-attributes`. If the style is ``'{'``, the message format string is assumed to be compatible with :meth:`str.format` (using -keyword arguments), while if the style is '$' then the message format string +keyword arguments), while if the style is ``'$'`` then the message format string should conform to what is expected by :meth:`string.Template.substitute`. .. versionchanged:: 3.2 diff --git a/Doc/howto/perf_profiling.rst b/Doc/howto/perf_profiling.rst index ed8de888b3bc21..387fb3ff893561 100644 --- a/Doc/howto/perf_profiling.rst +++ b/Doc/howto/perf_profiling.rst @@ -151,7 +151,7 @@ Enabling perf profiling mode ---------------------------- There are two main ways to activate the perf profiling mode. If you want it to be -active since the start of the Python interpreter, you can use the `-Xperf` option: +active since the start of the Python interpreter, you can use the ``-Xperf`` option: $ python -Xperf my_script.py diff --git a/Doc/install/index.rst b/Doc/install/index.rst index 3fc670b191424d..ab581d785ef7f0 100644 --- a/Doc/install/index.rst +++ b/Doc/install/index.rst @@ -765,7 +765,7 @@ And on Windows, the configuration files are: +--------------+-------------------------------------------------+-------+ On all platforms, the "personal" file can be temporarily disabled by -passing the `--no-user-cfg` option. +passing the ``--no-user-cfg`` option. Notes: diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst index 8b67f4b8957ef6..969354ceb163b5 100644 --- a/Doc/library/asyncio-protocol.rst +++ b/Doc/library/asyncio-protocol.rst @@ -553,7 +553,7 @@ accept factories that return streaming protocols. a connection is open. However, :meth:`protocol.eof_received() ` - is called at most once. Once `eof_received()` is called, + is called at most once. Once ``eof_received()`` is called, ``data_received()`` is not called anymore. .. method:: Protocol.eof_received() diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 9c17dc6397365f..fb6d23fda03e41 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -631,7 +631,7 @@ Timeouts Change the time the timeout will trigger. - If *when* is `None`, any current deadline will be removed, and the + If *when* is ``None``, any current deadline will be removed, and the context manager will wait indefinitely. If *when* is a float, it is set as the new deadline. @@ -867,17 +867,17 @@ Running in Threads # blocking_io complete at 19:50:54 # finished main at 19:50:54 - Directly calling `blocking_io()` in any coroutine would block the event loop + Directly calling ``blocking_io()`` in any coroutine would block the event loop for its duration, resulting in an additional 1 second of run time. Instead, - by using `asyncio.to_thread()`, we can run it in a separate thread without + by using ``asyncio.to_thread()``, we can run it in a separate thread without blocking the event loop. .. note:: - Due to the :term:`GIL`, `asyncio.to_thread()` can typically only be used + Due to the :term:`GIL`, ``asyncio.to_thread()`` can typically only be used to make IO-bound functions non-blocking. However, for extension modules that release the GIL or alternative Python implementations that don't - have one, `asyncio.to_thread()` can also be used for CPU-bound functions. + have one, ``asyncio.to_thread()`` can also be used for CPU-bound functions. .. versionadded:: 3.9 diff --git a/Doc/library/bdb.rst b/Doc/library/bdb.rst index 7b74bbd652be38..d201dc963b5995 100644 --- a/Doc/library/bdb.rst +++ b/Doc/library/bdb.rst @@ -143,7 +143,7 @@ The :mod:`bdb` module also defines two classes: For real file names, the canonical form is an operating-system-dependent, :func:`case-normalized ` :func:`absolute path - `. A *filename* with angle brackets, such as `""` + `. A *filename* with angle brackets, such as ``""`` generated in interactive mode, is returned unchanged. .. method:: reset() diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst index 999892e95f4715..ae5a1598f84b44 100644 --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -206,7 +206,7 @@ Incremental (de)compression will be set to ``True``. Attempting to decompress data after the end of stream is reached - raises an `EOFError`. Any data found after the end of the + raises an :exc:`EOFError`. Any data found after the end of the stream is ignored and saved in the :attr:`~.unused_data` attribute. .. versionchanged:: 3.5 @@ -303,7 +303,7 @@ Using :class:`BZ2Compressor` for incremental compression: >>> out = out + comp.flush() The example above uses a very "nonrandom" stream of data -(a stream of `b"z"` chunks). Random data tends to compress poorly, +(a stream of ``b"z"`` chunks). Random data tends to compress poorly, while ordered, repetitive data usually yields a high compression ratio. Writing and reading a bzip2-compressed file in binary mode: diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index 95c9e50991423f..8106cc235e5a3c 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -152,7 +152,7 @@ And:: All threads enqueued to ``ThreadPoolExecutor`` will be joined before the interpreter can exit. Note that the exit handler which does this is - executed *before* any exit handlers added using `atexit`. This means + executed *before* any exit handlers added using ``atexit``. This means exceptions in the main thread must be caught and handled in order to signal threads to exit gracefully. For this reason, it is recommended that ``ThreadPoolExecutor`` not be used for long-running tasks. @@ -411,13 +411,13 @@ The :class:`Future` class encapsulates the asynchronous execution of a callable. tests. If the method returns ``False`` then the :class:`Future` was cancelled, - i.e. :meth:`Future.cancel` was called and returned `True`. Any threads + i.e. :meth:`Future.cancel` was called and returned ``True``. Any threads waiting on the :class:`Future` completing (i.e. through :func:`as_completed` or :func:`wait`) will be woken up. If the method returns ``True`` then the :class:`Future` was not cancelled and has been put in the running state, i.e. calls to - :meth:`Future.running` will return `True`. + :meth:`Future.running` will return ``True``. This method can only be called once and cannot be called after :meth:`Future.set_result` or :meth:`Future.set_exception` have been diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 4b6b26c991fd38..0351ec970be0b8 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1948,7 +1948,7 @@ Utility functions .. function:: GetLastError() Windows only: Returns the last error code set by Windows in the calling thread. - This function calls the Windows `GetLastError()` function directly, + This function calls the Windows ``GetLastError()`` function directly, it does not return the ctypes-private copy of the error code. .. function:: get_errno() diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index bb203c48f19ffb..bf4e69a0170a62 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -275,7 +275,7 @@ The module :mod:`curses` defines the following functions: Change the definition of a color, taking the number of the color to be changed followed by three RGB values (for the amounts of red, green, and blue components). The value of *color_number* must be between ``0`` and - `COLORS - 1`. Each of *r*, *g*, *b*, must be a value between ``0`` and + ``COLORS - 1``. Each of *r*, *g*, *b*, must be a value between ``0`` and ``1000``. When :func:`init_color` is used, all occurrences of that color on the screen immediately change to the new definition. This function is a no-op on most terminals; it is active only if :func:`can_change_color` returns ``True``. diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index c3a66a4674b10a..f7e2bb3f3c6de3 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1769,7 +1769,7 @@ Other constructor: ISO 8601 format, with the following exceptions: 1. Time zone offsets may have fractional seconds. - 2. The leading `T`, normally required in cases where there may be ambiguity between + 2. The leading ``T``, normally required in cases where there may be ambiguity between a date and a time, is not required. 3. Fractional seconds may have any number of digits (anything beyond 6 will be truncated). @@ -2265,7 +2265,7 @@ where historical changes have been made to civil time. two digits of ``offset.hours`` and ``offset.minutes`` respectively. .. versionchanged:: 3.6 - Name generated from ``offset=timedelta(0)`` is now plain `'UTC'`, not + Name generated from ``offset=timedelta(0)`` is now plain ``'UTC'``, not ``'UTC+00:00'``. diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index b7e836308fa8aa..260108136df7f1 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -576,11 +576,11 @@ Decimal objects Alternative constructor that only accepts instances of :class:`float` or :class:`int`. - Note `Decimal.from_float(0.1)` is not the same as `Decimal('0.1')`. + Note ``Decimal.from_float(0.1)`` is not the same as ``Decimal('0.1')``. Since 0.1 is not exactly representable in binary floating point, the value is stored as the nearest representable value which is - `0x1.999999999999ap-4`. That equivalent value in decimal is - `0.1000000000000000055511151231257827021181583404541015625`. + ``0x1.999999999999ap-4``. That equivalent value in decimal is + ``0.1000000000000000055511151231257827021181583404541015625``. .. note:: From Python 3.2 onwards, a :class:`Decimal` instance can also be constructed directly from a :class:`float`. @@ -1209,7 +1209,7 @@ In addition to the three supplied contexts, new contexts can be created with the .. method:: exp(x) - Returns `e ** x`. + Returns ``e ** x``. .. method:: fma(x, y, z) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 47e4bf60072170..30a336197c397d 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -367,7 +367,7 @@ details of bytecode instructions as :class:`Instruction` instances: .. class:: Positions - In case the information is not available, some fields might be `None`. + In case the information is not available, some fields might be ``None``. .. data:: lineno .. data:: end_lineno diff --git a/Doc/library/email.compat32-message.rst b/Doc/library/email.compat32-message.rst index 4eaa9d588ca35e..5bef155a4af310 100644 --- a/Doc/library/email.compat32-message.rst +++ b/Doc/library/email.compat32-message.rst @@ -298,7 +298,7 @@ Here are the methods of the :class:`Message` class: In a model generated from bytes, any header values that (in contravention of the RFCs) contain non-ASCII bytes will, when retrieved through this interface, be represented as :class:`~email.header.Header` objects with - a charset of `unknown-8bit`. + a charset of ``unknown-8bit``. .. method:: __len__() diff --git a/Doc/library/email.headerregistry.rst b/Doc/library/email.headerregistry.rst index 98527cea43da2a..00a954e0307ea6 100644 --- a/Doc/library/email.headerregistry.rst +++ b/Doc/library/email.headerregistry.rst @@ -153,7 +153,7 @@ headers. specified as ``-0000`` (indicating it is in UTC but contains no information about the source timezone), then :attr:`.datetime` will be a naive :class:`~datetime.datetime`. If a specific timezone offset is - found (including `+0000`), then :attr:`.datetime` will contain an aware + found (including ``+0000``), then :attr:`.datetime` will contain an aware ``datetime`` that uses :class:`datetime.timezone` to record the timezone offset. diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index 3751800b3b1118..c46d88b2297aa1 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -99,7 +99,7 @@ another rational number, or from a string. ``typing.SupportsInt`` instance checks. .. versionchanged:: 3.12 - Space is allowed around the slash for string inputs: `Fraction('2 / 3')`. + Space is allowed around the slash for string inputs: ``Fraction('2 / 3')``. .. attribute:: numerator diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst index 386541acf510ac..8e47312fe77bf5 100644 --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -426,7 +426,7 @@ Constructor functions also accept the following tree hashing parameters: BLAKE2s, 0 in sequential mode). * *last_node*: boolean indicating whether the processed node is the last - one (`False` for sequential mode). + one (``False`` for sequential mode). .. figure:: hashlib-blake2-tree.png :alt: Explanation of tree mode parameters. diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 8fd6b3537019aa..0968509fbafec2 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1055,10 +1055,10 @@ Text I/O The initial value of the buffer can be set by providing *initial_value*. If newline translation is enabled, newlines will be encoded as if by :meth:`~TextIOBase.write`. The stream is positioned at the start of the - buffer which emulates opening an existing file in a `w+` mode, making it + buffer which emulates opening an existing file in a ``w+`` mode, making it ready for an immediate write from the beginning or for a write that - would overwrite the initial value. To emulate opening a file in an `a+` - mode ready for appending, use `f.seek(0, io.SEEK_END)` to reposition the + would overwrite the initial value. To emulate opening a file in an ``a+`` + mode ready for appending, use ``f.seek(0, io.SEEK_END)`` to reposition the stream at the end of the buffer. The *newline* argument works like that of :class:`TextIOWrapper`, diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst index f7aaa0cb30c2a8..a9311f2a03563f 100644 --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -258,7 +258,7 @@ Compressing and decompressing data in memory will be set to ``True``. Attempting to decompress data after the end of stream is reached - raises an `EOFError`. Any data found after the end of the + raises an :exc:`EOFError`. Any data found after the end of the stream is ignored and saved in the :attr:`~.unused_data` attribute. .. versionchanged:: 3.5 diff --git a/Doc/library/os.rst b/Doc/library/os.rst index cb06dc690b4d07..23b014b0b65924 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3194,7 +3194,7 @@ features: system records access and modification times; see :func:`~os.stat`. The best way to preserve exact times is to use the *st_atime_ns* and *st_mtime_ns* fields from the :func:`os.stat` result object with the *ns* parameter to - `utime`. + :func:`utime`. This function can support :ref:`specifying a file descriptor `, :ref:`paths relative to directory descriptors ` and :ref:`not @@ -4094,7 +4094,7 @@ written in Python, such as a mail server's external command delivery program. library :c:data:`POSIX_SPAWN_RESETIDS` flag. If the *setsid* argument is ``True``, it will create a new session ID - for `posix_spawn`. *setsid* requires :c:data:`POSIX_SPAWN_SETSID` + for ``posix_spawn``. *setsid* requires :c:data:`POSIX_SPAWN_SETSID` or :c:data:`POSIX_SPAWN_SETSID_NP` flag. Otherwise, :exc:`NotImplementedError` is raised. diff --git a/Doc/library/select.rst b/Doc/library/select.rst index a8df81f5bd1f07..2890706bab729c 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -61,7 +61,7 @@ The module defines the following: events. *sizehint* informs epoll about the expected number of events to be - registered. It must be positive, or `-1` to use the default. It is only + registered. It must be positive, or ``-1`` to use the default. It is only used on older systems where :c:func:`epoll_create1` is not available; otherwise it has no effect (though its value is still checked). diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index ee0c68e3a70779..3f6cb480371617 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -689,7 +689,7 @@ The following functions all create :ref:`socket objects `. When :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` bit flags are applied to *type* they are cleared, and :attr:`socket.type` will not reflect them. They are still passed - to the underlying system `socket()` call. Therefore, + to the underlying system ``socket()`` call. Therefore, :: diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index c3f9c1f5239e8b..88a887960edb58 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -839,7 +839,7 @@ of applications in statistics. The relative likelihood is computed as the probability of a sample occurring in a narrow range divided by the width of the range (hence the word "density"). Since the likelihood is relative to other points, - its value can be greater than `1.0`. + its value can be greater than ``1.0``. .. method:: NormalDist.cdf(x) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index aab3f6aa83fced..542b08b1878ed0 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -250,7 +250,7 @@ always available. Print low-level information to stderr about the state of CPython's memory allocator. - If Python is `built in debug mode ` (:option:`configure + If Python is :ref:`built in debug mode ` (:option:`configure --with-pydebug option <--with-pydebug>`), it also performs some expensive internal consistency checks. @@ -349,7 +349,7 @@ always available. files to (and read them from) a parallel directory tree rooted at this directory, rather than from ``__pycache__`` directories in the source code tree. Any ``__pycache__`` directories in the source code tree will be ignored - and new `.pyc` files written within the pycache prefix. Thus if you use + and new ``.pyc`` files written within the pycache prefix. Thus if you use :mod:`compileall` as a pre-build step, you must ensure you run it with the same pycache prefix (if any) that you will use at runtime. @@ -874,7 +874,7 @@ always available. .. function:: get_asyncgen_hooks() Returns an *asyncgen_hooks* object, which is similar to a - :class:`~collections.namedtuple` of the form `(firstiter, finalizer)`, + :class:`~collections.namedtuple` of the form ``(firstiter, finalizer)``, where *firstiter* and *finalizer* are expected to be either ``None`` or functions which take an :term:`asynchronous generator iterator` as an argument, and are used to schedule finalization of an asynchronous diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 054efa81266326..f9a207bad6903f 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -1116,7 +1116,7 @@ on first use). That aside there is a way to use ``mock`` to affect the results of an import. Importing fetches an *object* from the :data:`sys.modules` dictionary. Note that it fetches an *object*, which need not be a module. Importing a module for the -first time results in a module object being put in `sys.modules`, so usually +first time results in a module object being put in ``sys.modules``, so usually when you import something you get a module back. This need not be the case however. diff --git a/Doc/library/xml.dom.minidom.rst b/Doc/library/xml.dom.minidom.rst index 82e5d6aea2310e..72a7a98c2ac4f2 100644 --- a/Doc/library/xml.dom.minidom.rst +++ b/Doc/library/xml.dom.minidom.rst @@ -148,8 +148,8 @@ module documentation. This section lists the differences between the API and Similarly, explicitly stating the *standalone* argument causes the standalone document declarations to be added to the prologue of the XML document. - If the value is set to `True`, `standalone="yes"` is added, - otherwise it is set to `"no"`. + If the value is set to ``True``, ``standalone="yes"`` is added, + otherwise it is set to ``"no"``. Not stating the argument will omit the declaration from the document. .. versionchanged:: 3.8 diff --git a/Doc/library/xmlrpc.client.rst b/Doc/library/xmlrpc.client.rst index 8b09acd4bd3049..bd2c49a6edab7f 100644 --- a/Doc/library/xmlrpc.client.rst +++ b/Doc/library/xmlrpc.client.rst @@ -60,7 +60,7 @@ between conformable Python objects and XML on the wire. may be passed to calls. The *headers* parameter is an optional sequence of HTTP headers to send with each request, expressed as a sequence of 2-tuples representing the header - name and value. (e.g. `[('Header-Name', 'value')]`). + name and value. (e.g. ``[('Header-Name', 'value')]``). The obsolete *use_datetime* flag is similar to *use_builtin_types* but it applies only to date/time values. diff --git a/Doc/library/xmlrpc.server.rst b/Doc/library/xmlrpc.server.rst index 9778a859da1fbf..016369d2b89d2c 100644 --- a/Doc/library/xmlrpc.server.rst +++ b/Doc/library/xmlrpc.server.rst @@ -263,7 +263,7 @@ This ExampleService demo can be invoked from the command line:: The client that interacts with the above server is included in -`Lib/xmlrpc/client.py`:: +``Lib/xmlrpc/client.py``:: server = ServerProxy("http://localhost:8000") diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index a661e03b173498..11f49a8c33dc88 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1562,7 +1562,7 @@ built-in types. true). * Mappings (instances of :class:`dict`) compare equal if and only if they have - equal `(key, value)` pairs. Equality comparison of the keys and values + equal ``(key, value)`` pairs. Equality comparison of the keys and values enforces reflexivity. Order comparisons (``<``, ``>``, ``<=``, and ``>=``) raise :exc:`TypeError`. diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index b7a53cd0886f29..3fa875f52e013e 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -822,7 +822,7 @@ The path based finder iterates over every entry in the search path, and for each of these, looks for an appropriate :term:`path entry finder` (:class:`~importlib.abc.PathEntryFinder`) for the path entry. Because this can be an expensive operation (e.g. there may be -`stat()` call overheads for this search), the path based finder maintains +``stat()`` call overheads for this search), the path based finder maintains a cache mapping path entries to path entry finders. This cache is maintained in :data:`sys.path_importer_cache` (despite the name, this cache actually stores finder objects rather than being limited to :term:`importer` objects). diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 8311de0457f6af..5c9937fb5b6d72 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -994,20 +994,12 @@ The :keyword:`!nonlocal` statement .. productionlist:: python-grammar nonlocal_stmt: "nonlocal" `identifier` ("," `identifier`)* -.. XXX add when implemented - : ["=" (`target_list` "=")+ starred_expression] - : | "nonlocal" identifier augop expression_list - The :keyword:`nonlocal` statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope. -.. XXX not implemented - The :keyword:`nonlocal` statement may prepend an assignment or augmented - assignment, but not an expression. - Names listed in a :keyword:`nonlocal` statement, unlike those listed in a :keyword:`global` statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 960ac54d7b91ef..7f82dc32113a9d 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -10,7 +10,7 @@ blurb # sphinx-lint 0.6.2 yields many default role errors due to the new regular # expression used for default role detection, so we don't use the version # until the errors are fixed. -sphinx-lint==0.6.1 +sphinx-lint==0.6.4 # The theme used by the documentation is stored separately, so we need # to install that as well. diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index ec57c880ee7ad0..b99d9bdaa3f007 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -755,12 +755,12 @@ Compiler flags In particular, :envvar:`CFLAGS` should not contain: - * the compiler flag `-I` (for setting the search path for include files). - The `-I` flags are processed from left to right, and any flags in - :envvar:`CFLAGS` would take precedence over user- and package-supplied `-I` + * the compiler flag ``-I`` (for setting the search path for include files). + The ``-I`` flags are processed from left to right, and any flags in + :envvar:`CFLAGS` would take precedence over user- and package-supplied ``-I`` flags. - * hardening flags such as `-Werror` because distributions cannot control + * hardening flags such as ``-Werror`` because distributions cannot control whether packages installed by users conform to such heightened standards. @@ -878,9 +878,9 @@ Linker flags In particular, :envvar:`LDFLAGS` should not contain: - * the compiler flag `-L` (for setting the search path for libraries). - The `-L` flags are processed from left to right, and any flags in - :envvar:`LDFLAGS` would take precedence over user- and package-supplied `-L` + * the compiler flag ``-L`` (for setting the search path for libraries). + The ``-L`` flags are processed from left to right, and any flags in + :envvar:`LDFLAGS` would take precedence over user- and package-supplied ``-L`` flags. .. envvar:: CONFIGURE_LDFLAGS_NODIST diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst index 061cfa5be88f29..24c02c99f871d5 100644 --- a/Doc/using/unix.rst +++ b/Doc/using/unix.rst @@ -170,7 +170,7 @@ Custom OpenSSL $ popd 3. Build Python with custom OpenSSL - (see the configure `--with-openssl` and `--with-openssl-rpath` options) + (see the configure ``--with-openssl`` and ``--with-openssl-rpath`` options) .. code-block:: shell-session diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 9c339521c31151..b5c2c8ca712041 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -216,7 +216,7 @@ of available options is shown below. +---------------------------+--------------------------------------+--------------------------+ | Include_pip | Install bundled pip and setuptools | 1 | +---------------------------+--------------------------------------+--------------------------+ -| Include_symbols | Install debugging symbols (`*`.pdb) | 0 | +| Include_symbols | Install debugging symbols (``*.pdb``)| 0 | +---------------------------+--------------------------------------+--------------------------+ | Include_tcltk | Install Tcl/Tk support and IDLE | 1 | +---------------------------+--------------------------------------+--------------------------+ diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 731ce6aac6919d..34f2656f765c7d 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -717,13 +717,13 @@ This will produce the output:: PEP 3101: Advanced String Formatting ===================================================== -In Python 3.0, the `%` operator is supplemented by a more powerful string +In Python 3.0, the ``%`` operator is supplemented by a more powerful string formatting method, :meth:`format`. Support for the :meth:`str.format` method has been backported to Python 2.6. -In 2.6, both 8-bit and Unicode strings have a `.format()` method that +In 2.6, both 8-bit and Unicode strings have a ``.format()`` method that treats the string as a template and takes the arguments to be formatted. -The formatting template uses curly brackets (`{`, `}`) as special characters:: +The formatting template uses curly brackets (``{``, ``}``) as special characters:: >>> # Substitute positional argument 0 into the string. >>> "User ID: {0}".format("root") diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index e8f701d254cd28..276ab63b97f8a9 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -2485,8 +2485,8 @@ In the standard library: * The ElementTree library, :mod:`xml.etree`, no longer escapes ampersands and angle brackets when outputting an XML processing - instruction (which looks like ``) - or comment (which looks like ``). + instruction (which looks like ````) + or comment (which looks like ````). (Patch by Neil Muller; :issue:`2746`.) * The :meth:`~StringIO.StringIO.readline` method of :class:`~StringIO.StringIO` objects now does diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 428a19453db522..8beb8b19463f82 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -1184,7 +1184,7 @@ and will be incorrect in some rare cases, including some ``_``-s in New in 3.10 maintenance releases. -Apply syntax highlighting to `.pyi` files. (Contributed by Alex +Apply syntax highlighting to ``.pyi`` files. (Contributed by Alex Waygood and Terry Jan Reedy in :issue:`45447`.) Include prompts when saving Shell with inputs and outputs. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 6103da76a34d03..56a35f4e4802ba 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -723,7 +723,7 @@ hashlib IDLE and idlelib ---------------- -* Apply syntax highlighting to `.pyi` files. (Contributed by Alex +* Apply syntax highlighting to ``.pyi`` files. (Contributed by Alex Waygood and Terry Jan Reedy in :issue:`45447`.) * Include prompts when saving Shell with inputs and outputs. diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index d18c31fbe9866e..405de11e716b44 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -588,7 +588,7 @@ Porting to Python 3.12 ``tp_weaklistoffset``, respectively. The use of ``tp_dictoffset`` and ``tp_weaklistoffset`` is still supported, but does not fully support multiple inheritance - (:gh: `95589`), and performance may be worse. + (:gh:`95589`), and performance may be worse. Classes declaring :const:`Py_TPFLAGS_MANAGED_DICT` should call :c:func:`_PyObject_VisitManagedDict` and :c:func:`_PyObject_ClearManagedDict` to traverse and clear their instance's dictionaries. diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst index 65100b0be36a5a..6037db9f954d26 100644 --- a/Doc/whatsnew/3.2.rst +++ b/Doc/whatsnew/3.2.rst @@ -1746,7 +1746,7 @@ names. instead of module names for running specific tests (:issue:`10620`). The new test discovery can find tests within packages, locating any test importable from the top-level directory. The top-level directory can be specified with - the `-t` option, a pattern for matching files with ``-p``, and a directory to + the ``-t`` option, a pattern for matching files with ``-p``, and a directory to start discovery with ``-s``: .. code-block:: shell-session @@ -1857,7 +1857,7 @@ asyncore :class:`asyncore.dispatcher` now provides a :meth:`~asyncore.dispatcher.handle_accepted()` method -returning a `(sock, addr)` pair which is called when a connection has actually +returning a ``(sock, addr)`` pair which is called when a connection has actually been established with a new remote endpoint. This is supposed to be used as a replacement for old :meth:`~asyncore.dispatcher.handle_accept()` and avoids the user to call :meth:`~asyncore.dispatcher.accept()` directly. diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index fef1a8ac4c0101..96a632577b2c56 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -2389,10 +2389,10 @@ Porting Python code :attr:`sys.path_importer_cache` where it represents the use of implicit finders, but semantically it should not change anything. -* :class:`importlib.abc.Finder` no longer specifies a `find_module()` abstract +* :class:`importlib.abc.Finder` no longer specifies a ``find_module()`` abstract method that must be implemented. If you were relying on subclasses to implement that method, make sure to check for the method's existence first. - You will probably want to check for `find_loader()` first, though, in the + You will probably want to check for ``find_loader()`` first, though, in the case of working with :term:`path entry finders `. * :mod:`pkgutil` has been converted to use :mod:`importlib` internally. This diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 2c83c7ba3ad3b0..f872579ef546f5 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -2469,11 +2469,11 @@ Changes in the Python API ``opt-`` tag in ``.pyc`` file names. The :func:`importlib.util.cache_from_source` has gained an *optimization* parameter to help control the ``opt-`` tag. Because of this, the - *debug_override* parameter of the function is now deprecated. `.pyo` files + *debug_override* parameter of the function is now deprecated. ``.pyo`` files are also no longer supported as a file argument to the Python interpreter and thus serve no purpose when distributed on their own (i.e. sourceless code distribution). Due to the fact that the magic number for bytecode has changed - in Python 3.5, all old `.pyo` files from previous versions of Python are + in Python 3.5, all old ``.pyo`` files from previous versions of Python are invalid regardless of this PEP. * The :mod:`socket` module now exports the :data:`~socket.CAN_RAW_FD_FRAMES` diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index dfb56770ae7026..9308d1a76fec7a 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -960,8 +960,8 @@ contextlib The :class:`contextlib.AbstractContextManager` class has been added to provide an abstract base class for context managers. It provides a -sensible default implementation for `__enter__()` which returns -``self`` and leaves `__exit__()` an abstract method. A matching +sensible default implementation for ``__enter__()`` which returns +``self`` and leaves ``__exit__()`` an abstract method. A matching class has been added to the :mod:`typing` module as :class:`typing.ContextManager`. (Contributed by Brett Cannon in :issue:`25609`.) @@ -1388,7 +1388,7 @@ are treated as punctuation. site ---- -When specifying paths to add to :attr:`sys.path` in a `.pth` file, +When specifying paths to add to :attr:`sys.path` in a ``.pth`` file, you may now specify file paths on top of directories (e.g. zip files). (Contributed by Wolfgang Langner in :issue:`26587`). diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index f06cf29c713f99..df3b636cb9ec46 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -2497,7 +2497,7 @@ number of other issues). Some known details affected: * :c:func:`PySys_AddWarnOptionUnicode` is not currently usable by embedding applications due to the requirement to create a Unicode object prior to - calling `Py_Initialize`. Use :c:func:`PySys_AddWarnOption` instead. + calling ``Py_Initialize``. Use :c:func:`PySys_AddWarnOption` instead. * warnings filters added by an embedding application with :c:func:`PySys_AddWarnOption` should now more consistently take precedence diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index ff01a65772991f..624e71f9254c45 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -500,7 +500,7 @@ Reedy in :issue:`40468`.) Move the indent space setting from the Font tab to the new Windows tab. (Contributed by Mark Roseman and Terry Jan Reedy in :issue:`33962`.) -Apply syntax highlighting to `.pyi` files. (Contributed by Alex +Apply syntax highlighting to ``.pyi`` files. (Contributed by Alex Waygood and Terry Jan Reedy in :issue:`45447`.) imaplib diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-01-08-55-09.gh-issue-97591.pw6kkH.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-01-08-55-09.gh-issue-97591.pw6kkH.rst index d3a5867db7fce2..6f07529f15bba3 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-10-01-08-55-09.gh-issue-97591.pw6kkH.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-01-08-55-09.gh-issue-97591.pw6kkH.rst @@ -1,2 +1,2 @@ -Fixed a missing incref/decref pair in `Exception.__setstate__()`. +Fixed a missing incref/decref pair in ``Exception.__setstate__()``. Patch by Ofey Chan. diff --git a/Misc/NEWS.d/next/Documentation/2022-09-01-17-03-04.gh-issue-96432.1EJ1-4.rst b/Misc/NEWS.d/next/Documentation/2022-09-01-17-03-04.gh-issue-96432.1EJ1-4.rst index a5858f432932ca..c4d296e626b1b5 100644 --- a/Misc/NEWS.d/next/Documentation/2022-09-01-17-03-04.gh-issue-96432.1EJ1-4.rst +++ b/Misc/NEWS.d/next/Documentation/2022-09-01-17-03-04.gh-issue-96432.1EJ1-4.rst @@ -1,2 +1,2 @@ Fraction literals now support whitespace around the forward slash, -`Fraction('2 / 3')`. +``Fraction('2 / 3')``. diff --git a/Misc/NEWS.d/next/Library/2022-05-09-21-31-41.gh-issue-92445.tJosdm.rst b/Misc/NEWS.d/next/Library/2022-05-09-21-31-41.gh-issue-92445.tJosdm.rst index ba69a011601fd0..16bad6d34b1c0b 100644 --- a/Misc/NEWS.d/next/Library/2022-05-09-21-31-41.gh-issue-92445.tJosdm.rst +++ b/Misc/NEWS.d/next/Library/2022-05-09-21-31-41.gh-issue-92445.tJosdm.rst @@ -1,3 +1,3 @@ -Fix a bug in :mod:`argparse` where `nargs="*"` would raise an error instead of returning +Fix a bug in :mod:`argparse` where ``nargs="*"`` would raise an error instead of returning an empty list when 0 arguments were supplied if choice was also defined in ``parser.add_argument``. diff --git a/Misc/NEWS.d/next/Library/2022-07-06-14-57-33.gh-issue-94619.PRqKVX.rst b/Misc/NEWS.d/next/Library/2022-07-06-14-57-33.gh-issue-94619.PRqKVX.rst index a7905034424684..987ea6b045afda 100644 --- a/Misc/NEWS.d/next/Library/2022-07-06-14-57-33.gh-issue-94619.PRqKVX.rst +++ b/Misc/NEWS.d/next/Library/2022-07-06-14-57-33.gh-issue-94619.PRqKVX.rst @@ -1 +1 @@ -Remove the long-deprecated `module_repr()` from `importlib`. +Remove the long-deprecated ``module_repr()`` from :mod:`importlib`. From c729f2d285ccc8bdc5d798c316e48a7d9864fc04 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 6 Oct 2022 18:27:51 -0700 Subject: [PATCH 083/151] gh-82874: Convert remaining importlib format uses to f-str. (#98005) f-yes --- Lib/importlib/__init__.py | 18 ++++++--------- Lib/importlib/_bootstrap_external.py | 22 +++++++++---------- Lib/importlib/resources/_adapters.py | 2 +- Lib/importlib/util.py | 8 +++---- .../2019-11-04-22-21-27.bpo-38693.w_OAov.rst | 2 +- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py index ce61883288aa35..21d9dee652b3df 100644 --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -84,13 +84,13 @@ def find_loader(name, path=None): try: loader = sys.modules[name].__loader__ if loader is None: - raise ValueError('{}.__loader__ is None'.format(name)) + raise ValueError(f'{name}.__loader__ is None') else: return loader except KeyError: pass except AttributeError: - raise ValueError('{}.__loader__ is not set'.format(name)) from None + raise ValueError(f'{name}.__loader__ is not set') from None spec = _bootstrap._find_spec(name, path) # We won't worry about malformed specs (missing attributes). @@ -98,8 +98,7 @@ def find_loader(name, path=None): return None if spec.loader is None: if spec.submodule_search_locations is None: - raise ImportError('spec for {} missing loader'.format(name), - name=name) + raise ImportError(f'spec for {name} missing loader', name=name) raise ImportError('namespace packages do not have loaders', name=name) return spec.loader @@ -116,9 +115,8 @@ def import_module(name, package=None): level = 0 if name.startswith('.'): if not package: - msg = ("the 'package' argument is required to perform a relative " - "import for {!r}") - raise TypeError(msg.format(name)) + raise TypeError("the 'package' argument is required to perform a " + f"relative import for {name!r}") for character in name: if character != '.': break @@ -144,8 +142,7 @@ def reload(module): raise TypeError("reload() argument must be a module") if sys.modules.get(name) is not module: - msg = "module {} not in sys.modules" - raise ImportError(msg.format(name), name=name) + raise ImportError(f"module {name} not in sys.modules", name=name) if name in _RELOADING: return _RELOADING[name] _RELOADING[name] = module @@ -155,8 +152,7 @@ def reload(module): try: parent = sys.modules[parent_name] except KeyError: - msg = "parent {!r} not in sys.modules" - raise ImportError(msg.format(parent_name), + raise ImportError(f"parent {parent_name!r} not in sys.modules", name=parent_name) from None else: pkgpath = parent.__path__ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index efda49382540c5..39d63ae9d6e98c 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -197,7 +197,7 @@ def _write_atomic(path, data, mode=0o666): Be prepared to handle a FileExistsError if concurrent writing of the temporary file is attempted.""" # id() is used to generate a pseudo-random filename. - path_tmp = '{}.{}'.format(path, id(path)) + path_tmp = f'{path}.{id(path)}' fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY, mode & 0o666) try: @@ -492,8 +492,8 @@ def cache_from_source(path, debug_override=None, *, optimization=None): optimization = str(optimization) if optimization != '': if not optimization.isalnum(): - raise ValueError('{!r} is not alphanumeric'.format(optimization)) - almost_filename = '{}.{}{}'.format(almost_filename, _OPT, optimization) + raise ValueError(f'{optimization!r} is not alphanumeric') + almost_filename = f'{almost_filename}.{_OPT}{optimization}' filename = almost_filename + BYTECODE_SUFFIXES[0] if sys.pycache_prefix is not None: # We need an absolute path to the py file to avoid the possibility of @@ -651,8 +651,8 @@ def _find_module_shim(self, fullname): # return None. loader, portions = self.find_loader(fullname) if loader is None and len(portions): - msg = 'Not importing directory {}: missing __init__' - _warnings.warn(msg.format(portions[0]), ImportWarning) + msg = f'Not importing directory {portions[0]}: missing __init__' + _warnings.warn(msg, ImportWarning) return loader @@ -750,7 +750,7 @@ def _compile_bytecode(data, name=None, bytecode_path=None, source_path=None): _imp._fix_co_filename(code, source_path) return code else: - raise ImportError('Non-code object in {!r}'.format(bytecode_path), + raise ImportError(f'Non-code object in {bytecode_path!r}', name=name, path=bytecode_path) @@ -951,8 +951,8 @@ def exec_module(self, module): """Execute the module.""" code = self.get_code(module.__name__) if code is None: - raise ImportError('cannot load module {!r} when get_code() ' - 'returns None'.format(module.__name__)) + raise ImportError(f'cannot load module {module.__name__!r} when ' + 'get_code() returns None') _bootstrap._call_with_frames_removed(exec, code, module.__dict__) def load_module(self, fullname): @@ -1337,7 +1337,7 @@ def __len__(self): return len(self._recalculate()) def __repr__(self): - return '_NamespacePath({!r})'.format(self._path) + return f'_NamespacePath({self._path!r})' def __contains__(self, item): return item in self._recalculate() @@ -1678,7 +1678,7 @@ def _fill_cache(self): for item in contents: name, dot, suffix = item.partition('.') if dot: - new_name = '{}.{}'.format(name, suffix.lower()) + new_name = f'{name}.{suffix.lower()}' else: new_name = name lower_suffix_contents.add(new_name) @@ -1705,7 +1705,7 @@ def path_hook_for_FileFinder(path): return path_hook_for_FileFinder def __repr__(self): - return 'FileFinder({!r})'.format(self.path) + return f'FileFinder({self.path!r})' # Import setup ############################################################### diff --git a/Lib/importlib/resources/_adapters.py b/Lib/importlib/resources/_adapters.py index ea363d86a564b5..f22f6bc509a3d7 100644 --- a/Lib/importlib/resources/_adapters.py +++ b/Lib/importlib/resources/_adapters.py @@ -35,7 +35,7 @@ def _io_wrapper(file, mode='r', *args, **kwargs): elif mode == 'rb': return file raise ValueError( - "Invalid mode value '{}', only 'r' and 'rb' are supported".format(mode) + f"Invalid mode value '{mode}', only 'r' and 'rb' are supported" ) diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index 9e29c581b1db33..5294578cc26cf3 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -60,10 +60,10 @@ def _find_spec_from_path(name, path=None): try: spec = module.__spec__ except AttributeError: - raise ValueError('{}.__spec__ is not set'.format(name)) from None + raise ValueError(f'{name}.__spec__ is not set') from None else: if spec is None: - raise ValueError('{}.__spec__ is None'.format(name)) + raise ValueError(f'{name}.__spec__ is None') return spec @@ -105,10 +105,10 @@ def find_spec(name, package=None): try: spec = module.__spec__ except AttributeError: - raise ValueError('{}.__spec__ is not set'.format(name)) from None + raise ValueError(f'{name}.__spec__ is not set') from None else: if spec is None: - raise ValueError('{}.__spec__ is None'.format(name)) + raise ValueError(f'{name}.__spec__ is None') return spec diff --git a/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst b/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst index 6d0ad36809ee77..a81e9220a8e6d3 100644 --- a/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst +++ b/Misc/NEWS.d/next/Library/2019-11-04-22-21-27.bpo-38693.w_OAov.rst @@ -1 +1 @@ -importlib now uses f-strings internally instead of str.format(). +:mod:`importlib` now uses f-strings internally instead of ``str.format``. From 2781fef8c56733a98e61e9d876714ee0a4b22445 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 6 Oct 2022 19:32:53 -0700 Subject: [PATCH 084/151] gh-86298: Ensure that __loader__ and __spec__.loader agree in warnings.warn_explicit() (GH-97803) In `_warnings.c`, in the C equivalent of `warnings.warn_explicit()`, if the module globals are given (and not None), the warning will attempt to get the source line for the issued warning. To do this, it needs the module's loader. Previously, it would only look up `__loader__` in the module globals. In https://github.com/python/cpython/issues/86298 we want to defer to the `__spec__.loader` if available. The first step on this journey is to check that `loader == __spec__.loader` and issue another warning if it is not. This commit does that. Since this is a PoC, only manual testing for now. ```python # /tmp/foo.py import warnings import bar warnings.warn_explicit( 'warning!', RuntimeWarning, 'bar.py', 2, module='bar knee', module_globals=bar.__dict__, ) ``` ```python # /tmp/bar.py import sys import os import pathlib # __loader__ = pathlib.Path() ``` Then running this: `./python.exe -Wdefault /tmp/foo.py` Produces: ``` bar.py:2: RuntimeWarning: warning! import os ``` Uncomment the `__loader__ = ` line in `bar.py` and try it again: ``` sys:1: ImportWarning: Module bar; __loader__ != __spec__.loader (<_frozen_importlib_external.SourceFileLoader object at 0x109f7dfa0> != PosixPath('.')) bar.py:2: RuntimeWarning: warning! import os ``` Automerge-Triggered-By: GH:warsaw --- Doc/reference/import.rst | 8 ++ Lib/importlib/_bootstrap_external.py | 48 ++++++++ .../test_importlib/import_/test_helpers.py | 113 ++++++++++++++++++ ...2-10-04-14-04-40.gh-issue-86298.QVM7G1.rst | 3 + Python/_warnings.c | 13 +- 5 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-04-14-04-40.gh-issue-86298.QVM7G1.rst diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst index 3fa875f52e013e..b22b5251f1de46 100644 --- a/Doc/reference/import.rst +++ b/Doc/reference/import.rst @@ -558,6 +558,11 @@ listed below. It is **strongly** recommended that you rely on :attr:`__spec__` instead instead of this attribute. + .. versionchanged:: 3.12 + The value of ``__loader__`` is expected to be the same as + ``__spec__.loader``. The use of ``__loader__`` is deprecated and slated + for removal in Python 3.14. + .. attribute:: __package__ The module's ``__package__`` attribute may be set. Its value must @@ -568,6 +573,9 @@ listed below. submodules, to the parent package's name. See :pep:`366` for further details. + This attribute is used instead of ``__name__`` to calculate explicit + relative imports for main modules, as defined in :pep:`366`. + It is **strongly** recommended that you rely on :attr:`__spec__` instead instead of this attribute. diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 39d63ae9d6e98c..fc50e70539cacf 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -862,6 +862,54 @@ def spec_from_file_location(name, location=None, *, loader=None, return spec +def _bless_my_loader(module_globals): + """Helper function for _warnings.c + + See GH#97850 for details. + """ + # 2022-10-06(warsaw): For now, this helper is only used in _warnings.c and + # that use case only has the module globals. This function could be + # extended to accept either that or a module object. However, in the + # latter case, it would be better to raise certain exceptions when looking + # at a module, which should have either a __loader__ or __spec__.loader. + # For backward compatibility, it is possible that we'll get an empty + # dictionary for the module globals, and that cannot raise an exception. + if not isinstance(module_globals, dict): + return None + + missing = object() + loader = module_globals.get('__loader__', None) + spec = module_globals.get('__spec__', missing) + + if loader is None: + if spec is missing: + # If working with a module: + # raise AttributeError('Module globals is missing a __spec__') + return None + elif spec is None: + raise ValueError('Module globals is missing a __spec__.loader') + + spec_loader = getattr(spec, 'loader', missing) + + if spec_loader in (missing, None): + if loader is None: + exc = AttributeError if spec_loader is missing else ValueError + raise exc('Module globals is missing a __spec__.loader') + _warnings.warn( + 'Module globals is missing a __spec__.loader', + DeprecationWarning) + spec_loader = loader + + assert spec_loader is not None + if loader is not None and loader != spec_loader: + _warnings.warn( + 'Module globals; __loader__ != __spec__.loader', + DeprecationWarning) + return loader + + return spec_loader + + # Loaders ##################################################################### class WindowsRegistryFinder: diff --git a/Lib/test/test_importlib/import_/test_helpers.py b/Lib/test/test_importlib/import_/test_helpers.py index 90df56f09fe52d..550f88d1d7a651 100644 --- a/Lib/test/test_importlib/import_/test_helpers.py +++ b/Lib/test/test_importlib/import_/test_helpers.py @@ -2,7 +2,9 @@ from importlib import _bootstrap_external, machinery import os.path +from types import ModuleType, SimpleNamespace import unittest +import warnings from .. import util @@ -67,5 +69,116 @@ def test_no_loader_no_spec_but_source(self): FrozenFixUpModuleTests, SourceFixUpModuleTests = util.test_both(FixUpModuleTests) + +class TestBlessMyLoader(unittest.TestCase): + # GH#86298 is part of the migration away from module attributes and toward + # __spec__ attributes. There are several cases to test here. This will + # have to change in Python 3.14 when we actually remove/ignore __loader__ + # in favor of requiring __spec__.loader. + + def test_gh86298_no_loader_and_no_spec(self): + bar = ModuleType('bar') + del bar.__loader__ + del bar.__spec__ + # 2022-10-06(warsaw): For backward compatibility with the + # implementation in _warnings.c, this can't raise an + # AttributeError. See _bless_my_loader() in _bootstrap_external.py + # If working with a module: + ## self.assertRaises( + ## AttributeError, _bootstrap_external._bless_my_loader, + ## bar.__dict__) + self.assertIsNone(_bootstrap_external._bless_my_loader(bar.__dict__)) + + def test_gh86298_loader_is_none_and_no_spec(self): + bar = ModuleType('bar') + bar.__loader__ = None + del bar.__spec__ + # 2022-10-06(warsaw): For backward compatibility with the + # implementation in _warnings.c, this can't raise an + # AttributeError. See _bless_my_loader() in _bootstrap_external.py + # If working with a module: + ## self.assertRaises( + ## AttributeError, _bootstrap_external._bless_my_loader, + ## bar.__dict__) + self.assertIsNone(_bootstrap_external._bless_my_loader(bar.__dict__)) + + def test_gh86298_no_loader_and_spec_is_none(self): + bar = ModuleType('bar') + del bar.__loader__ + bar.__spec__ = None + self.assertRaises( + ValueError, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_loader_is_none_and_spec_is_none(self): + bar = ModuleType('bar') + bar.__loader__ = None + bar.__spec__ = None + self.assertRaises( + ValueError, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_loader_is_none_and_spec_loader_is_none(self): + bar = ModuleType('bar') + bar.__loader__ = None + bar.__spec__ = SimpleNamespace(loader=None) + self.assertRaises( + ValueError, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_no_spec(self): + bar = ModuleType('bar') + bar.__loader__ = object() + del bar.__spec__ + with warnings.catch_warnings(): + self.assertWarns( + DeprecationWarning, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_spec_is_none(self): + bar = ModuleType('bar') + bar.__loader__ = object() + bar.__spec__ = None + with warnings.catch_warnings(): + self.assertWarns( + DeprecationWarning, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_no_spec_loader(self): + bar = ModuleType('bar') + bar.__loader__ = object() + bar.__spec__ = SimpleNamespace() + with warnings.catch_warnings(): + self.assertWarns( + DeprecationWarning, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_loader_and_spec_loader_disagree(self): + bar = ModuleType('bar') + bar.__loader__ = object() + bar.__spec__ = SimpleNamespace(loader=object()) + with warnings.catch_warnings(): + self.assertWarns( + DeprecationWarning, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_no_loader_and_no_spec_loader(self): + bar = ModuleType('bar') + del bar.__loader__ + bar.__spec__ = SimpleNamespace() + self.assertRaises( + AttributeError, + _bootstrap_external._bless_my_loader, bar.__dict__) + + def test_gh86298_no_loader_with_spec_loader_okay(self): + bar = ModuleType('bar') + del bar.__loader__ + loader = object() + bar.__spec__ = SimpleNamespace(loader=loader) + self.assertEqual( + _bootstrap_external._bless_my_loader(bar.__dict__), + loader) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-04-14-04-40.gh-issue-86298.QVM7G1.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-04-14-04-40.gh-issue-86298.QVM7G1.rst new file mode 100644 index 00000000000000..6e349d56c99f25 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-04-14-04-40.gh-issue-86298.QVM7G1.rst @@ -0,0 +1,3 @@ +In cases where ``warnings.warn_explicit()`` consults the module's loader, an +``DeprecationWarning`` is issued when ``m.__loader__`` differs from +``m.__spec__.loader``. diff --git a/Python/_warnings.c b/Python/_warnings.c index 1b9e107ea30b13..0d4c50f769b03c 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -977,6 +977,7 @@ warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category, static PyObject * get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno) { + PyObject *external; PyObject *loader; PyObject *module_name; PyObject *get_source; @@ -984,12 +985,18 @@ get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno PyObject *source_list; PyObject *source_line; - /* Check/get the requisite pieces needed for the loader. */ - loader = _PyDict_GetItemWithError(module_globals, &_Py_ID(__loader__)); + /* stolen from import.c */ + external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); + if (external == NULL) { + return NULL; + } + + loader = PyObject_CallMethod(external, "_bless_my_loader", "O", module_globals, NULL); + Py_DECREF(external); if (loader == NULL) { return NULL; } - Py_INCREF(loader); + module_name = _PyDict_GetItemWithError(module_globals, &_Py_ID(__name__)); if (!module_name) { Py_DECREF(loader); From 6fe8abf838e3e8a33e72a739efe9323d007273db Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Fri, 7 Oct 2022 09:10:51 +0200 Subject: [PATCH 085/151] Doc: sphinx-lint finds two other default roles. (GH-98019) --- Doc/library/functools.rst | 4 ++-- Doc/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 943a05c39d6840..2f0a9bd8be8815 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -150,7 +150,7 @@ The :mod:`functools` module defines the following functions: arguments to the function must be hashable. Distinct argument patterns may be considered to be distinct calls with - separate cache entries. For example, `f(a=1, b=2)` and `f(b=2, a=1)` + separate cache entries. For example, ``f(a=1, b=2)`` and ``f(b=2, a=1)`` differ in their keyword argument order and may have two separate cache entries. @@ -197,7 +197,7 @@ The :mod:`functools` module defines the following functions: The cache keeps references to the arguments and return values until they age out of the cache or until the cache is cleared. - If a method is cached, the `self` instance argument is included in the + If a method is cached, the ``self`` instance argument is included in the cache. See :ref:`faq-cache-method-calls` An `LRU (least recently used) cache diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 7f82dc32113a9d..32c53e95917ee4 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -10,7 +10,7 @@ blurb # sphinx-lint 0.6.2 yields many default role errors due to the new regular # expression used for default role detection, so we don't use the version # until the errors are fixed. -sphinx-lint==0.6.4 +sphinx-lint==0.6.5 # The theme used by the documentation is stored separately, so we need # to install that as well. From a1ce6e1cd7ab4f7386744490fd4fdc5fd1461b5d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 7 Oct 2022 03:37:21 -0500 Subject: [PATCH 086/151] Misc updates to the itertools recipes and tests (GH-98018) --- Doc/library/itertools.rst | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index b83163560f4d9a..6571114ef311f9 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -775,10 +775,7 @@ which incur interpreter overhead. return sum(map(pred, iterable)) def pad_none(iterable): - """Returns the sequence elements and then returns None indefinitely. - - Useful for emulating the behavior of the built-in map() function. - """ + "Returns the sequence elements and then returns None indefinitely." return chain(iterable, repeat(None)) def ncycles(iterable, n): @@ -850,6 +847,13 @@ which incur interpreter overhead. else: raise ValueError('Expected fill, strict, or ignore') + def batched(iterable, n): + "Batch data into lists of length n. The last batch may be shorter." + # batched('ABCDEFG', 3) --> ABC DEF G + it = iter(iterable) + while (batch := list(islice(it, n))): + yield batch + def triplewise(iterable): "Return overlapping triplets from an iterable" # triplewise('ABCDEFG') --> ABC BCD CDE DEF EFG @@ -1168,8 +1172,8 @@ which incur interpreter overhead. >>> list(sieve(30)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - >>> small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59] - >>> all(list(sieve(n)) == [p for p in small_primes if p < n] for n in range(60)) + >>> small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] + >>> all(list(sieve(n)) == [p for p in small_primes if p < n] for n in range(101)) True >>> len(list(sieve(100))) 25 @@ -1212,6 +1216,36 @@ which incur interpreter overhead. >>> list(grouper('abcdefg', n=3, incomplete='ignore')) [('a', 'b', 'c'), ('d', 'e', 'f')] + >>> list(batched('ABCDEFG', 3)) + [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] + >>> list(batched('ABCDEF', 3)) + [['A', 'B', 'C'], ['D', 'E', 'F']] + >>> list(batched('ABCDE', 3)) + [['A', 'B', 'C'], ['D', 'E']] + >>> list(batched('ABCD', 3)) + [['A', 'B', 'C'], ['D']] + >>> list(batched('ABC', 3)) + [['A', 'B', 'C']] + >>> list(batched('AB', 3)) + [['A', 'B']] + >>> list(batched('A', 3)) + [['A']] + >>> list(batched('', 3)) + [] + >>> list(batched('ABCDEFG', 2)) + [['A', 'B'], ['C', 'D'], ['E', 'F'], ['G']] + >>> list(batched('ABCDEFG', 1)) + [['A'], ['B'], ['C'], ['D'], ['E'], ['F'], ['G']] + >>> list(batched('ABCDEFG', 0)) + [] + >>> list(batched('ABCDEFG', -1)) + Traceback (most recent call last): + ... + ValueError: Stop argument for islice() must be None or an integer: 0 <= x <= sys.maxsize. + >>> s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + >>> all(list(flatten(batched(s[:n], 5))) == list(s[:n]) for n in range(len(s))) + True + >>> list(triplewise('ABCDEFG')) [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E'), ('D', 'E', 'F'), ('E', 'F', 'G')] From 81021bf4c16891ab4811907651435a643aa151f9 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Fri, 7 Oct 2022 09:54:21 -0400 Subject: [PATCH 087/151] gh-71316: Update dis documentation to include changes to jump arguments (GH-95798) --- Doc/library/dis.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 30a336197c397d..33df7be4363423 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -30,6 +30,10 @@ interpreter. Use 2 bytes for each instruction. Previously the number of bytes varied by instruction. + .. versionchanged:: 3.10 + The argument of jump, exception handling and loop instructions is now + the instruction offset rather than the byte offset. + .. versionchanged:: 3.11 Some instructions are accompanied by one or more inline cache entries, which take the form of :opcode:`CACHE` instructions. These instructions From e75ada1ca771c92d7f901cec3640ce4367ffb420 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 7 Oct 2022 18:14:28 +0400 Subject: [PATCH 088/151] gh-97983: Revert "Lay the foundation for further work in asyncio.test_streams: port server cases to IsolatedAsyncioTestCase" (#98015) This PR reverts gh-93369 and gh-97896 because they've made asyncio tests unstable. After these PRs were merged, random GitHub action jobs of random commits started to fail unrelated tests and test framework methods. The reverting is necessary because such shrapnel failures are a symptom of some underlying bug that must be found and fixed first. I had a hope that it's a server overload because we already have extremely rare disc access errors. However, one and a half day passed, and the failures continue to emerge both in PRs and commits. Affected issue: gh-93357. First reported in https://github.com/python/cpython/pull/97940#issuecomment-1270004134. * Revert "gh-93357: Port test cases to IsolatedAsyncioTestCase, part 2 (#97896)" This reverts commit 09aea94d291fed2f3e96558dcd6db04014c3e2fb. * Revert "gh-93357: Start porting asyncio server test cases to IsolatedAsyncioTestCase (#93369)" This reverts commit ce8fc186ac81bce1727bf4192205148daabf5c2e. --- Lib/test/test_asyncio/test_streams.py | 326 ++++++++++++++++---------- 1 file changed, 197 insertions(+), 129 deletions(-) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 61d5e984dfbfbb..0c49099bc499a5 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -566,10 +566,46 @@ def test_exception_cancel(self): test_utils.run_briefly(self.loop) self.assertIs(stream._waiter, None) - -class NewStreamTests(unittest.IsolatedAsyncioTestCase): - - async def test_start_server(self): + def test_start_server(self): + + class MyServer: + + def __init__(self, loop): + self.server = None + self.loop = loop + + async def handle_client(self, client_reader, client_writer): + data = await client_reader.readline() + client_writer.write(data) + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + def start(self): + sock = socket.create_server(('127.0.0.1', 0)) + self.server = self.loop.run_until_complete( + asyncio.start_server(self.handle_client, + sock=sock)) + return sock.getsockname() + + def handle_client_callback(self, client_reader, client_writer): + self.loop.create_task(self.handle_client(client_reader, + client_writer)) + + def start_callback(self): + sock = socket.create_server(('127.0.0.1', 0)) + addr = sock.getsockname() + sock.close() + self.server = self.loop.run_until_complete( + asyncio.start_server(self.handle_client_callback, + host=addr[0], port=addr[1])) + return addr + + def stop(self): + if self.server is not None: + self.server.close() + self.loop.run_until_complete(self.server.wait_closed()) + self.server = None async def client(addr): reader, writer = await asyncio.open_connection(*addr) @@ -581,43 +617,61 @@ async def client(addr): await writer.wait_closed() return msgback - async def handle_client(client_reader, client_writer): - data = await client_reader.readline() - client_writer.write(data) - await client_writer.drain() - client_writer.close() - await client_writer.wait_closed() - - with self.subTest(msg="coroutine"): - server = await asyncio.start_server( - handle_client, - host=socket_helper.HOSTv4 - ) - addr = server.sockets[0].getsockname() - msg = await client(addr) - server.close() - await server.wait_closed() - self.assertEqual(msg, b"hello world!\n") + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - with self.subTest(msg="callback"): - async def handle_client_callback(client_reader, client_writer): - asyncio.get_running_loop().create_task( - handle_client(client_reader, client_writer) - ) + # test the server variant with a coroutine as client handler + server = MyServer(self.loop) + addr = server.start() + msg = self.loop.run_until_complete(self.loop.create_task(client(addr))) + server.stop() + self.assertEqual(msg, b"hello world!\n") - server = await asyncio.start_server( - handle_client_callback, - host=socket_helper.HOSTv4 - ) - addr = server.sockets[0].getsockname() - reader, writer = await asyncio.open_connection(*addr) - msg = await client(addr) - server.close() - await server.wait_closed() - self.assertEqual(msg, b"hello world!\n") + # test the server variant with a callback as client handler + server = MyServer(self.loop) + addr = server.start_callback() + msg = self.loop.run_until_complete(self.loop.create_task(client(addr))) + server.stop() + self.assertEqual(msg, b"hello world!\n") + + self.assertEqual(messages, []) @socket_helper.skip_unless_bind_unix_socket - async def test_start_unix_server(self): + def test_start_unix_server(self): + + class MyServer: + + def __init__(self, loop, path): + self.server = None + self.loop = loop + self.path = path + + async def handle_client(self, client_reader, client_writer): + data = await client_reader.readline() + client_writer.write(data) + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + def start(self): + self.server = self.loop.run_until_complete( + asyncio.start_unix_server(self.handle_client, + path=self.path)) + + def handle_client_callback(self, client_reader, client_writer): + self.loop.create_task(self.handle_client(client_reader, + client_writer)) + + def start_callback(self): + start = asyncio.start_unix_server(self.handle_client_callback, + path=self.path) + self.server = self.loop.run_until_complete(start) + + def stop(self): + if self.server is not None: + self.server.close() + self.loop.run_until_complete(self.server.wait_closed()) + self.server = None async def client(path): reader, writer = await asyncio.open_unix_connection(path) @@ -629,42 +683,64 @@ async def client(path): await writer.wait_closed() return msgback - async def handle_client(client_reader, client_writer): - data = await client_reader.readline() - client_writer.write(data) - await client_writer.drain() - client_writer.close() - await client_writer.wait_closed() - - with self.subTest(msg="coroutine"): - with test_utils.unix_socket_path() as path: - server = await asyncio.start_unix_server( - handle_client, - path=path - ) - msg = await client(path) - server.close() - await server.wait_closed() - self.assertEqual(msg, b"hello world!\n") - - with self.subTest(msg="callback"): - async def handle_client_callback(client_reader, client_writer): - asyncio.get_running_loop().create_task( - handle_client(client_reader, client_writer) - ) - - with test_utils.unix_socket_path() as path: - server = await asyncio.start_unix_server( - handle_client_callback, - path=path - ) - msg = await client(path) - server.close() - await server.wait_closed() - self.assertEqual(msg, b"hello world!\n") + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) + + # test the server variant with a coroutine as client handler + with test_utils.unix_socket_path() as path: + server = MyServer(self.loop, path) + server.start() + msg = self.loop.run_until_complete( + self.loop.create_task(client(path))) + server.stop() + self.assertEqual(msg, b"hello world!\n") + + # test the server variant with a callback as client handler + with test_utils.unix_socket_path() as path: + server = MyServer(self.loop, path) + server.start_callback() + msg = self.loop.run_until_complete( + self.loop.create_task(client(path))) + server.stop() + self.assertEqual(msg, b"hello world!\n") + + self.assertEqual(messages, []) @unittest.skipIf(ssl is None, 'No ssl module') - async def test_start_tls(self): + def test_start_tls(self): + + class MyServer: + + def __init__(self, loop): + self.server = None + self.loop = loop + + async def handle_client(self, client_reader, client_writer): + data1 = await client_reader.readline() + client_writer.write(data1) + await client_writer.drain() + assert client_writer.get_extra_info('sslcontext') is None + await client_writer.start_tls( + test_utils.simple_server_sslcontext()) + assert client_writer.get_extra_info('sslcontext') is not None + data2 = await client_reader.readline() + client_writer.write(data2) + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + def start(self): + sock = socket.create_server(('127.0.0.1', 0)) + self.server = self.loop.run_until_complete( + asyncio.start_server(self.handle_client, + sock=sock)) + return sock.getsockname() + + def stop(self): + if self.server is not None: + self.server.close() + self.loop.run_until_complete(self.server.wait_closed()) + self.server = None async def client(addr): reader, writer = await asyncio.open_connection(*addr) @@ -681,48 +757,17 @@ async def client(addr): await writer.wait_closed() return msgback1, msgback2 - async def handle_client(client_reader, client_writer): - data1 = await client_reader.readline() - client_writer.write(data1) - await client_writer.drain() - assert client_writer.get_extra_info('sslcontext') is None - await client_writer.start_tls( - test_utils.simple_server_sslcontext()) - assert client_writer.get_extra_info('sslcontext') is not None - - data2 = await client_reader.readline() - client_writer.write(data2) - await client_writer.drain() - client_writer.close() - await client_writer.wait_closed() - - server = await asyncio.start_server( - handle_client, - host=socket_helper.HOSTv4 - ) - addr = server.sockets[0].getsockname() - - msg1, msg2 = await client(addr) - server.close() - await server.wait_closed() - self.assertEqual(msg1, b"hello world 1!\n") - self.assertEqual(msg2, b"hello world 2!\n") - - -class StreamTests2(test_utils.TestCase): - - def setUp(self): - super().setUp() - self.loop = asyncio.new_event_loop() - self.set_event_loop(self.loop) + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) - def tearDown(self): - # just in case if we have transport close callbacks - test_utils.run_briefly(self.loop) + server = MyServer(self.loop) + addr = server.start() + msg1, msg2 = self.loop.run_until_complete(client(addr)) + server.stop() - self.loop.close() - gc.collect() - super().tearDown() + self.assertEqual(messages, []) + self.assertEqual(msg1, b"hello world 1!\n") + self.assertEqual(msg2, b"hello world 2!\n") @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") def test_read_all_from_pipe_reader(self): @@ -941,32 +986,36 @@ def test_LimitOverrunError_pickleable(self): self.assertEqual(str(e), str(e2)) self.assertEqual(e.consumed, e2.consumed) -class NewStreamTests2(unittest.IsolatedAsyncioTestCase): - async def test_wait_closed_on_close(self): + def test_wait_closed_on_close(self): with test_utils.run_test_server() as httpd: - rd, wr = await asyncio.open_connection(*httpd.address) + rd, wr = self.loop.run_until_complete( + asyncio.open_connection(*httpd.address)) wr.write(b'GET / HTTP/1.0\r\n\r\n') - data = await rd.readline() + f = rd.readline() + data = self.loop.run_until_complete(f) self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') - data = await rd.read() + f = rd.read() + data = self.loop.run_until_complete(f) self.assertTrue(data.endswith(b'\r\n\r\nTest message')) self.assertFalse(wr.is_closing()) wr.close() self.assertTrue(wr.is_closing()) - await wr.wait_closed() + self.loop.run_until_complete(wr.wait_closed()) - async def test_wait_closed_on_close_with_unread_data(self): + def test_wait_closed_on_close_with_unread_data(self): with test_utils.run_test_server() as httpd: - rd, wr = await asyncio.open_connection(*httpd.address) + rd, wr = self.loop.run_until_complete( + asyncio.open_connection(*httpd.address)) wr.write(b'GET / HTTP/1.0\r\n\r\n') - data = await rd.readline() + f = rd.readline() + data = self.loop.run_until_complete(f) self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') wr.close() - await wr.wait_closed() + self.loop.run_until_complete(wr.wait_closed()) - async def test_async_writer_api(self): + def test_async_writer_api(self): async def inner(httpd): rd, wr = await asyncio.open_connection(*httpd.address) @@ -978,10 +1027,15 @@ async def inner(httpd): wr.close() await wr.wait_closed() + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) + with test_utils.run_test_server() as httpd: - await inner(httpd) + self.loop.run_until_complete(inner(httpd)) - async def test_async_writer_api_exception_after_close(self): + self.assertEqual(messages, []) + + def test_async_writer_api_exception_after_close(self): async def inner(httpd): rd, wr = await asyncio.open_connection(*httpd.address) @@ -995,19 +1049,33 @@ async def inner(httpd): wr.write(b'data') await wr.drain() + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) + with test_utils.run_test_server() as httpd: - await inner(httpd) + self.loop.run_until_complete(inner(httpd)) - async def test_eof_feed_when_closing_writer(self): + self.assertEqual(messages, []) + + def test_eof_feed_when_closing_writer(self): # See http://bugs.python.org/issue35065 + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) + with test_utils.run_test_server() as httpd: - rd, wr = await asyncio.open_connection(*httpd.address) + rd, wr = self.loop.run_until_complete( + asyncio.open_connection(*httpd.address)) + wr.close() - await wr.wait_closed() + f = wr.wait_closed() + self.loop.run_until_complete(f) self.assertTrue(rd.at_eof()) - data = await rd.read() + f = rd.read() + data = self.loop.run_until_complete(f) self.assertEqual(data, b'') + self.assertEqual(messages, []) + if __name__ == '__main__': unittest.main() From 43583f729321b04db5ba6e1979229347d4f12490 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 7 Oct 2022 08:17:41 -0700 Subject: [PATCH 089/151] Fix memory leaks in test_capi (#98017) --- Lib/test/test_capi.py | 3 +++ Modules/_testcapimodule.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index cb90d55941cae7..19367dfcc1ccbb 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -1495,6 +1495,9 @@ def unraisable_hook(unraisable): unraisable = unraisables[0] self.assertIs(unraisable.object, d) self.assertEqual(str(unraisable.exc_value), "boom!") + # avoid leaking reference cycles + del unraisable + del unraisables def test_two_watchers(self): d1 = {} diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index fd5734cedae9a9..7dcd6bb7470ead 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5210,6 +5210,7 @@ dict_watch_callback(PyDict_WatchEvent event, Py_DECREF(msg); return -1; } + Py_DECREF(msg); return 0; } @@ -5224,8 +5225,10 @@ dict_watch_callback_second(PyDict_WatchEvent event, return -1; } if (PyList_Append(g_dict_watch_events, msg) < 0) { + Py_DECREF(msg); return -1; } + Py_DECREF(msg); return 0; } From f100fc33c710ce5a0e3c170c079392488951db11 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 19:53:42 +0300 Subject: [PATCH 090/151] gh-94808: Cover `%p` in `PyUnicode_FromFormat` (#96677) Co-authored-by: Jelle Zijlstra --- Lib/test/test_unicode.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 30faaaf83bec96..b9ee9d30318c46 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2807,6 +2807,25 @@ def check_format(expected, format, *args): check_format('repr=abc', b'repr=%V', 'abc', b'xyz') + # test %p + # We cannot test the exact result, + # because it returns a hex representation of a C pointer, + # which is going to be different each time. But, we can test the format. + p_format_regex = r'^0x[a-zA-Z0-9]{8,}$' + p_format1 = PyUnicode_FromFormat(b'%p', 'abc') + self.assertIsInstance(p_format1, str) + self.assertRegex(p_format1, p_format_regex) + + p_format2 = PyUnicode_FromFormat(b'%p %p', '123456', b'xyz') + self.assertIsInstance(p_format2, str) + self.assertRegex(p_format2, + r'0x[a-zA-Z0-9]{8,} 0x[a-zA-Z0-9]{8,}') + + # Extra args are ignored: + p_format3 = PyUnicode_FromFormat(b'%p', '123456', None, b'xyz') + self.assertIsInstance(p_format3, str) + self.assertRegex(p_format3, p_format_regex) + # Test string decode from parameter of %s using utf-8. # b'\xe4\xba\xba\xe6\xb0\x91' is utf-8 encoded byte sequence of # '\u4eba\u6c11' From 603deafd7fafb538b2f8af1c56fd8f8bb2ecc39b Mon Sep 17 00:00:00 2001 From: andrei kulakov Date: Fri, 7 Oct 2022 13:08:08 -0400 Subject: [PATCH 091/151] Add note on capture_output arg to subprocess.run() docstring (#98012) add note on capture_output arg to the docstring --- Lib/subprocess.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 760b93b47ebba6..9cadd1bf8e622c 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -509,7 +509,8 @@ def run(*popenargs, The returned instance will have attributes args, returncode, stdout and stderr. By default, stdout and stderr are not captured, and those attributes - will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them. + will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them, + or pass capture_output=True to capture both. If check is True and the exit code was non-zero, it raises a CalledProcessError. The CalledProcessError object will have the return code From bdf03738e8ef9f7acfa683adb85183e77d48f001 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 7 Oct 2022 20:17:08 +0300 Subject: [PATCH 092/151] Add more syslog tests (GH-97953) --- Lib/test/audit-tests.py | 20 +++++++++++++++ Lib/test/test_audit.py | 25 +++++++++++++++++- Lib/test/test_syslog.py | 56 ++++++++++++++++++++++++++++++++++++----- Modules/syslogmodule.c | 2 +- 4 files changed, 95 insertions(+), 8 deletions(-) diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py index 66c08f7f2ff8d5..4abf33d7cad1b6 100644 --- a/Lib/test/audit-tests.py +++ b/Lib/test/audit-tests.py @@ -429,6 +429,26 @@ def hook(event, args): sys.addaudithook(hook) _wmi.exec_query("SELECT * FROM Win32_OperatingSystem") +def test_syslog(): + import syslog + + def hook(event, args): + if event.startswith("syslog."): + print(event, *args) + + sys.addaudithook(hook) + syslog.openlog('python') + syslog.syslog('test') + syslog.setlogmask(syslog.LOG_DEBUG) + syslog.closelog() + # implicit open + syslog.syslog('test2') + # open with default ident + syslog.openlog(logoption=syslog.LOG_NDELAY, facility=syslog.LOG_LOCAL0) + sys.argv = None + syslog.openlog() + syslog.closelog() + if __name__ == "__main__": from test.support import suppress_msvcrt_asserts diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 09b3333afe184f..50f78f2a6b8a46 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -16,6 +16,7 @@ class AuditTest(unittest.TestCase): + maxDiff = None @support.requires_subprocess() def do_test(self, *args): @@ -185,7 +186,6 @@ def test_sys_getframe(self): self.assertEqual(actual, expected) - def test_wmi_exec_query(self): import_helper.import_module("_wmi") returncode, events, stderr = self.run_python("test_wmi_exec_query") @@ -199,6 +199,29 @@ def test_wmi_exec_query(self): self.assertEqual(actual, expected) + def test_syslog(self): + syslog = import_helper.import_module("syslog") + + returncode, events, stderr = self.run_python("test_syslog") + if returncode: + self.fail(stderr) + + if support.verbose: + print('Events:', *events, sep='\n ') + + self.assertSequenceEqual( + events, + [('syslog.openlog', ' ', f'python 0 {syslog.LOG_USER}'), + ('syslog.syslog', ' ', f'{syslog.LOG_INFO} test'), + ('syslog.setlogmask', ' ', f'{syslog.LOG_DEBUG}'), + ('syslog.closelog', '', ''), + ('syslog.syslog', ' ', f'{syslog.LOG_INFO} test2'), + ('syslog.openlog', ' ', f'audit-tests.py 0 {syslog.LOG_USER}'), + ('syslog.openlog', ' ', f'audit-tests.py {syslog.LOG_NDELAY} {syslog.LOG_LOCAL0}'), + ('syslog.openlog', ' ', f'None 0 {syslog.LOG_USER}'), + ('syslog.closelog', '', '')] + ) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_syslog.py b/Lib/test/test_syslog.py index fe09bd39f8b7fd..2125ec58d87e03 100644 --- a/Lib/test/test_syslog.py +++ b/Lib/test/test_syslog.py @@ -1,5 +1,9 @@ -from test.support import import_helper +from test.support import import_helper, threading_helper syslog = import_helper.import_module("syslog") #skip if not supported +from test import support +import sys +import threading +import time import unittest # XXX(nnorwitz): This test sucks. I don't know of a platform independent way @@ -8,6 +12,9 @@ class Test(unittest.TestCase): + def tearDown(self): + syslog.closelog() + def test_openlog(self): syslog.openlog('python') # Issue #6697. @@ -18,22 +25,59 @@ def test_syslog(self): syslog.syslog('test message from python test_syslog') syslog.syslog(syslog.LOG_ERR, 'test error from python test_syslog') + def test_syslog_implicit_open(self): + syslog.closelog() # Make sure log is closed + syslog.syslog('test message from python test_syslog') + syslog.syslog(syslog.LOG_ERR, 'test error from python test_syslog') + def test_closelog(self): syslog.openlog('python') syslog.closelog() + syslog.closelog() # idempotent operation def test_setlogmask(self): - syslog.setlogmask(syslog.LOG_DEBUG) + mask = syslog.LOG_UPTO(syslog.LOG_WARNING) + oldmask = syslog.setlogmask(mask) + self.assertEqual(syslog.setlogmask(0), mask) + self.assertEqual(syslog.setlogmask(oldmask), mask) def test_log_mask(self): - syslog.LOG_MASK(syslog.LOG_INFO) - - def test_log_upto(self): - syslog.LOG_UPTO(syslog.LOG_INFO) + mask = syslog.LOG_UPTO(syslog.LOG_WARNING) + self.assertTrue(mask & syslog.LOG_MASK(syslog.LOG_WARNING)) + self.assertTrue(mask & syslog.LOG_MASK(syslog.LOG_ERR)) + self.assertFalse(mask & syslog.LOG_MASK(syslog.LOG_INFO)) def test_openlog_noargs(self): syslog.openlog() syslog.syslog('test message from python test_syslog') + @threading_helper.requires_working_threading() + def test_syslog_threaded(self): + start = threading.Event() + stop = False + def opener(): + start.wait(10) + i = 1 + while not stop: + syslog.openlog(f'python-test-{i}') # new string object + i += 1 + def logger(): + start.wait(10) + while not stop: + syslog.syslog('test message from python test_syslog') + + orig_si = sys.getswitchinterval() + support.setswitchinterval(1e-9) + try: + threads = [threading.Thread(target=opener)] + threads += [threading.Thread(target=logger) for k in range(10)] + with threading_helper.start_threads(threads): + start.set() + time.sleep(0.1) + stop = True + finally: + sys.setswitchinterval(orig_si) + + if __name__ == "__main__": unittest.main() diff --git a/Modules/syslogmodule.c b/Modules/syslogmodule.c index c409fe968f8894..1593eea94a62bf 100644 --- a/Modules/syslogmodule.c +++ b/Modules/syslogmodule.c @@ -235,7 +235,7 @@ syslog_setlogmask(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "l;mask for priority", &maskpri)) return NULL; - if (PySys_Audit("syslog.setlogmask", "(O)", args ? args : Py_None) < 0) { + if (PySys_Audit("syslog.setlogmask", "l", maskpri) < 0) { return NULL; } omaskpri = setlogmask(maskpri); From 72bd5b94f382323c00f566d42c3cd4803376640e Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 20:27:14 +0300 Subject: [PATCH 093/151] gh-96415: Remove `types._cell_factory` from a module namespace (#96416) Closes #96415 --- Lib/types.py | 2 +- .../next/Library/2022-08-30-12-32-00.gh-issue-96415.6W7ORH.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2022-08-30-12-32-00.gh-issue-96415.6W7ORH.rst diff --git a/Lib/types.py b/Lib/types.py index 2e73fbc4501337..f8353126cb527c 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -60,7 +60,7 @@ def _m(self): pass GetSetDescriptorType = type(FunctionType.__code__) MemberDescriptorType = type(FunctionType.__globals__) -del sys, _f, _g, _C, _c, _ag # Not for export +del sys, _f, _g, _C, _c, _ag, _cell_factory # Not for export # Provide a PEP 3115 compliant mechanism for class creation diff --git a/Misc/NEWS.d/next/Library/2022-08-30-12-32-00.gh-issue-96415.6W7ORH.rst b/Misc/NEWS.d/next/Library/2022-08-30-12-32-00.gh-issue-96415.6W7ORH.rst new file mode 100644 index 00000000000000..0c93abf6dfcf16 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-30-12-32-00.gh-issue-96415.6W7ORH.rst @@ -0,0 +1 @@ +Remove ``types._cell_factory`` from module namespace. From 5df9a714c5f33e672064782f559629afc9adce11 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 20:36:40 +0300 Subject: [PATCH 094/151] gh-64373: Convert `_functools` to Argument Clinic (#96640) --- Include/internal/pycore_global_strings.h | 1 + .../internal/pycore_runtime_init_generated.h | 7 ++ Lib/test/test_functools.py | 12 ++ ...2-09-08-20-58-10.gh-issue-64373.AfCi36.rst | 1 + Modules/_functoolsmodule.c | 86 ++++++++++----- Modules/clinic/_functoolsmodule.c.h | 104 ++++++++++++++++++ 6 files changed, 182 insertions(+), 29 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-09-08-20-58-10.gh-issue-64373.AfCi36.rst create mode 100644 Modules/clinic/_functoolsmodule.c.h diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 1523eef73931c9..94f56fadc46301 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -468,6 +468,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(modules) STRUCT_FOR_ID(mro) STRUCT_FOR_ID(msg) + STRUCT_FOR_ID(mycmp) STRUCT_FOR_ID(n) STRUCT_FOR_ID(n_arg) STRUCT_FOR_ID(n_fields) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 32ff57b731e1a0..ea01fc01bbef3f 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -977,6 +977,7 @@ extern "C" { INIT_ID(modules), \ INIT_ID(mro), \ INIT_ID(msg), \ + INIT_ID(mycmp), \ INIT_ID(n), \ INIT_ID(n_arg), \ INIT_ID(n_fields), \ @@ -2260,6 +2261,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(msg); PyUnicode_InternInPlace(&string); + string = &_Py_ID(mycmp); + PyUnicode_InternInPlace(&string); string = &_Py_ID(n); PyUnicode_InternInPlace(&string); string = &_Py_ID(n_arg); @@ -6447,6 +6450,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(msg)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(mycmp)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(mycmp)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(n)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(n)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index a4b098a2a5b8ed..730ab1f595f22c 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -17,6 +17,7 @@ import gc from weakref import proxy import contextlib +from inspect import Signature from test.support import import_helper from test.support import threading_helper @@ -941,6 +942,10 @@ def mycmp(x, y): self.assertRaises(TypeError, hash, k) self.assertNotIsInstance(k, collections.abc.Hashable) + def test_cmp_to_signature(self): + self.assertEqual(str(Signature.from_callable(self.cmp_to_key)), + '(mycmp)') + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestCmpToKeyC(TestCmpToKey, unittest.TestCase): @@ -1853,6 +1858,13 @@ def test_staticmethod(x): for ref in refs: self.assertIsNone(ref()) + def test_common_signatures(self): + def orig(): ... + lru = self.module.lru_cache(1)(orig) + + self.assertEqual(str(Signature.from_callable(lru.cache_info)), '()') + self.assertEqual(str(Signature.from_callable(lru.cache_clear)), '()') + @py_functools.lru_cache() def py_cached_func(x, y): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-08-20-58-10.gh-issue-64373.AfCi36.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-08-20-58-10.gh-issue-64373.AfCi36.rst new file mode 100644 index 00000000000000..e7e13cb3a00c33 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-08-20-58-10.gh-issue-64373.AfCi36.rst @@ -0,0 +1 @@ +Convert :mod:`_functools` to argument clinic. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 3abb7fd710aeb4..e4036e166921d9 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -7,6 +7,13 @@ #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "structmember.h" // PyMemberDef +#include "clinic/_functoolsmodule.c.h" +/*[clinic input] +module _functools +class _functools._lru_cache_wrapper "PyObject *" "&lru_cache_type_spec" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bece4053896b09c0]*/ + /* _functools module written and maintained by Hye-Shik Chang with adaptations by Raymond Hettinger @@ -58,6 +65,7 @@ get_functools_state_by_type(PyTypeObject *type) return get_functools_state(module); } +// Not converted to argument clinic, because of `*args, **kwargs` arguments. static PyObject * partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) { @@ -282,6 +290,7 @@ partial_setvectorcall(partialobject *pto) } +// Not converted to argument clinic, because of `*args, **kwargs` arguments. static PyObject * partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) { @@ -625,33 +634,37 @@ keyobject_richcompare(PyObject *ko, PyObject *other, int op) return answer; } +/*[clinic input] +_functools.cmp_to_key + + mycmp: object + Function that compares two objects. + +Convert a cmp= function into a key= function. +[clinic start generated code]*/ + static PyObject * -functools_cmp_to_key(PyObject *self, PyObject *args, PyObject *kwds) +_functools_cmp_to_key_impl(PyObject *module, PyObject *mycmp) +/*[clinic end generated code: output=71eaad0f4fc81f33 input=d1b76f231c0dfeb3]*/ { - PyObject *cmp; - static char *kwargs[] = {"mycmp", NULL}; keyobject *object; _functools_state *state; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:cmp_to_key", kwargs, &cmp)) - return NULL; - - state = get_functools_state(self); + state = get_functools_state(module); object = PyObject_GC_New(keyobject, state->keyobject_type); if (!object) return NULL; - Py_INCREF(cmp); - object->cmp = cmp; + Py_INCREF(mycmp); + object->cmp = mycmp; object->object = NULL; PyObject_GC_Track(object); return (PyObject *)object; } -PyDoc_STRVAR(functools_cmp_to_key_doc, -"Convert a cmp= function into a key= function."); - /* reduce (used to be a builtin) ********************************************/ +// Not converted to argument clinic, because of `args` in-place modification. +// AC will affect performance. static PyObject * functools_reduce(PyObject *self, PyObject *args) { @@ -1299,25 +1312,41 @@ lru_cache_descr_get(PyObject *self, PyObject *obj, PyObject *type) return PyMethod_New(self, obj); } +/*[clinic input] +_functools._lru_cache_wrapper.cache_info + +Report cache statistics +[clinic start generated code]*/ + static PyObject * -lru_cache_cache_info(lru_cache_object *self, PyObject *unused) +_functools__lru_cache_wrapper_cache_info_impl(PyObject *self) +/*[clinic end generated code: output=cc796a0b06dbd717 input=f05e5b6ebfe38645]*/ { - if (self->maxsize == -1) { - return PyObject_CallFunction(self->cache_info_type, "nnOn", - self->hits, self->misses, Py_None, - PyDict_GET_SIZE(self->cache)); - } - return PyObject_CallFunction(self->cache_info_type, "nnnn", - self->hits, self->misses, self->maxsize, - PyDict_GET_SIZE(self->cache)); + lru_cache_object *_self = (lru_cache_object *) self; + if (_self->maxsize == -1) { + return PyObject_CallFunction(_self->cache_info_type, "nnOn", + _self->hits, _self->misses, Py_None, + PyDict_GET_SIZE(_self->cache)); + } + return PyObject_CallFunction(_self->cache_info_type, "nnnn", + _self->hits, _self->misses, _self->maxsize, + PyDict_GET_SIZE(_self->cache)); } +/*[clinic input] +_functools._lru_cache_wrapper.cache_clear + +Clear the cache and cache statistics +[clinic start generated code]*/ + static PyObject * -lru_cache_cache_clear(lru_cache_object *self, PyObject *unused) +_functools__lru_cache_wrapper_cache_clear_impl(PyObject *self) +/*[clinic end generated code: output=58423b35efc3e381 input=6ca59dba09b12584]*/ { - lru_list_elem *list = lru_cache_unlink_list(self); - self->hits = self->misses = 0; - PyDict_Clear(self->cache); + lru_cache_object *_self = (lru_cache_object *) self; + lru_list_elem *list = lru_cache_unlink_list(_self); + _self->hits = _self->misses = 0; + PyDict_Clear(_self->cache); lru_cache_clear_list(list); Py_RETURN_NONE; } @@ -1381,8 +1410,8 @@ cache_info_type: namedtuple class with the fields:\n\ ); static PyMethodDef lru_cache_methods[] = { - {"cache_info", (PyCFunction)lru_cache_cache_info, METH_NOARGS}, - {"cache_clear", (PyCFunction)lru_cache_cache_clear, METH_NOARGS}, + _FUNCTOOLS__LRU_CACHE_WRAPPER_CACHE_INFO_METHODDEF + _FUNCTOOLS__LRU_CACHE_WRAPPER_CACHE_CLEAR_METHODDEF {"__reduce__", (PyCFunction)lru_cache_reduce, METH_NOARGS}, {"__copy__", (PyCFunction)lru_cache_copy, METH_VARARGS}, {"__deepcopy__", (PyCFunction)lru_cache_deepcopy, METH_VARARGS}, @@ -1432,8 +1461,7 @@ PyDoc_STRVAR(_functools_doc, static PyMethodDef _functools_methods[] = { {"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc}, - {"cmp_to_key", _PyCFunction_CAST(functools_cmp_to_key), - METH_VARARGS | METH_KEYWORDS, functools_cmp_to_key_doc}, + _FUNCTOOLS_CMP_TO_KEY_METHODDEF {NULL, NULL} /* sentinel */ }; diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h new file mode 100644 index 00000000000000..9c79e6430413bf --- /dev/null +++ b/Modules/clinic/_functoolsmodule.c.h @@ -0,0 +1,104 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif + + +PyDoc_STRVAR(_functools_cmp_to_key__doc__, +"cmp_to_key($module, /, mycmp)\n" +"--\n" +"\n" +"Convert a cmp= function into a key= function.\n" +"\n" +" mycmp\n" +" Function that compares two objects."); + +#define _FUNCTOOLS_CMP_TO_KEY_METHODDEF \ + {"cmp_to_key", _PyCFunction_CAST(_functools_cmp_to_key), METH_FASTCALL|METH_KEYWORDS, _functools_cmp_to_key__doc__}, + +static PyObject * +_functools_cmp_to_key_impl(PyObject *module, PyObject *mycmp); + +static PyObject * +_functools_cmp_to_key(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(mycmp), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"mycmp", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "cmp_to_key", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *mycmp; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + mycmp = args[0]; + return_value = _functools_cmp_to_key_impl(module, mycmp); + +exit: + return return_value; +} + +PyDoc_STRVAR(_functools__lru_cache_wrapper_cache_info__doc__, +"cache_info($self, /)\n" +"--\n" +"\n" +"Report cache statistics"); + +#define _FUNCTOOLS__LRU_CACHE_WRAPPER_CACHE_INFO_METHODDEF \ + {"cache_info", (PyCFunction)_functools__lru_cache_wrapper_cache_info, METH_NOARGS, _functools__lru_cache_wrapper_cache_info__doc__}, + +static PyObject * +_functools__lru_cache_wrapper_cache_info_impl(PyObject *self); + +static PyObject * +_functools__lru_cache_wrapper_cache_info(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _functools__lru_cache_wrapper_cache_info_impl(self); +} + +PyDoc_STRVAR(_functools__lru_cache_wrapper_cache_clear__doc__, +"cache_clear($self, /)\n" +"--\n" +"\n" +"Clear the cache and cache statistics"); + +#define _FUNCTOOLS__LRU_CACHE_WRAPPER_CACHE_CLEAR_METHODDEF \ + {"cache_clear", (PyCFunction)_functools__lru_cache_wrapper_cache_clear, METH_NOARGS, _functools__lru_cache_wrapper_cache_clear__doc__}, + +static PyObject * +_functools__lru_cache_wrapper_cache_clear_impl(PyObject *self); + +static PyObject * +_functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _functools__lru_cache_wrapper_cache_clear_impl(self); +} +/*[clinic end generated code: output=7e7f3bcf9ed61f23 input=a9049054013a1b77]*/ From ca73c857f10557713479c666e58358d06e6e866c Mon Sep 17 00:00:00 2001 From: Marc Monfort Date: Fri, 7 Oct 2022 19:42:35 +0200 Subject: [PATCH 095/151] Fix a mistake in isSet() deprecated message doc (#95720) From 4ca8bb0af1b5cdac225bae53461697efb71f5321 Mon Sep 17 00:00:00 2001 From: zikcheng Date: Sat, 8 Oct 2022 01:53:07 +0800 Subject: [PATCH 096/151] Make _symtable_entry.ste_type's comment consistent wit _Py_block_ty (#92414) _Py_block_ty defines four types of block, FunctionBlock, ClassBlock, ModuleBlock and AnnotationBlock. But _symtable_entry.ste_type only comments three of them, I think it's better both sides are consistent. --- Include/internal/pycore_symtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h index 2d64aba22ff905..8532646ce7d95c 100644 --- a/Include/internal/pycore_symtable.h +++ b/Include/internal/pycore_symtable.h @@ -49,7 +49,7 @@ typedef struct _symtable_entry { PyObject *ste_varnames; /* list of function parameters */ PyObject *ste_children; /* list of child blocks */ PyObject *ste_directives;/* locations of global and nonlocal statements */ - _Py_block_ty ste_type; /* module, class or function */ + _Py_block_ty ste_type; /* module, class, function or annotation */ int ste_nested; /* true if block is nested */ unsigned ste_free : 1; /* true if block has free variables */ unsigned ste_child_free : 1; /* true if a child block has free vars, From 97ae2e91dc634ec33462fa42b1eba07e8843cbdd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Oct 2022 19:57:48 +0200 Subject: [PATCH 097/151] gh-97669: Move difflib examples to Doc/includes/ (#97964) Remove diff.py and ndiff.py scripts of Tools/scripts/: move them to Doc/includes/. * diff.py and ndiff.py files are no longer executable. Remove also their shebang ("#!/usr/bin/env python3"). * Remove the -profile command from ndiff.py to simply the code. * Remove ndiff.py copyright and history command. The Python documentation examples are distributed under the "Zero Clause BSD License". --- {Tools/scripts => Doc/includes}/diff.py | 1 - {Tools/scripts => Doc/includes}/ndiff.py | 24 +----------------------- Doc/library/difflib.rst | 15 ++++++++------- 3 files changed, 9 insertions(+), 31 deletions(-) rename {Tools/scripts => Doc/includes}/diff.py (98%) mode change 100755 => 100644 rename {Tools/scripts => Doc/includes}/ndiff.py (80%) mode change 100755 => 100644 diff --git a/Tools/scripts/diff.py b/Doc/includes/diff.py old mode 100755 new mode 100644 similarity index 98% rename from Tools/scripts/diff.py rename to Doc/includes/diff.py index 96199b85116d7d..001619f5f83fc0 --- a/Tools/scripts/diff.py +++ b/Doc/includes/diff.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """ Command line interface to difflib.py providing diffs in four formats: * ndiff: lists every line and highlights interline changes. diff --git a/Tools/scripts/ndiff.py b/Doc/includes/ndiff.py old mode 100755 new mode 100644 similarity index 80% rename from Tools/scripts/ndiff.py rename to Doc/includes/ndiff.py index c6d09b8f242fef..32c251bce9120b --- a/Tools/scripts/ndiff.py +++ b/Doc/includes/ndiff.py @@ -1,16 +1,3 @@ -#! /usr/bin/env python3 - -# Module ndiff version 1.7.0 -# Released to the public domain 08-Dec-2000, -# by Tim Peters (tim.one@home.com). - -# Provided as-is; use at your own risk; no warranty; no promises; enjoy! - -# ndiff.py is now simply a front-end to the difflib.ndiff() function. -# Originally, it contained the difflib.SequenceMatcher class as well. -# This completes the raiding of reusable code from this formerly -# self-contained script. - """ndiff [-q] file1 file2 or ndiff (-r1 | -r2) < ndiff_output > file1_or_file2 @@ -121,13 +108,4 @@ def restore(which): sys.stdout.writelines(restored) if __name__ == '__main__': - args = sys.argv[1:] - if "-profile" in args: - import profile, pstats - args.remove("-profile") - statf = "ndiff.pro" - profile.run("main(args)", statf) - stats = pstats.Stats(statf) - stats.strip_dirs().sort_stats('time').print_stats() - else: - main(args) + main(sys.argv[1:]) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index c5a279688a4491..5ee1f4a02c6816 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -145,8 +145,6 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. The arguments for this method are the same as those for the :meth:`make_file` method. - :file:`Tools/scripts/diff.py` is a command-line front-end to this class and - contains a good example of its use. .. function:: context_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n') @@ -240,8 +238,6 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. function :func:`IS_CHARACTER_JUNK`, which filters out whitespace characters (a blank or tab; it's a bad idea to include newline in this!). - :file:`Tools/scripts/ndiff.py` is a command-line front-end to this function. - >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> print(''.join(diff), end="") @@ -759,7 +755,12 @@ A command-line interface to difflib ----------------------------------- This example shows how to use difflib to create a ``diff``-like utility. -It is also contained in the Python source distribution, as -:file:`Tools/scripts/diff.py`. -.. literalinclude:: ../../Tools/scripts/diff.py +.. literalinclude:: ../includes/diff.py + +ndiff example +------------- + +This example shows how to use :func:`difflib.ndiff`. + +.. literalinclude:: ../includes/ndiff.py From 1096dfa97dd30ceebd0364a5442ead117f1e06bf Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 21:06:23 +0300 Subject: [PATCH 098/151] gh-97955: Migrate `zoneinfo` to Argument Clinic (#97958) --- Include/internal/pycore_global_strings.h | 1 + .../internal/pycore_runtime_init_generated.h | 7 + Lib/test/test_zoneinfo/test_zoneinfo.py | 13 ++ ...2-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst | 1 + Modules/_zoneinfo.c | 84 ++++---- Modules/clinic/_zoneinfo.c.h | 188 ++++++++++++++++++ 6 files changed, 258 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst create mode 100644 Modules/clinic/_zoneinfo.c.h diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 94f56fadc46301..2966b60e0cd830 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -494,6 +494,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(offset_src) STRUCT_FOR_ID(on_type_read) STRUCT_FOR_ID(onceregistry) + STRUCT_FOR_ID(only_keys) STRUCT_FOR_ID(oparg) STRUCT_FOR_ID(opcode) STRUCT_FOR_ID(open) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index ea01fc01bbef3f..617582f96e33f5 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1003,6 +1003,7 @@ extern "C" { INIT_ID(offset_src), \ INIT_ID(on_type_read), \ INIT_ID(onceregistry), \ + INIT_ID(only_keys), \ INIT_ID(oparg), \ INIT_ID(opcode), \ INIT_ID(open), \ @@ -2313,6 +2314,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(onceregistry); PyUnicode_InternInPlace(&string); + string = &_Py_ID(only_keys); + PyUnicode_InternInPlace(&string); string = &_Py_ID(oparg); PyUnicode_InternInPlace(&string); string = &_Py_ID(opcode); @@ -6554,6 +6557,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(onceregistry)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(only_keys)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(only_keys)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(oparg)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(oparg)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index a2172f3ac21d08..fd0e3bc032ec0c 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -404,6 +404,19 @@ def test_time_fixed_offset(self): class CZoneInfoTest(ZoneInfoTest): module = c_zoneinfo + def test_signatures(self): + """Ensure that C module has valid method signatures.""" + import inspect + + must_have_signatures = ( + self.klass.clear_cache, + self.klass.no_cache, + self.klass.from_file, + ) + for method in must_have_signatures: + with self.subTest(method=method): + inspect.Signature.from_callable(method) + def test_fold_mutate(self): """Test that fold isn't mutated when no change is necessary. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst new file mode 100644 index 00000000000000..e21794df4f1840 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst @@ -0,0 +1 @@ +Migrate :mod:`zoneinfo` to Argument Clinic. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 207340adec152f..36b25bf3c05469 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -12,6 +12,13 @@ #include "datetime.h" +#include "clinic/_zoneinfo.c.h" +/*[clinic input] +module zoneinfo +class zoneinfo.ZoneInfo "PyObject *" "PyTypeObject *" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d12c73c0eef36df8]*/ + // Imports static PyObject *io_open = NULL; static PyObject *_tzpath_find_tzfile = NULL; @@ -338,20 +345,25 @@ zoneinfo_dealloc(PyObject *obj_self) Py_TYPE(self)->tp_free((PyObject *)self); } +/*[clinic input] +@classmethod +zoneinfo.ZoneInfo.from_file + + file_obj: object + / + key: object = None + +Create a ZoneInfo file from a file object. +[clinic start generated code]*/ + static PyObject * -zoneinfo_from_file(PyTypeObject *type, PyObject *args, PyObject *kwargs) +zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, + PyObject *key) +/*[clinic end generated code: output=68ed2022404ae5be input=ccfe73708133d2e4]*/ { - PyObject *file_obj = NULL; PyObject *file_repr = NULL; - PyObject *key = Py_None; PyZoneInfo_ZoneInfo *self = NULL; - static char *kwlist[] = {"", "key", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, &file_obj, - &key)) { - return NULL; - } - PyObject *obj_self = (PyObject *)(type->tp_alloc(type, 0)); self = (PyZoneInfo_ZoneInfo *)obj_self; if (self == NULL) { @@ -379,16 +391,20 @@ zoneinfo_from_file(PyTypeObject *type, PyObject *args, PyObject *kwargs) return NULL; } +/*[clinic input] +@classmethod +zoneinfo.ZoneInfo.no_cache + + key: object + +Get a new instance of ZoneInfo, bypassing the cache. +[clinic start generated code]*/ + static PyObject * -zoneinfo_no_cache(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key) +/*[clinic end generated code: output=751c6894ad66f91b input=bb24afd84a80ba46]*/ { - static char *kwlist[] = {"key", NULL}; - PyObject *key = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &key)) { - return NULL; - } - - PyObject *out = zoneinfo_new_instance(cls, key); + PyObject *out = zoneinfo_new_instance(type, key); if (out != NULL) { ((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE; } @@ -396,18 +412,20 @@ zoneinfo_no_cache(PyTypeObject *cls, PyObject *args, PyObject *kwargs) return out; } -static PyObject * -zoneinfo_clear_cache(PyObject *cls, PyObject *args, PyObject *kwargs) -{ - PyObject *only_keys = NULL; - static char *kwlist[] = {"only_keys", NULL}; +/*[clinic input] +@classmethod +zoneinfo.ZoneInfo.clear_cache - if (!(PyArg_ParseTupleAndKeywords(args, kwargs, "|$O", kwlist, - &only_keys))) { - return NULL; - } + * + only_keys: object = None - PyTypeObject *type = (PyTypeObject *)cls; +Clear the ZoneInfo cache. +[clinic start generated code]*/ + +static PyObject * +zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys) +/*[clinic end generated code: output=eec0a3276f07bd90 input=8cff0182a95f295b]*/ +{ PyObject *weak_cache = get_weak_cache(type); if (only_keys == NULL || only_keys == Py_None) { @@ -2545,15 +2563,9 @@ zoneinfo_init_subclass(PyTypeObject *cls, PyObject *args, PyObject **kwargs) ///// // Specify the ZoneInfo type static PyMethodDef zoneinfo_methods[] = { - {"clear_cache", (PyCFunction)(void (*)(void))zoneinfo_clear_cache, - METH_VARARGS | METH_KEYWORDS | METH_CLASS, - PyDoc_STR("Clear the ZoneInfo cache.")}, - {"no_cache", (PyCFunction)(void (*)(void))zoneinfo_no_cache, - METH_VARARGS | METH_KEYWORDS | METH_CLASS, - PyDoc_STR("Get a new instance of ZoneInfo, bypassing the cache.")}, - {"from_file", (PyCFunction)(void (*)(void))zoneinfo_from_file, - METH_VARARGS | METH_KEYWORDS | METH_CLASS, - PyDoc_STR("Create a ZoneInfo file from a file object.")}, + ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF + ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF + ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF {"utcoffset", (PyCFunction)zoneinfo_utcoffset, METH_O, PyDoc_STR("Retrieve a timedelta representing the UTC offset in a zone at " "the given datetime.")}, diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h new file mode 100644 index 00000000000000..78fcbfa9411bb8 --- /dev/null +++ b/Modules/clinic/_zoneinfo.c.h @@ -0,0 +1,188 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif + + +PyDoc_STRVAR(zoneinfo_ZoneInfo_from_file__doc__, +"from_file($type, file_obj, /, key=None)\n" +"--\n" +"\n" +"Create a ZoneInfo file from a file object."); + +#define ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF \ + {"from_file", _PyCFunction_CAST(zoneinfo_ZoneInfo_from_file), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_from_file__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj, + PyObject *key); + +static PyObject * +zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(key), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "key", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "from_file", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *file_obj; + PyObject *key = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); + if (!args) { + goto exit; + } + file_obj = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + key = args[1]; +skip_optional_pos: + return_value = zoneinfo_ZoneInfo_from_file_impl(type, file_obj, key); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo_no_cache__doc__, +"no_cache($type, /, key)\n" +"--\n" +"\n" +"Get a new instance of ZoneInfo, bypassing the cache."); + +#define ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF \ + {"no_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_no_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_no_cache__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key); + +static PyObject * +zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(key), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"key", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "no_cache", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *key; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + key = args[0]; + return_value = zoneinfo_ZoneInfo_no_cache_impl(type, key); + +exit: + return return_value; +} + +PyDoc_STRVAR(zoneinfo_ZoneInfo_clear_cache__doc__, +"clear_cache($type, /, *, only_keys=None)\n" +"--\n" +"\n" +"Clear the ZoneInfo cache."); + +#define ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF \ + {"clear_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_clear_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_clear_cache__doc__}, + +static PyObject * +zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys); + +static PyObject * +zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(only_keys), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"only_keys", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "clear_cache", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *only_keys = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + only_keys = args[0]; +skip_optional_kwonly: + return_value = zoneinfo_ZoneInfo_clear_cache_impl(type, only_keys); + +exit: + return return_value; +} +/*[clinic end generated code: output=d2da73ef66146b83 input=a9049054013a1b77]*/ From e7d9ea0bc9c9ad7c537a52d11ca3ecf8f1ca3c3a Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:12:08 -0700 Subject: [PATCH 099/151] gh-64921: Clarify wording for open()'s newline arg (#96171) --- Doc/library/functions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 26ee302d2eabc3..93c9f4ad2bc23a 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1254,8 +1254,8 @@ are always available. They are listed here in alphabetical order. .. _open-newline-parameter: - *newline* controls how :term:`universal newlines` mode works (it only - applies to text mode). It can be ``None``, ``''``, ``'\n'``, ``'\r'``, and + *newline* determines how to parse newline characters from the stream. + It can be ``None``, ``''``, ``'\n'``, ``'\r'``, and ``'\r\n'``. It works as follows: * When reading input from the stream, if *newline* is ``None``, universal From bd660315e183926af5ecabd2bdb05dab1341e1c0 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:15:34 -0700 Subject: [PATCH 100/151] gh-65496: Correct wording on csv's skipinitialspace argument (#96170) --- Doc/library/csv.rst | 2 +- Lib/test/test_csv.py | 5 +++++ Modules/_csv.c | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 0cab95e1500a66..41f11505aa1030 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -420,7 +420,7 @@ Dialects support the following attributes: .. attribute:: Dialect.skipinitialspace - When :const:`True`, whitespace immediately following the *delimiter* is ignored. + When :const:`True`, spaces immediately following the *delimiter* are ignored. The default is :const:`False`. diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index a2b00430c7dd60..d64bff13a44e87 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -362,6 +362,11 @@ def test_read_quoting(self): self._read_test(['1,@,3,@,5'], [['1', ',3,', '5']], quotechar='@') self._read_test(['1,\0,3,\0,5'], [['1', ',3,', '5']], quotechar='\0') + def test_read_skipinitialspace(self): + self._read_test(['no space, space, spaces,\ttab'], + [['no space', 'space', 'spaces', '\ttab']], + skipinitialspace=True) + def test_read_bigfield(self): # This exercises the buffer realloc functionality and field size # limits. diff --git a/Modules/_csv.c b/Modules/_csv.c index d34d0a1296ae72..25bf86f78e8fb9 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -704,7 +704,7 @@ parse_process_char(ReaderObj *self, _csvstate *module_state, Py_UCS4 c) self->state = ESCAPED_CHAR; } else if (c == ' ' && dialect->skipinitialspace) - /* ignore space at start of field */ + /* ignore spaces at start of field */ ; else if (c == dialect->delimiter) { /* save empty field */ @@ -1647,9 +1647,9 @@ PyDoc_STRVAR(csv_module_doc, " quoting character. It defaults to '\"'.\n" " * delimiter - specifies a one-character string to use as the\n" " field separator. It defaults to ','.\n" -" * skipinitialspace - specifies how to interpret whitespace which\n" -" immediately follows a delimiter. It defaults to False, which\n" -" means that whitespace immediately following a delimiter is part\n" +" * skipinitialspace - specifies how to interpret spaces which\n" +" immediately follow a delimiter. It defaults to False, which\n" +" means that spaces immediately following a delimiter is part\n" " of the following field.\n" " * lineterminator - specifies the character sequence which should\n" " terminate rows.\n" From 41b817230b0d75e31d465ff698c291977fd3e119 Mon Sep 17 00:00:00 2001 From: Anh71me Date: Sat, 8 Oct 2022 02:23:06 +0800 Subject: [PATCH 101/151] GH-96073: Fix wild replacement in inspect.formatannotation (#96074) Co-authored-by: Jelle Zijlstra --- Lib/inspect.py | 5 ++++- Lib/test/test_inspect.py | 7 +++++++ Lib/test/typinganndata/__init__.py | 0 Lib/test/typinganndata/ann_module9.py | 14 ++++++++++++++ .../2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst | 1 + 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Lib/test/typinganndata/__init__.py create mode 100644 Lib/test/typinganndata/ann_module9.py create mode 100644 Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 8a107a894909eb..585875a30c35c3 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1433,7 +1433,10 @@ def getargvalues(frame): def formatannotation(annotation, base_module=None): if getattr(annotation, '__module__', None) == 'typing': - return repr(annotation).replace('typing.', '') + def repl(match): + text = match.group() + return text.removeprefix('typing.') + return re.sub(r'[\w\.]+', repl, repr(annotation)) if isinstance(annotation, types.GenericAlias): return str(annotation) if isinstance(annotation, type): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 710b609ce550d6..61fed323dceae4 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1421,6 +1421,13 @@ def wrapper(a, b): self.assertEqual(inspect.get_annotations(isa.MyClassWithLocalAnnotations, eval_str=True), {'x': int}) +class TestFormatAnnotation(unittest.TestCase): + def test_typing_replacement(self): + from test.typinganndata.ann_module9 import ann, ann1 + self.assertEqual(inspect.formatannotation(ann), 'Union[List[str], int]') + self.assertEqual(inspect.formatannotation(ann1), 'Union[List[testModule.typing.A], int]') + + class TestIsDataDescriptor(unittest.TestCase): def test_custom_descriptors(self): diff --git a/Lib/test/typinganndata/__init__.py b/Lib/test/typinganndata/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/typinganndata/ann_module9.py b/Lib/test/typinganndata/ann_module9.py new file mode 100644 index 00000000000000..952217393e1ff7 --- /dev/null +++ b/Lib/test/typinganndata/ann_module9.py @@ -0,0 +1,14 @@ +# Test ``inspect.formatannotation`` +# https://github.com/python/cpython/issues/96073 + +from typing import Union, List + +ann = Union[List[str], int] + +# mock typing._type_repr behaviour +class A: ... + +A.__module__ = 'testModule.typing' +A.__qualname__ = 'A' + +ann1 = Union[List[A], int] diff --git a/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst b/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst new file mode 100644 index 00000000000000..0e6dd8d360cbc9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst @@ -0,0 +1 @@ +In :mod:`inspect`, fix overeager replacement of "`typing.`" in formatting annotations. From 358e173dc2f7c7673886cad596cb41554a67d474 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 7 Oct 2022 11:49:53 -0700 Subject: [PATCH 102/151] Add a warning message about PyOS_snprintf (#95993) --- Doc/c-api/conversion.rst | 3 ++- Python/mysnprintf.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index 9b9c4ffa4d0343..fdb321fe7ab3f2 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -28,7 +28,8 @@ not. The wrappers ensure that ``str[size-1]`` is always ``'\0'`` upon return. They never write more than *size* bytes (including the trailing ``'\0'``) into str. Both functions require that ``str != NULL``, ``size > 0``, ``format != NULL`` -and ``size < INT_MAX``. +and ``size < INT_MAX``. Note that this means there is no equivalent to the C99 +``n = snprintf(NULL, 0, ...)`` which would determine the necessary buffer size. The return value (*rv*) for these functions should be interpreted as follows: diff --git a/Python/mysnprintf.c b/Python/mysnprintf.c index cd69198011e3c9..2a505d14f82c99 100644 --- a/Python/mysnprintf.c +++ b/Python/mysnprintf.c @@ -9,6 +9,7 @@ would have been written had the buffer not been too small, and to set the last byte of the buffer to \0. At least MS _vsnprintf returns a negative value instead, and fills the entire buffer with non-\0 data. + Unlike C99, our wrappers do not support passing a null buffer. The wrappers ensure that str[size-1] is always \0 upon return. From af02efee790aed57f7ef2f9eece33d081138da64 Mon Sep 17 00:00:00 2001 From: 180909 <734461790@qq.com> Date: Sat, 8 Oct 2022 02:52:45 +0800 Subject: [PATCH 103/151] gh-96959: Update HTTP links which are redirected to HTTPS (#98039) --- Doc/faq/library.rst | 2 +- Doc/includes/tzinfo_examples.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/faq/library.rst b/Doc/faq/library.rst index f79cf485712274..a9cde456575020 100644 --- a/Doc/faq/library.rst +++ b/Doc/faq/library.rst @@ -609,7 +609,7 @@ use ``p.read(n)``. substituted for standard input and output. You will have to use pseudo ttys ("ptys") instead of pipes. Or you can use a Python interface to Don Libes' "expect" library. A Python extension that interfaces to expect is called - "expy" and available from http://expectpy.sourceforge.net. A pure Python + "expy" and available from https://expectpy.sourceforge.net. A pure Python solution that works like expect is `pexpect `_. diff --git a/Doc/includes/tzinfo_examples.py b/Doc/includes/tzinfo_examples.py index 9b9e32a553e7d8..1fa6e615e46a76 100644 --- a/Doc/includes/tzinfo_examples.py +++ b/Doc/includes/tzinfo_examples.py @@ -71,7 +71,7 @@ def first_sunday_on_or_after(dt): # DST start and end times. For a complete and up-to-date set of DST rules # and timezone definitions, visit the Olson Database (or try pytz): # http://www.twinsun.com/tz/tz-link.htm -# http://sourceforge.net/projects/pytz/ (might not be up-to-date) +# https://sourceforge.net/projects/pytz/ (might not be up-to-date) # # In the US, since 2007, DST starts at 2am (standard time) on the second # Sunday in March, which is the first Sunday on or after Mar 8. From 8bc17aa293d21da8e575c08816b0626bfc9e11f3 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 21:54:45 +0300 Subject: [PATCH 104/151] gh-97956: Mention `generate_global_objects.py` in `AC How-To` (#97957) --- Doc/howto/clinic.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index d634c4b47db90e..b8afc7e6d762aa 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -539,7 +539,15 @@ Let's dive in! }; -16. Compile, then run the relevant portions of the regression-test suite. +16. Argument Clinic may generate new instances of ``_Py_ID``. For example:: + + &_Py_ID(new_unique_py_id) + + If it does, you'll have to run ``Tools/scripts/generate_global_objects.py`` + to regenerate the list of precompiled identifiers at this point. + + +17. Compile, then run the relevant portions of the regression-test suite. This change should not introduce any new compile-time warnings or errors, and there should be no externally visible change to Python's behavior. From dc2c10e6280be730df0c956b2e4ea3df512dcf27 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Oct 2022 21:58:46 +0300 Subject: [PATCH 105/151] gh-97923: Always run Ubuntu SSL tests with others in CI (#97940) --- .github/workflows/build.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1db5c505149397..dc91c0dbcb027b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,6 @@ jobs: runs-on: ubuntu-latest outputs: run_tests: ${{ steps.check.outputs.run_tests }} - run_ssl_tests: ${{ steps.check.outputs.run_ssl_tests }} steps: - uses: actions/checkout@v3 - name: Check for source changes @@ -39,7 +38,6 @@ jobs: run: | if [ -z "$GITHUB_BASE_REF" ]; then echo '::set-output name=run_tests::true' - echo '::set-output name=run_ssl_tests::true' else git fetch origin $GITHUB_BASE_REF --depth=1 # git diff "origin/$GITHUB_BASE_REF..." (3 dots) may be more @@ -56,7 +54,6 @@ jobs: # # https://github.com/python/core-workflow/issues/373 git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qvE '(\.rst$|^Doc|^Misc)' && echo '::set-output name=run_tests::true' || true - git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qE '(ssl|hashlib|hmac|^.github)' && echo '::set-output name=run_ssl_tests::true' || true fi check_generated_files: @@ -230,7 +227,7 @@ jobs: name: 'Ubuntu SSL tests with OpenSSL' runs-on: ubuntu-20.04 needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_ssl_tests == 'true' + if: needs.check_source.outputs.run_tests == 'true' strategy: fail-fast: false matrix: From 9f5d3b51e204ca36f35e624aa9c4b93be1a30757 Mon Sep 17 00:00:00 2001 From: Noam Cohen Date: Fri, 7 Oct 2022 22:00:53 +0300 Subject: [PATCH 106/151] gh-97646: Change `.js` and `.mjs` files mimetype to conform to RFC 9239 (#97934) --- Lib/mimetypes.py | 4 ++-- .../Library/2022-10-05-20-52-17.gh-issue-97646.Q4fVww.rst | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-05-20-52-17.gh-issue-97646.Q4fVww.rst diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index f6c43b3b92bc50..3224363a3f2bfb 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -427,8 +427,8 @@ def _default_mime_types(): # Make sure the entry with the preferred file extension for a particular mime type # appears before any others of the same mimetype. types_map = _types_map_default = { - '.js' : 'application/javascript', - '.mjs' : 'application/javascript', + '.js' : 'text/javascript', + '.mjs' : 'text/javascript', '.json' : 'application/json', '.webmanifest': 'application/manifest+json', '.doc' : 'application/msword', diff --git a/Misc/NEWS.d/next/Library/2022-10-05-20-52-17.gh-issue-97646.Q4fVww.rst b/Misc/NEWS.d/next/Library/2022-10-05-20-52-17.gh-issue-97646.Q4fVww.rst new file mode 100644 index 00000000000000..6eed16c8e8bbfb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-05-20-52-17.gh-issue-97646.Q4fVww.rst @@ -0,0 +1 @@ +Replace deprecated ``application/javascript`` with ``text/javascript`` in :mod:`mimetypes`. See :rfc:`9239`. Patch by Noam Cohen. From 0085f20949f48ccb147bd5ccc11928d684e8a55b Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:04:14 -0700 Subject: [PATCH 107/151] gh-73196: Add namespace/scope clarification for inheritance section (#92840) Add namespace/scope clarification for inheritance section --- Doc/tutorial/classes.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index f27abe48b2d4ed..9ecbf8b87efbf1 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -581,7 +581,8 @@ this:: . -The name :class:`BaseClassName` must be defined in a scope containing the +The name :class:`BaseClassName` must be defined in a +namespace accessible from the scope containing the derived class definition. In place of a base class name, other arbitrary expressions are also allowed. This can be useful, for example, when the base class is defined in another module:: From 53bc2db300ca420aeda28e65b1efd3a28caf4ef3 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:15:41 -0700 Subject: [PATCH 108/151] gh-96265: Fix some formatting in faq/design.rst (#96924) Co-authored-by: Ezio Melotti Co-authored-by: C.A.M. Gerlach --- Doc/faq/design.rst | 58 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index ccdc9bf02b1c7b..763222aba8b6ed 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -62,7 +62,7 @@ and think it is a bug in Python. It's not. This has little to do with Python, and much more to do with how the underlying platform handles floating-point numbers. -The :class:`float` type in CPython uses a C ``double`` for storage. A +The :class:`float` type in CPython uses a C :c:type:`double` for storage. A :class:`float` object's value is stored in binary floating-point with a fixed precision (typically 53 bits) and Python uses C operations, which in turn rely on the hardware implementation in the processor, to perform floating-point @@ -129,7 +129,7 @@ reference or call the method from a particular class. In C++, if you want to use a method from a base class which is overridden in a derived class, you have to use the ``::`` operator -- in Python you can write ``baseclass.methodname(self, )``. This is particularly useful -for :meth:`__init__` methods, and in general in cases where a derived class +for :meth:`~object.__init__` methods, and in general in cases where a derived class method wants to extend the base class method of the same name and thus has to call the base class method somehow. @@ -232,7 +232,8 @@ Similar methods exist for bytes and bytearray objects. How fast are exceptions? ------------------------ -A try/except block is extremely efficient if no exceptions are raised. Actually +A :keyword:`try`/:keyword:`except` block is extremely efficient if no exceptions +are raised. Actually catching an exception is expensive. In versions of Python prior to 2.0 it was common to use this idiom:: @@ -352,7 +353,7 @@ will probably run out of file descriptors:: c = f.read(1) Indeed, using CPython's reference counting and destructor scheme, each new -assignment to *f* closes the previous file. With a traditional GC, however, +assignment to ``f`` closes the previous file. With a traditional GC, however, those file objects will only get collected (and closed) at varying and possibly long intervals. @@ -376,10 +377,10 @@ Python to work with it.) Traditional GC also becomes a problem when Python is embedded into other applications. While in a standalone Python it's fine to replace the standard -malloc() and free() with versions provided by the GC library, an application -embedding Python may want to have its *own* substitute for malloc() and free(), +``malloc()`` and ``free()`` with versions provided by the GC library, an application +embedding Python may want to have its *own* substitute for ``malloc()`` and ``free()``, and may not want Python's. Right now, CPython works with anything that -implements malloc() and free() properly. +implements ``malloc()`` and ``free()`` properly. Why isn't all memory freed when CPython exits? @@ -401,14 +402,15 @@ Why are there separate tuple and list data types? Lists and tuples, while similar in many respects, are generally used in fundamentally different ways. Tuples can be thought of as being similar to -Pascal records or C structs; they're small collections of related data which may +Pascal ``records`` or C ``structs``; they're small collections of related data which may be of different types which are operated on as a group. For example, a Cartesian coordinate is appropriately represented as a tuple of two or three numbers. Lists, on the other hand, are more like arrays in other languages. They tend to hold a varying number of objects all of which have the same type and which are -operated on one-by-one. For example, ``os.listdir('.')`` returns a list of +operated on one-by-one. For example, :func:`os.listdir('.') ` +returns a list of strings representing the files in the current directory. Functions which operate on this output would generally not break if you added another file or two to the directory. @@ -444,9 +446,9 @@ far) under most circumstances, and the implementation is simpler. Dictionaries work by computing a hash code for each key stored in the dictionary using the :func:`hash` built-in function. The hash code varies widely depending -on the key and a per-process seed; for example, "Python" could hash to --539294296 while "python", a string that differs by a single bit, could hash -to 1142331976. The hash code is then used to calculate a location in an +on the key and a per-process seed; for example, ``'Python'`` could hash to +``-539294296`` while ``'python'``, a string that differs by a single bit, could hash +to ``1142331976``. The hash code is then used to calculate a location in an internal array where the value will be stored. Assuming that you're storing keys that all have different hash values, this means that dictionaries take constant time -- O(1), in Big-O notation -- to retrieve a key. @@ -497,7 +499,8 @@ Some unacceptable solutions that have been proposed: There is a trick to get around this if you need to, but use it at your own risk: You can wrap a mutable structure inside a class instance which has both a -:meth:`__eq__` and a :meth:`__hash__` method. You must then make sure that the +:meth:`~object.__eq__` and a :meth:`~object.__hash__` method. +You must then make sure that the hash value for all such wrapper objects that reside in a dictionary (or other hash based structure), remain fixed while the object is in the dictionary (or other structure). :: @@ -528,7 +531,7 @@ is True``) then ``hash(o1) == hash(o2)`` (ie, ``o1.__hash__() == o2.__hash__()`` regardless of whether the object is in a dictionary or not. If you fail to meet these restrictions dictionaries and other hash based structures will misbehave. -In the case of ListWrapper, whenever the wrapper object is in a dictionary the +In the case of :class:`!ListWrapper`, whenever the wrapper object is in a dictionary the wrapped list must not change to avoid anomalies. Don't do this unless you are prepared to think hard about the requirements and the consequences of not meeting them correctly. Consider yourself warned. @@ -581,9 +584,9 @@ exhaustive test suites that exercise every line of code in a module. An appropriate testing discipline can help build large complex applications in Python as well as having interface specifications would. In fact, it can be better because an interface specification cannot test certain properties of a -program. For example, the :meth:`append` method is expected to add new elements +program. For example, the :meth:`list.append` method is expected to add new elements to the end of some internal list; an interface specification cannot test that -your :meth:`append` implementation will actually do this correctly, but it's +your :meth:`list.append` implementation will actually do this correctly, but it's trivial to check this property in a test suite. Writing test suites is very helpful, and you might want to design your code to @@ -599,14 +602,14 @@ Why is there no goto? In the 1970s people realized that unrestricted goto could lead to messy "spaghetti" code that was hard to understand and revise. In a high-level language, it is also unneeded as long as there -are ways to branch (in Python, with ``if`` statements and ``or``, -``and``, and ``if-else`` expressions) and loop (with ``while`` -and ``for`` statements, possibly containing ``continue`` and ``break``). +are ways to branch (in Python, with :keyword:`if` statements and :keyword:`or`, +:keyword:`and`, and :keyword:`if`/:keyword:`else` expressions) and loop (with :keyword:`while` +and :keyword:`for` statements, possibly containing :keyword:`continue` and :keyword:`break`). One can also use exceptions to provide a "structured goto" that works even across function calls. Many feel that exceptions can conveniently emulate all -reasonable uses of the "go" or "goto" constructs of C, Fortran, and other +reasonable uses of the ``go`` or ``goto`` constructs of C, Fortran, and other languages. For example:: class label(Exception): pass # declare a label @@ -620,7 +623,7 @@ languages. For example:: ... This doesn't allow you to jump into the middle of a loop, but that's usually -considered an abuse of goto anyway. Use sparingly. +considered an abuse of ``goto`` anyway. Use sparingly. Why can't raw strings (r-strings) end with a backslash? @@ -652,7 +655,7 @@ If you're trying to build a pathname for a DOS command, try e.g. one of :: Why doesn't Python have a "with" statement for attribute assignments? --------------------------------------------------------------------- -Python has a 'with' statement that wraps the execution of a block, calling code +Python has a :keyword:`with` statement that wraps the execution of a block, calling code on the entrance and exit from the block. Some languages have a construct that looks like this:: @@ -679,13 +682,13 @@ For instance, take the following incomplete snippet:: with a: print(x) -The snippet assumes that "a" must have a member attribute called "x". However, +The snippet assumes that ``a`` must have a member attribute called ``x``. However, there is nothing in Python that tells the interpreter this. What should happen -if "a" is, let us say, an integer? If there is a global variable named "x", -will it be used inside the with block? As you see, the dynamic nature of Python +if ``a`` is, let us say, an integer? If there is a global variable named ``x``, +will it be used inside the :keyword:`with` block? As you see, the dynamic nature of Python makes such choices much harder. -The primary benefit of "with" and similar language features (reduction of code +The primary benefit of :keyword:`with` and similar language features (reduction of code volume) can, however, easily be achieved in Python by assignment. Instead of:: function(args).mydict[index][index].a = 21 @@ -714,7 +717,8 @@ Why don't generators support the with statement? For technical reasons, a generator used directly as a context manager would not work correctly. When, as is most common, a generator is used as an iterator run to completion, no closing is needed. When it is, wrap -it as "contextlib.closing(generator)" in the 'with' statement. +it as :func:`contextlib.closing(generator) ` +in the :keyword:`with` statement. Why are colons required for the if/while/def/class statements? From 8ce47395e3ce229aaa93d7a93519ffc5dfe96e87 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:19:36 -0700 Subject: [PATCH 109/151] gh-91708: Revert params note in urllib.parse.urlparse table (#96699) Revert params note in urllib.parse.urlparse table --- Doc/library/urllib.parse.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index 1478b34bc95514..96b396510794b4 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -113,7 +113,8 @@ or on combining URL components into a URL string. +------------------+-------+-------------------------+------------------------+ | :attr:`path` | 2 | Hierarchical path | empty string | +------------------+-------+-------------------------+------------------------+ - | :attr:`params` | 3 | No longer used | always an empty string | + | :attr:`params` | 3 | Parameters for last | empty string | + | | | path element | | +------------------+-------+-------------------------+------------------------+ | :attr:`query` | 4 | Query component | empty string | +------------------+-------+-------------------------+------------------------+ From 053555cbc8c4fe9392a3967b1a98a6d94c2e1484 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 7 Oct 2022 22:21:42 +0300 Subject: [PATCH 110/151] gh-96346: Use double caching for re._compile() (#96347) --- Lib/re/__init__.py | 67 +++++++++++++------ ...2-08-27-23-16-09.gh-issue-96346.jJX14I.rst | 1 + 2 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-08-27-23-16-09.gh-issue-96346.jJX14I.rst diff --git a/Lib/re/__init__.py b/Lib/re/__init__.py index d58c2117ef3e14..8d6a4ef3880f0c 100644 --- a/Lib/re/__init__.py +++ b/Lib/re/__init__.py @@ -229,6 +229,7 @@ def compile(pattern, flags=0): def purge(): "Clear the regular expression caches" _cache.clear() + _cache2.clear() _compile_repl.cache_clear() def template(pattern, flags=0): @@ -266,40 +267,64 @@ def escape(pattern): # -------------------------------------------------------------------- # internals -_cache = {} # ordered! - +# Use the fact that dict keeps the insertion order. +# _cache2 uses the simple FIFO policy which has better latency. +# _cache uses the LRU policy which has better hit rate. +_cache = {} # LRU +_cache2 = {} # FIFO _MAXCACHE = 512 +_MAXCACHE2 = 256 +assert _MAXCACHE2 < _MAXCACHE + def _compile(pattern, flags): # internal: compile pattern if isinstance(flags, RegexFlag): flags = flags.value try: - return _cache[type(pattern), pattern, flags] + return _cache2[type(pattern), pattern, flags] except KeyError: pass - if isinstance(pattern, Pattern): - if flags: - raise ValueError( - "cannot process flags argument with a compiled pattern") - return pattern - if not _compiler.isstring(pattern): - raise TypeError("first argument must be string or compiled pattern") - if flags & T: - import warnings - warnings.warn("The re.TEMPLATE/re.T flag is deprecated " - "as it is an undocumented flag " - "without an obvious purpose. " - "Don't use it.", - DeprecationWarning) - p = _compiler.compile(pattern, flags) - if not (flags & DEBUG): + + key = (type(pattern), pattern, flags) + # Item in _cache should be moved to the end if found. + p = _cache.pop(key, None) + if p is None: + if isinstance(pattern, Pattern): + if flags: + raise ValueError( + "cannot process flags argument with a compiled pattern") + return pattern + if not _compiler.isstring(pattern): + raise TypeError("first argument must be string or compiled pattern") + if flags & T: + import warnings + warnings.warn("The re.TEMPLATE/re.T flag is deprecated " + "as it is an undocumented flag " + "without an obvious purpose. " + "Don't use it.", + DeprecationWarning) + p = _compiler.compile(pattern, flags) + if flags & DEBUG: + return p if len(_cache) >= _MAXCACHE: - # Drop the oldest item + # Drop the least recently used item. + # next(iter(_cache)) is known to have linear amortized time, + # but it is used here to avoid a dependency from using OrderedDict. + # For the small _MAXCACHE value it doesn't make much of a difference. try: del _cache[next(iter(_cache))] except (StopIteration, RuntimeError, KeyError): pass - _cache[type(pattern), pattern, flags] = p + # Append to the end. + _cache[key] = p + + if len(_cache2) >= _MAXCACHE2: + # Drop the oldest item. + try: + del _cache2[next(iter(_cache2))] + except (StopIteration, RuntimeError, KeyError): + pass + _cache2[key] = p return p @functools.lru_cache(_MAXCACHE) diff --git a/Misc/NEWS.d/next/Library/2022-08-27-23-16-09.gh-issue-96346.jJX14I.rst b/Misc/NEWS.d/next/Library/2022-08-27-23-16-09.gh-issue-96346.jJX14I.rst new file mode 100644 index 00000000000000..9883348b9c3e24 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-08-27-23-16-09.gh-issue-96346.jJX14I.rst @@ -0,0 +1 @@ +Use double caching for compiled RE patterns. From 8cb415b1fe6d9dcaeb99b2f8b147d70f6da0c1e8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 7 Oct 2022 12:56:50 -0700 Subject: [PATCH 111/151] GH-88968: Reject socket that is already used as a transport (#98010) --- Lib/asyncio/selector_events.py | 3 +++ Lib/test/test_asyncio/test_selector_events.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index c9bbe2ac014351..d2ee49dd88f8cf 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -58,6 +58,7 @@ def __init__(self, selector=None): def _make_socket_transport(self, sock, protocol, waiter=None, *, extra=None, server=None): + self._ensure_fd_no_transport(sock) return _SelectorSocketTransport(self, sock, protocol, waiter, extra, server) @@ -68,6 +69,7 @@ def _make_ssl_transport( ssl_handshake_timeout=constants.SSL_HANDSHAKE_TIMEOUT, ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT, ): + self._ensure_fd_no_transport(rawsock) ssl_protocol = sslproto.SSLProtocol( self, protocol, sslcontext, waiter, server_side, server_hostname, @@ -80,6 +82,7 @@ def _make_ssl_transport( def _make_datagram_transport(self, sock, protocol, address=None, waiter=None, extra=None): + self._ensure_fd_no_transport(sock) return _SelectorDatagramTransport(self, sock, protocol, address, waiter, extra) diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 22dcfb23083522..796037bcf59c49 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -61,8 +61,10 @@ def setUp(self): def test_make_socket_transport(self): m = mock.Mock() self.loop.add_reader = mock.Mock() + self.loop._ensure_fd_no_transport = mock.Mock() transport = self.loop._make_socket_transport(m, asyncio.Protocol()) self.assertIsInstance(transport, _SelectorSocketTransport) + self.assertEqual(self.loop._ensure_fd_no_transport.call_count, 1) # Calling repr() must not fail when the event loop is closed self.loop.close() @@ -78,8 +80,10 @@ def test_make_ssl_transport_without_ssl_error(self): self.loop.add_writer = mock.Mock() self.loop.remove_reader = mock.Mock() self.loop.remove_writer = mock.Mock() + self.loop._ensure_fd_no_transport = mock.Mock() with self.assertRaises(RuntimeError): self.loop._make_ssl_transport(m, m, m, m) + self.assertEqual(self.loop._ensure_fd_no_transport.call_count, 1) def test_close(self): class EventLoop(BaseSelectorEventLoop): From 7a39f025234771349f0b8bfe4779c7adafec86fb Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Fri, 7 Oct 2022 14:38:35 -0700 Subject: [PATCH 112/151] gh-97997: Add col_offset field to tokenizer and use that for AST nodes (#98000) --- ...2-10-06-23-13-34.gh-issue-97997.JQaJKF.rst | 1 + Parser/tokenizer.c | 52 +++++++++++++++---- Parser/tokenizer.h | 2 + 3 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-06-23-13-34.gh-issue-97997.JQaJKF.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-06-23-13-34.gh-issue-97997.JQaJKF.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-23-13-34.gh-issue-97997.JQaJKF.rst new file mode 100644 index 00000000000000..5cb5e2126638be --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-23-13-34.gh-issue-97997.JQaJKF.rst @@ -0,0 +1 @@ +Add running column offset to the tokenizer state to avoid calculating AST column information with pointer arithmetic. diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index c5d3e580247cc1..1c356d3d47c945 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -37,6 +37,11 @@ #define TABSIZE 8 #define MAKE_TOKEN(token_type) token_setup(tok, token, token_type, p_start, p_end) +#define MAKE_TYPE_COMMENT_TOKEN(token_type, col_offset, end_col_offset) (\ + type_comment_token_setup(tok, token, token_type, col_offset, end_col_offset, p_start, p_end)) +#define ADVANCE_LINENO() \ + tok->lineno++; \ + tok->col_offset = 0; /* Forward */ static struct tok_state *tok_new(void); @@ -73,6 +78,8 @@ tok_new(void) tok->pendin = 0; tok->prompt = tok->nextprompt = NULL; tok->lineno = 0; + tok->starting_col_offset = -1; + tok->col_offset = -1; tok->level = 0; tok->altindstack[0] = 0; tok->decoding_state = STATE_INIT; @@ -871,7 +878,7 @@ tok_underflow_string(struct tok_state *tok) { tok->buf = tok->cur; } tok->line_start = tok->cur; - tok->lineno++; + ADVANCE_LINENO(); tok->inp = end; return 1; } @@ -930,7 +937,7 @@ tok_underflow_interactive(struct tok_state *tok) { else if (tok->start != NULL) { Py_ssize_t cur_multi_line_start = tok->multi_line_start - tok->buf; size_t size = strlen(newtok); - tok->lineno++; + ADVANCE_LINENO(); if (!tok_reserve_buf(tok, size + 1)) { PyMem_Free(tok->buf); tok->buf = NULL; @@ -943,7 +950,7 @@ tok_underflow_interactive(struct tok_state *tok) { tok->multi_line_start = tok->buf + cur_multi_line_start; } else { - tok->lineno++; + ADVANCE_LINENO(); PyMem_Free(tok->buf); tok->buf = newtok; tok->cur = tok->buf; @@ -998,7 +1005,7 @@ tok_underflow_file(struct tok_state *tok) { *tok->inp = '\0'; } - tok->lineno++; + ADVANCE_LINENO(); if (tok->decoding_state != STATE_NORMAL) { if (tok->lineno > 2) { tok->decoding_state = STATE_NORMAL; @@ -1056,6 +1063,7 @@ tok_nextc(struct tok_state *tok) int rc; for (;;) { if (tok->cur != tok->inp) { + tok->col_offset++; return Py_CHARMASK(*tok->cur++); /* Fast path */ } if (tok->done != E_OK) { @@ -1104,6 +1112,7 @@ tok_backup(struct tok_state *tok, int c) if ((int)(unsigned char)*tok->cur != c) { Py_FatalError("tok_backup: wrong character"); } + tok->col_offset--; } } @@ -1390,6 +1399,19 @@ tok_continuation_line(struct tok_state *tok) { return c; } +static int +type_comment_token_setup(struct tok_state *tok, struct token *token, int type, int col_offset, + int end_col_offset, const char *start, const char *end) +{ + token->level = tok->level; + token->lineno = token->end_lineno = tok->lineno; + token->col_offset = col_offset; + token->end_col_offset = end_col_offset; + token->start = start; + token->end = end; + return type; +} + static int token_setup(struct tok_state *tok, struct token *token, int type, const char *start, const char *end) { @@ -1397,14 +1419,13 @@ token_setup(struct tok_state *tok, struct token *token, int type, const char *st token->level = tok->level; token->lineno = type == STRING ? tok->first_lineno : tok->lineno; token->end_lineno = tok->lineno; - token->col_offset = -1; - token->end_col_offset = -1; + token->col_offset = token->end_col_offset = -1; token->start = start; token->end = end; + if (start != NULL && end != NULL) { - const char *line_start = type == STRING ? tok->multi_line_start : tok->line_start; - token->col_offset = (start >= line_start) ? (int)(start - line_start) : -1; - token->end_col_offset = (end >= tok->line_start) ? (int)(end - tok->line_start) : -1; + token->col_offset = tok->starting_col_offset; + token->end_col_offset = tok->col_offset; } return type; } @@ -1419,6 +1440,7 @@ tok_get(struct tok_state *tok, struct token *token) const char *p_end = NULL; nextline: tok->start = NULL; + tok->starting_col_offset = -1; blankline = 0; /* Get indentation level */ @@ -1518,6 +1540,7 @@ tok_get(struct tok_state *tok, struct token *token) } tok->start = tok->cur; + tok->starting_col_offset = tok->col_offset; /* Return pending indents/dedents */ if (tok->pendin != 0) { @@ -1565,10 +1588,12 @@ tok_get(struct tok_state *tok, struct token *token) /* Set start of current token */ tok->start = tok->cur == NULL ? NULL : tok->cur - 1; + tok->starting_col_offset = tok->col_offset - 1; /* Skip comment, unless it's a type comment */ if (c == '#') { const char *prefix, *p, *type_start; + int current_starting_col_offset; while (c != EOF && c != '\n') { c = tok_nextc(tok); @@ -1576,14 +1601,17 @@ tok_get(struct tok_state *tok, struct token *token) if (tok->type_comments) { p = tok->start; + current_starting_col_offset = tok->starting_col_offset; prefix = type_comment_prefix; while (*prefix && p < tok->cur) { if (*prefix == ' ') { while (*p == ' ' || *p == '\t') { p++; + current_starting_col_offset++; } } else if (*prefix == *p) { p++; + current_starting_col_offset++; } else { break; } @@ -1594,7 +1622,9 @@ tok_get(struct tok_state *tok, struct token *token) /* This is a type comment if we matched all of type_comment_prefix. */ if (!*prefix) { int is_type_ignore = 1; + // +6 in order to skip the word 'ignore' const char *ignore_end = p + 6; + const int ignore_end_col_offset = current_starting_col_offset + 6; tok_backup(tok, c); /* don't eat the newline or EOF */ type_start = p; @@ -1615,11 +1645,11 @@ tok_get(struct tok_state *tok, struct token *token) tok_nextc(tok); tok->atbol = 1; } - return MAKE_TOKEN(TYPE_IGNORE); + return MAKE_TYPE_COMMENT_TOKEN(TYPE_IGNORE, ignore_end_col_offset, tok->col_offset); } else { p_start = type_start; p_end = tok->cur; - return MAKE_TOKEN(TYPE_COMMENT); + return MAKE_TYPE_COMMENT_TOKEN(TYPE_COMMENT, current_starting_col_offset, tok->col_offset); } } } diff --git a/Parser/tokenizer.h b/Parser/tokenizer.h index 5b8c7f314386ec..2542d30e1da0ed 100644 --- a/Parser/tokenizer.h +++ b/Parser/tokenizer.h @@ -57,6 +57,8 @@ struct tok_state { int lineno; /* Current line number */ int first_lineno; /* First line of a single line or multi line string expression (cf. issue 16806) */ + int starting_col_offset; /* The column offset at the beginning of a token */ + int col_offset; /* Current col offset */ int level; /* () [] {} Parentheses nesting level */ /* Used to allow free continuations inside them */ char parenstack[MAXLEVEL]; From ca135e17a22f0dfa37ab130708b6d72080a903f6 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 7 Oct 2022 22:41:35 +0100 Subject: [PATCH 113/151] gh-92886: [clinic.py] raise exception on invalid input instead of assertion (GH-98051) Tests should pass with -O (assertions off). Automerge-Triggered-By: GH:iritkatriel --- Lib/test/test_clinic.py | 2 +- Tools/clinic/clinic.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 4aa9691a4829d1..8ab40c694f711d 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -153,7 +153,7 @@ def test_right_only(self): def test_have_left_options_but_required_is_empty(self): def fn(): clinic.permute_optional_groups(['a'], [], []) - self.assertRaises(AssertionError, fn) + self.assertRaises(ValueError, fn) class ClinicLinearFormatTest(TestCase): diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 30a676328706f6..a8687e3470a185 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -495,7 +495,8 @@ def permute_optional_groups(left, required, right): result = [] if not required: - assert not left + if left: + raise ValueError("required is empty but left is not") accumulator = [] counts = set() From 875841adaf8f5217ba62d7d885fbd720d605d8c2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 7 Oct 2022 14:44:56 -0700 Subject: [PATCH 114/151] gh-96073: fix backticks in NEWS entry (GH-98056) Automerge-Triggered-By: GH:JelleZijlstra --- Doc/faq/design.rst | 2 +- .../next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index 763222aba8b6ed..11d01374dc1e79 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -62,7 +62,7 @@ and think it is a bug in Python. It's not. This has little to do with Python, and much more to do with how the underlying platform handles floating-point numbers. -The :class:`float` type in CPython uses a C :c:type:`double` for storage. A +The :class:`float` type in CPython uses a C ``double`` for storage. A :class:`float` object's value is stored in binary floating-point with a fixed precision (typically 53 bits) and Python uses C operations, which in turn rely on the hardware implementation in the processor, to perform floating-point diff --git a/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst b/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst index 0e6dd8d360cbc9..8f20588c4c585e 100644 --- a/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst +++ b/Misc/NEWS.d/next/Library/2022-08-29-12-35-28.gh-issue-96073.WaGstf.rst @@ -1 +1 @@ -In :mod:`inspect`, fix overeager replacement of "`typing.`" in formatting annotations. +In :mod:`inspect`, fix overeager replacement of "``typing.``" in formatting annotations. From b9bd94d38f4dc9256f375606c66789f02182298c Mon Sep 17 00:00:00 2001 From: Hagai Helman Tov Date: Sat, 8 Oct 2022 01:06:16 +0300 Subject: [PATCH 115/151] gh-96288: Add a sentence to `os.mkdir`'s docstring. (#96271) --- Modules/clinic/posixmodule.c.h | 5 +++-- Modules/posixmodule.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 7fac07804686b4..31bd01d1c3c1fb 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1849,7 +1849,8 @@ PyDoc_STRVAR(os_mkdir__doc__, "dir_fd may not be implemented on your platform.\n" " If it is unavailable, using it will raise a NotImplementedError.\n" "\n" -"The mode argument is ignored on Windows."); +"The mode argument is ignored on Windows. Where it is used, the current umask\n" +"value is first masked out."); #define OS_MKDIR_METHODDEF \ {"mkdir", _PyCFunction_CAST(os_mkdir), METH_FASTCALL|METH_KEYWORDS, os_mkdir__doc__}, @@ -11367,4 +11368,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=dd43d388b442c96d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=471ab8f2ad3d46b0 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a72d57771c2294..1e556fc904b77b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4539,12 +4539,13 @@ If dir_fd is not None, it should be a file descriptor open to a directory, dir_fd may not be implemented on your platform. If it is unavailable, using it will raise a NotImplementedError. -The mode argument is ignored on Windows. +The mode argument is ignored on Windows. Where it is used, the current umask +value is first masked out. [clinic start generated code]*/ static PyObject * os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) -/*[clinic end generated code: output=a70446903abe821f input=e965f68377e9b1ce]*/ +/*[clinic end generated code: output=a70446903abe821f input=a61722e1576fab03]*/ { int result; #ifdef HAVE_MKDIRAT From 864f9b97bac72b0969bc2961b658a467e674e0f5 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:21:13 -0700 Subject: [PATCH 116/151] gh-61105: Add default param, note on using cookiejar subclass (#95427) --- Doc/library/http.cookiejar.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst index eb31315438f66a..87ef156a0bed57 100644 --- a/Doc/library/http.cookiejar.rst +++ b/Doc/library/http.cookiejar.rst @@ -61,7 +61,7 @@ The following classes are provided: responsible for storing and retrieving cookies from a file or database. -.. class:: FileCookieJar(filename, delayload=None, policy=None) +.. class:: FileCookieJar(filename=None, delayload=None, policy=None) *policy* is an object implementing the :class:`CookiePolicy` interface. For the other arguments, see the documentation for the corresponding attributes. @@ -71,6 +71,8 @@ The following classes are provided: :meth:`load` or :meth:`revert` method is called. Subclasses of this class are documented in section :ref:`file-cookie-jar-classes`. + This should not be initialized directly – use its subclasses below instead. + .. versionchanged:: 3.8 The filename parameter supports a :term:`path-like object`. @@ -317,7 +319,7 @@ FileCookieJar subclasses and co-operation with web browsers The following :class:`CookieJar` subclasses are provided for reading and writing. -.. class:: MozillaCookieJar(filename, delayload=None, policy=None) +.. class:: MozillaCookieJar(filename=None, delayload=None, policy=None) A :class:`FileCookieJar` that can load from and save cookies to disk in the Mozilla ``cookies.txt`` file format (which is also used by curl and the Lynx @@ -338,7 +340,7 @@ writing. Mozilla. -.. class:: LWPCookieJar(filename, delayload=None, policy=None) +.. class:: LWPCookieJar(filename=None, delayload=None, policy=None) A :class:`FileCookieJar` that can load from and save cookies to disk in format compatible with the libwww-perl library's ``Set-Cookie3`` file format. This is From c437b6e9c4ad9827a38d1bbe85a0286647b7430a Mon Sep 17 00:00:00 2001 From: Frazer McLean Date: Sat, 8 Oct 2022 00:24:17 +0200 Subject: [PATCH 117/151] GH-83901: Improve Signature.bind error message for missing keyword-only params (#95347) Fixes GH-83901 --- Lib/inspect.py | 8 ++++++-- Lib/test/test_inspect.py | 3 ++- .../Library/2022-07-27-19-47-51.gh-issue-83901.OSw06c.rst | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-27-19-47-51.gh-issue-83901.OSw06c.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 585875a30c35c3..f6750c3b211fbd 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3102,8 +3102,12 @@ def _bind(self, args, kwargs, *, partial=False): parameters_ex = (param,) break else: - msg = 'missing a required argument: {arg!r}' - msg = msg.format(arg=param.name) + if param.kind == _KEYWORD_ONLY: + argtype = ' keyword-only' + else: + argtype = '' + msg = 'missing a required{argtype} argument: {arg!r}' + msg = msg.format(arg=param.name, argtype=argtype) raise TypeError(msg) from None else: # We have a positional argument to process diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 61fed323dceae4..cfc6e411ea680d 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -3898,7 +3898,8 @@ def test(foo, *, bar): self.call(test, 1, bar=2, spam='ham') with self.assertRaisesRegex(TypeError, - "missing a required argument: 'bar'"): + "missing a required keyword-only " + "argument: 'bar'"): self.call(test, 1) def test(foo, *, bar, **bin): diff --git a/Misc/NEWS.d/next/Library/2022-07-27-19-47-51.gh-issue-83901.OSw06c.rst b/Misc/NEWS.d/next/Library/2022-07-27-19-47-51.gh-issue-83901.OSw06c.rst new file mode 100644 index 00000000000000..da407905a886fd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-27-19-47-51.gh-issue-83901.OSw06c.rst @@ -0,0 +1 @@ +Improve :meth:`Signature.bind ` error message for missing keyword-only arguments. From 5a71338125fd2219fc09d6f56da6ef91871ade41 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:38:20 -0700 Subject: [PATCH 118/151] gh-90085: Remove vestigial -t and -c timeit options (#94941) See bpo-28240. The functionality was removed in 3d7feb9ac2. The options had been deprecated since Python 3.3 --- Lib/timeit.py | 5 ++--- .../Library/2022-07-17-22-31-32.gh-issue-90085.c4FWcS.rst | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-17-22-31-32.gh-issue-90085.c4FWcS.rst diff --git a/Lib/timeit.py b/Lib/timeit.py index 9dfd454936e6b8..0cf8db67723a49 100755 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -259,10 +259,9 @@ def main(args=None, *, _wrap_timer=None): args = sys.argv[1:] import getopt try: - opts, args = getopt.getopt(args, "n:u:s:r:tcpvh", + opts, args = getopt.getopt(args, "n:u:s:r:pvh", ["number=", "setup=", "repeat=", - "time", "clock", "process", - "verbose", "unit=", "help"]) + "process", "verbose", "unit=", "help"]) except getopt.error as err: print(err) print("use -h/--help for command line help") diff --git a/Misc/NEWS.d/next/Library/2022-07-17-22-31-32.gh-issue-90085.c4FWcS.rst b/Misc/NEWS.d/next/Library/2022-07-17-22-31-32.gh-issue-90085.c4FWcS.rst new file mode 100644 index 00000000000000..37952adc8f1a92 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-17-22-31-32.gh-issue-90085.c4FWcS.rst @@ -0,0 +1,3 @@ +Remove ``-c/--clock`` and ``-t/--time`` CLI options of :mod:`timeit`. +The options had been deprecated since Python 3.3 and the functionality +was removed in Python 3.7. Patch by Shantanu Jain. From 644402991348676107d577166505accdbe4f6ad7 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 7 Oct 2022 15:39:53 -0700 Subject: [PATCH 119/151] gh-94808: Fix regex on exotic platforms (#98036) The test failed on a buildbot because the pointer was only 7 hex characters. To be safe, I bumped it down to 3: 4 in case we have 32-bit platforms, and 3 in case the pointer is very small. --- Lib/test/test_unicode.py | 4 ++-- Makefile.pre.in | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index b9ee9d30318c46..5b816574f2c335 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2811,7 +2811,7 @@ def check_format(expected, format, *args): # We cannot test the exact result, # because it returns a hex representation of a C pointer, # which is going to be different each time. But, we can test the format. - p_format_regex = r'^0x[a-zA-Z0-9]{8,}$' + p_format_regex = r'^0x[a-zA-Z0-9]{3,}$' p_format1 = PyUnicode_FromFormat(b'%p', 'abc') self.assertIsInstance(p_format1, str) self.assertRegex(p_format1, p_format_regex) @@ -2819,7 +2819,7 @@ def check_format(expected, format, *args): p_format2 = PyUnicode_FromFormat(b'%p %p', '123456', b'xyz') self.assertIsInstance(p_format2, str) self.assertRegex(p_format2, - r'0x[a-zA-Z0-9]{8,} 0x[a-zA-Z0-9]{8,}') + r'0x[a-zA-Z0-9]{3,} 0x[a-zA-Z0-9]{3,}') # Extra args are ignored: p_format3 = PyUnicode_FromFormat(b'%p', '123456', None, b'xyz') diff --git a/Makefile.pre.in b/Makefile.pre.in index 11118354f15def..4602db69d15af9 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2038,6 +2038,7 @@ TESTSUBDIRS= distutils/tests \ test/test_zoneinfo test/test_zoneinfo/data \ test/test_unittest test/test_unittest/testmock \ test/tracedmodules \ + test/typinganndata \ test/xmltestdata test/xmltestdata/c14n-20 \ test/ziptestdata From c42b93186ced447a095cb0a46bd30eb841a3f0b9 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:51:50 -0700 Subject: [PATCH 120/151] gh-57179: Add note on symlinks for os.walk (#94799) --- Doc/library/os.rst | 3 ++- Lib/os.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 23b014b0b65924..8727f811def1c0 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3222,7 +3222,8 @@ features: filenames)``. *dirpath* is a string, the path to the directory. *dirnames* is a list of the - names of the subdirectories in *dirpath* (excluding ``'.'`` and ``'..'``). + names of the subdirectories in *dirpath* (including symlinks to directories, + and excluding ``'.'`` and ``'..'``). *filenames* is a list of the names of the non-directory files in *dirpath*. Note that the names in the lists contain no path components. To get a full path (which begins with *top*) to a file or directory in *dirpath*, do diff --git a/Lib/os.py b/Lib/os.py index 648188e0f13490..fd1e774fdcbcfa 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -288,7 +288,8 @@ def walk(top, topdown=True, onerror=None, followlinks=False): dirpath, dirnames, filenames dirpath is a string, the path to the directory. dirnames is a list of - the names of the subdirectories in dirpath (excluding '.' and '..'). + the names of the subdirectories in dirpath (including symlinks to directories, + and excluding '.' and '..'). filenames is a list of the names of the non-directory files in dirpath. Note that the names in the lists are just names, with no path components. To get a full path (which begins with top) to a file or directory in From dcbae747fe4a9a01f75089e9c2c4523a72292019 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sat, 8 Oct 2022 00:12:28 +0100 Subject: [PATCH 121/151] gh-92886: make test_coroutines pass with -O (assertions off) (GH-98060) Automerge-Triggered-By: GH:iritkatriel --- Lib/test/test_coroutines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 9a2279d353ffa4..f91c9cc47741b5 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1287,7 +1287,7 @@ async def __aexit__(self, *exc): async def func(): async with CM(): - assert (1, ) == 1 + self.assertEqual((1, ), 1) with self.assertRaises(AssertionError): run_async(func()) From efbc78394c251cabb2f52245ee497be1c7aa50a8 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Sat, 8 Oct 2022 00:30:23 +0100 Subject: [PATCH 122/151] gh-92886: make test_ast pass with -O (assertions off) (GH-98058) -O does not strip docstrings. Automerge-Triggered-By: GH:iritkatriel --- Lib/test/test_dis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index fc2862c61baadb..667b267d27a06a 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1212,7 +1212,7 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs): return output.getvalue() -if sys.flags.optimize: +if dis.code_info.__doc__ is None: code_info_consts = "0: None" else: code_info_consts = "0: 'Formatted details of methods, functions, or code.'" From 6cd489d76ca747ce8af12340b434d61233b04a72 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sat, 8 Oct 2022 01:24:01 +0100 Subject: [PATCH 123/151] GH-94182: Run the PidfdChildWatcher on the running loop (#94184) There is no reason for this watcher to be attached to any particular loop. This should make it safe to use regardless of the lifetime of the event loop running in the main thread (relative to other loops). Co-authored-by: Yury Selivanov Co-authored-by: Jelle Zijlstra --- Lib/asyncio/unix_events.py | 44 +++++---------- Lib/test/test_asyncio/test_subprocess.py | 54 +++++++++++++++---- ...2-06-24-08-49-47.gh-issue-94182.Wknau0.rst | 1 + 3 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 96e6d73a759794..0f67b4d469f28c 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -912,10 +912,6 @@ class PidfdChildWatcher(AbstractChildWatcher): recent (5.3+) kernels. """ - def __init__(self): - self._loop = None - self._callbacks = {} - def __enter__(self): return self @@ -923,35 +919,22 @@ def __exit__(self, exc_type, exc_value, exc_traceback): pass def is_active(self): - return self._loop is not None and self._loop.is_running() + return True def close(self): - self.attach_loop(None) + pass def attach_loop(self, loop): - if self._loop is not None and loop is None and self._callbacks: - warnings.warn( - 'A loop is being detached ' - 'from a child watcher with pending handlers', - RuntimeWarning) - for pidfd, _, _ in self._callbacks.values(): - self._loop._remove_reader(pidfd) - os.close(pidfd) - self._callbacks.clear() - self._loop = loop + pass def add_child_handler(self, pid, callback, *args): - existing = self._callbacks.get(pid) - if existing is not None: - self._callbacks[pid] = existing[0], callback, args - else: - pidfd = os.pidfd_open(pid) - self._loop._add_reader(pidfd, self._do_wait, pid) - self._callbacks[pid] = pidfd, callback, args + loop = events.get_running_loop() + pidfd = os.pidfd_open(pid) + loop._add_reader(pidfd, self._do_wait, pid, pidfd, callback, args) - def _do_wait(self, pid): - pidfd, callback, args = self._callbacks.pop(pid) - self._loop._remove_reader(pidfd) + def _do_wait(self, pid, pidfd, callback, args): + loop = events.get_running_loop() + loop._remove_reader(pidfd) try: _, status = os.waitpid(pid, 0) except ChildProcessError: @@ -969,12 +952,9 @@ def _do_wait(self, pid): callback(pid, returncode, *args) def remove_child_handler(self, pid): - try: - pidfd, _, _ = self._callbacks.pop(pid) - except KeyError: - return False - self._loop._remove_reader(pidfd) - os.close(pidfd) + # asyncio never calls remove_child_handler() !!! + # The method is no-op but is implemented because + # abstract base classes require it. return True diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 9bc60b9dc2ae2e..6ba889407b802e 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -4,6 +4,7 @@ import sys import unittest import warnings +import functools from unittest import mock import asyncio @@ -30,6 +31,19 @@ 'sys.stdout.buffer.write(data)'))] +@functools.cache +def _has_pidfd_support(): + if not hasattr(os, 'pidfd_open'): + return False + + try: + os.close(os.pidfd_open(os.getpid())) + except OSError: + return False + + return True + + def tearDownModule(): asyncio.set_event_loop_policy(None) @@ -708,17 +722,8 @@ class SubprocessFastWatcherTests(SubprocessWatcherMixin, Watcher = unix_events.FastChildWatcher - def has_pidfd_support(): - if not hasattr(os, 'pidfd_open'): - return False - try: - os.close(os.pidfd_open(os.getpid())) - except OSError: - return False - return True - @unittest.skipUnless( - has_pidfd_support(), + _has_pidfd_support(), "operating system does not support pidfds", ) class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, @@ -751,6 +756,35 @@ async def execute(): mock.call.__exit__(RuntimeError, mock.ANY, mock.ANY), ]) + + @unittest.skipUnless( + _has_pidfd_support(), + "operating system does not support pidfds", + ) + def test_create_subprocess_with_pidfd(self): + async def in_thread(): + proc = await asyncio.create_subprocess_exec( + *PROGRAM_CAT, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + stdout, stderr = await proc.communicate(b"some data") + return proc.returncode, stdout + + async def main(): + # asyncio.Runner did not call asyncio.set_event_loop() + with self.assertRaises(RuntimeError): + asyncio.get_event_loop_policy().get_event_loop() + return await asyncio.to_thread(asyncio.run, in_thread()) + + asyncio.set_child_watcher(asyncio.PidfdChildWatcher()) + try: + with asyncio.Runner(loop_factory=asyncio.new_event_loop) as runner: + returncode, stdout = runner.run(main()) + self.assertEqual(returncode, 0) + self.assertEqual(stdout, b'some data') + finally: + asyncio.set_child_watcher(None) else: # Windows class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase): diff --git a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst new file mode 100644 index 00000000000000..c7be8640ef1f7e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst @@ -0,0 +1 @@ +run the :class:`asyncio.PidfdChildWatcher` on the running loop, this allows event loops to run subprocesses when there is no default event loop running on the main thread From 776f8942d4d302ce56f14cada8caa7ee85348038 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sat, 8 Oct 2022 05:59:09 +0530 Subject: [PATCH 124/151] GH-98023: Change default child watcher to PidfdChildWatcher on supported systems (#98024) --- Lib/asyncio/unix_events.py | 16 +++++++++++++++- Lib/test/test_asyncio/test_unix_events.py | 14 +++++++++++++- ...2022-10-07-09-52-37.gh-issue-98023.aliEcl.rst | 1 + 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-07-09-52-37.gh-issue-98023.aliEcl.rst diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 0f67b4d469f28c..7fc75cd17ef741 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1403,6 +1403,17 @@ def _do_waitpid(self, loop, expected_pid, callback, args): self._threads.pop(expected_pid) +def can_use_pidfd(): + if not hasattr(os, 'pidfd_open'): + return False + try: + pid = os.getpid() + os.close(os.pidfd_open(pid, 0)) + except OSError: + # blocked by security policy like SECCOMP + return False + return True + class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy): """UNIX event loop policy with a watcher for child processes.""" @@ -1415,7 +1426,10 @@ def __init__(self): def _init_watcher(self): with events._lock: if self._watcher is None: # pragma: no branch - self._watcher = ThreadedChildWatcher() + if can_use_pidfd(): + self._watcher = PidfdChildWatcher() + else: + self._watcher = ThreadedChildWatcher() if threading.current_thread() is threading.main_thread(): self._watcher.attach_loop(self._local._loop) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 5bad21ecbae4af..03fb5e649d8e97 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -1702,7 +1702,8 @@ def create_policy(self): def test_get_default_child_watcher(self): policy = self.create_policy() self.assertIsNone(policy._watcher) - + unix_events.can_use_pidfd = mock.Mock() + unix_events.can_use_pidfd.return_value = False watcher = policy.get_child_watcher() self.assertIsInstance(watcher, asyncio.ThreadedChildWatcher) @@ -1710,6 +1711,17 @@ def test_get_default_child_watcher(self): self.assertIs(watcher, policy.get_child_watcher()) + policy = self.create_policy() + self.assertIsNone(policy._watcher) + unix_events.can_use_pidfd = mock.Mock() + unix_events.can_use_pidfd.return_value = True + watcher = policy.get_child_watcher() + self.assertIsInstance(watcher, asyncio.PidfdChildWatcher) + + self.assertIs(policy._watcher, watcher) + + self.assertIs(watcher, policy.get_child_watcher()) + def test_get_child_watcher_after_set(self): policy = self.create_policy() watcher = asyncio.FastChildWatcher() diff --git a/Misc/NEWS.d/next/Library/2022-10-07-09-52-37.gh-issue-98023.aliEcl.rst b/Misc/NEWS.d/next/Library/2022-10-07-09-52-37.gh-issue-98023.aliEcl.rst new file mode 100644 index 00000000000000..1bfd68d4ac7ca0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-07-09-52-37.gh-issue-98023.aliEcl.rst @@ -0,0 +1 @@ +Change default child watcher to :class:`~asyncio.PidfdChildWatcher` on Linux systems which supports it. Patch by Kumar Aditya. From 7bba92f2a665ccc03307122f50dcd83d596af30a Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 7 Oct 2022 17:37:46 -0700 Subject: [PATCH 125/151] gh-91052: Add PyDict_Unwatch for unwatching a dictionary (#98055) --- Doc/c-api/dict.rst | 21 ++++++++++++- Doc/whatsnew/3.12.rst | 5 +++ Include/cpython/dictobject.h | 1 + Lib/test/test_capi.py | 60 ++++++++++++++++++++++++++---------- Modules/_testcapimodule.c | 15 +++++++++ Objects/dictobject.c | 41 +++++++++++++++++------- 6 files changed, 115 insertions(+), 28 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 7bebea0c97de5a..e5f28b59a701e0 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -246,17 +246,32 @@ Dictionary Objects of error (e.g. no more watcher IDs available), return ``-1`` and set an exception. + .. versionadded:: 3.12 + .. c:function:: int PyDict_ClearWatcher(int watcher_id) Clear watcher identified by *watcher_id* previously returned from :c:func:`PyDict_AddWatcher`. Return ``0`` on success, ``-1`` on error (e.g. if the given *watcher_id* was never registered.) + .. versionadded:: 3.12 + .. c:function:: int PyDict_Watch(int watcher_id, PyObject *dict) Mark dictionary *dict* as watched. The callback granted *watcher_id* by :c:func:`PyDict_AddWatcher` will be called when *dict* is modified or - deallocated. + deallocated. Return ``0`` on success or ``-1`` on error. + + .. versionadded:: 3.12 + +.. c:function:: int PyDict_Unwatch(int watcher_id, PyObject *dict) + + Mark dictionary *dict* as no longer watched. The callback granted + *watcher_id* by :c:func:`PyDict_AddWatcher` will no longer be called when + *dict* is modified or deallocated. The dict must previously have been + watched by this watcher. Return ``0`` on success or ``-1`` on error. + + .. versionadded:: 3.12 .. c:type:: PyDict_WatchEvent @@ -264,6 +279,8 @@ Dictionary Objects ``PyDict_EVENT_MODIFIED``, ``PyDict_EVENT_DELETED``, ``PyDict_EVENT_CLONED``, ``PyDict_EVENT_CLEARED``, or ``PyDict_EVENT_DEALLOCATED``. + .. versionadded:: 3.12 + .. c:type:: int (*PyDict_WatchCallback)(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value) Type of a dict watcher callback function. @@ -289,3 +306,5 @@ Dictionary Objects If the callback returns with an exception set, it must return ``-1``; this exception will be printed as an unraisable exception using :c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``. + + .. versionadded:: 3.12 diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 405de11e716b44..f873974b3e78fe 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -546,6 +546,11 @@ New Features which sets the vectorcall field of a given :c:type:`PyFunctionObject`. (Contributed by Andrew Frost in :gh:`92257`.) +* The C API now permits registering callbacks via :c:func:`PyDict_AddWatcher`, + :c:func:`PyDict_AddWatch` and related APIs to be called whenever a dictionary + is modified. This is intended for use by optimizing interpreters, JIT + compilers, or debuggers. + Porting to Python 3.12 ---------------------- diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index f8a74a597b0ea2..2dff59ef0b8a6b 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -106,3 +106,4 @@ PyAPI_FUNC(int) PyDict_ClearWatcher(int watcher_id); // Mark given dictionary as "watched" (callback will be called if it is modified) PyAPI_FUNC(int) PyDict_Watch(int watcher_id, PyObject* dict); +PyAPI_FUNC(int) PyDict_Unwatch(int watcher_id, PyObject* dict); diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 19367dfcc1ccbb..17425050ce00c0 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -20,6 +20,7 @@ import weakref from test import support from test.support import MISSING_C_DOCSTRINGS +from test.support import catch_unraisable_exception from test.support import import_helper from test.support import threading_helper from test.support import warnings_helper @@ -1421,6 +1422,9 @@ def assert_events(self, expected): def watch(self, wid, d): _testcapi.watch_dict(wid, d) + def unwatch(self, wid, d): + _testcapi.unwatch_dict(wid, d) + def test_set_new_item(self): d = {} with self.watcher() as wid: @@ -1477,27 +1481,24 @@ def test_dealloc(self): del d self.assert_events(["dealloc"]) + def test_unwatch(self): + d = {} + with self.watcher() as wid: + self.watch(wid, d) + d["foo"] = "bar" + self.unwatch(wid, d) + d["hmm"] = "baz" + self.assert_events(["new:foo:bar"]) + def test_error(self): d = {} - unraisables = [] - def unraisable_hook(unraisable): - unraisables.append(unraisable) with self.watcher(kind=self.ERROR) as wid: self.watch(wid, d) - orig_unraisable_hook = sys.unraisablehook - sys.unraisablehook = unraisable_hook - try: + with catch_unraisable_exception() as cm: d["foo"] = "bar" - finally: - sys.unraisablehook = orig_unraisable_hook + self.assertIs(cm.unraisable.object, d) + self.assertEqual(str(cm.unraisable.exc_value), "boom!") self.assert_events([]) - self.assertEqual(len(unraisables), 1) - unraisable = unraisables[0] - self.assertIs(unraisable.object, d) - self.assertEqual(str(unraisable.exc_value), "boom!") - # avoid leaking reference cycles - del unraisable - del unraisables def test_two_watchers(self): d1 = {} @@ -1522,11 +1523,38 @@ def test_watch_out_of_range_watcher_id(self): with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): self.watch(8, d) # DICT_MAX_WATCHERS = 8 - def test_unassigned_watcher_id(self): + def test_watch_unassigned_watcher_id(self): d = {} with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): self.watch(1, d) + def test_unwatch_non_dict(self): + with self.watcher() as wid: + with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): + self.unwatch(wid, 1) + + def test_unwatch_out_of_range_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): + self.unwatch(-1, d) + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): + self.unwatch(8, d) # DICT_MAX_WATCHERS = 8 + + def test_unwatch_unassigned_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): + self.unwatch(1, d) + + def test_clear_out_of_range_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): + self.clear_watcher(-1) + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): + self.clear_watcher(8) # DICT_MAX_WATCHERS = 8 + + def test_clear_unassigned_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): + self.clear_watcher(1) + if __name__ == "__main__": unittest.main() diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7dcd6bb7470ead..d54f706e5bd852 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5296,6 +5296,20 @@ watch_dict(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject * +unwatch_dict(PyObject *self, PyObject *args) +{ + PyObject *dict; + int watcher_id; + if (!PyArg_ParseTuple(args, "iO", &watcher_id, &dict)) { + return NULL; + } + if (PyDict_Unwatch(watcher_id, dict)) { + return NULL; + } + Py_RETURN_NONE; +} + static PyObject * get_dict_watcher_events(PyObject *self, PyObject *Py_UNUSED(args)) { @@ -5904,6 +5918,7 @@ static PyMethodDef TestMethods[] = { {"add_dict_watcher", add_dict_watcher, METH_O, NULL}, {"clear_dict_watcher", clear_dict_watcher, METH_O, NULL}, {"watch_dict", watch_dict, METH_VARARGS, NULL}, + {"unwatch_dict", unwatch_dict, METH_VARARGS, NULL}, {"get_dict_watcher_events", get_dict_watcher_events, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 6542b1803ffa2e..97007479b1be91 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5720,6 +5720,20 @@ uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictKeysObject *dictkeys) return v; } +static inline int +validate_watcher_id(PyInterpreterState *interp, int watcher_id) +{ + if (watcher_id < 0 || watcher_id >= DICT_MAX_WATCHERS) { + PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); + return -1; + } + if (!interp->dict_watchers[watcher_id]) { + PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); + return -1; + } + return 0; +} + int PyDict_Watch(int watcher_id, PyObject* dict) { @@ -5727,16 +5741,26 @@ PyDict_Watch(int watcher_id, PyObject* dict) PyErr_SetString(PyExc_ValueError, "Cannot watch non-dictionary"); return -1; } - if (watcher_id < 0 || watcher_id >= DICT_MAX_WATCHERS) { - PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (validate_watcher_id(interp, watcher_id)) { + return -1; + } + ((PyDictObject*)dict)->ma_version_tag |= (1LL << watcher_id); + return 0; +} + +int +PyDict_Unwatch(int watcher_id, PyObject* dict) +{ + if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_ValueError, "Cannot watch non-dictionary"); return -1; } PyInterpreterState *interp = _PyInterpreterState_GET(); - if (!interp->dict_watchers[watcher_id]) { - PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); + if (validate_watcher_id(interp, watcher_id)) { return -1; } - ((PyDictObject*)dict)->ma_version_tag |= (1LL << watcher_id); + ((PyDictObject*)dict)->ma_version_tag &= ~(1LL << watcher_id); return 0; } @@ -5759,13 +5783,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback) int PyDict_ClearWatcher(int watcher_id) { - if (watcher_id < 0 || watcher_id >= DICT_MAX_WATCHERS) { - PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); - return -1; - } PyInterpreterState *interp = _PyInterpreterState_GET(); - if (!interp->dict_watchers[watcher_id]) { - PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); + if (validate_watcher_id(interp, watcher_id)) { return -1; } interp->dict_watchers[watcher_id] = NULL; From e20a1907c24461a9f2770b9e1b99f25e137a86e2 Mon Sep 17 00:00:00 2001 From: JasonYZ Date: Sat, 8 Oct 2022 05:46:23 +0100 Subject: [PATCH 126/151] gh-97822: Fix http.server documentation reference to test() function (#98027) Co-authored-by: Jelle Zijlstra --- Doc/library/http.server.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst index 48f952daae12f5..81b6bf5373b495 100644 --- a/Doc/library/http.server.rst +++ b/Doc/library/http.server.rst @@ -392,8 +392,8 @@ provides three different variants: contents of the file are output. If the file's MIME type starts with ``text/`` the file is opened in text mode; otherwise binary mode is used. - For example usage, see the implementation of the :func:`test` function - invocation in the :mod:`http.server` module. + For example usage, see the implementation of the ``test`` function + in :source:`Lib/http/server.py`. .. versionchanged:: 3.7 Support of the ``'If-Modified-Since'`` header. From 98b3acaa0ab996848f48ba03c679583594cc3deb Mon Sep 17 00:00:00 2001 From: partev Date: Sat, 8 Oct 2022 01:55:35 -0400 Subject: [PATCH 127/151] [doc] Fix broken links to C extensions accelerating stdlib modules (#96914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Łukasz Langa Co-authored-by: C.A.M. Gerlach --- Doc/license.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Doc/license.rst b/Doc/license.rst index 00691b30ba6d3e..54643744dcf9c1 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -302,7 +302,8 @@ for third-party software incorporated in the Python distribution. Mersenne Twister ---------------- -The :mod:`_random` module includes code based on a download from +The :mod:`!_random` C extension underlying the :mod:`random` module +includes code based on a download from http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/emt19937ar.html. The following are the verbatim comments from the original code:: @@ -819,7 +820,8 @@ sources unless the build is configured ``--with-system-expat``:: libffi ------ -The :mod:`_ctypes` extension is built using an included copy of the libffi +The :mod:`!_ctypes` C extension underlying the :mod:`ctypes` module +is built using an included copy of the libffi sources unless the build is configured ``--with-system-libffi``:: Copyright (c) 1996-2008 Red Hat, Inc and others. @@ -920,7 +922,8 @@ on the cfuhash project:: libmpdec -------- -The :mod:`_decimal` module is built using an included copy of the libmpdec +The :mod:`!_decimal` C extension underlying the :mod:`decimal` module +is built using an included copy of the libmpdec library unless the build is configured ``--with-system-libmpdec``:: Copyright (c) 2008-2020 Stefan Krah. All rights reserved. From 637bf6a0d320f8b2548761235faf8222555028ac Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 7 Oct 2022 23:54:16 -0700 Subject: [PATCH 128/151] gh-97913 Docs: Add walrus operator to the index (#97921) * Add walrus operator to the index * Add named expression to the index Co-authored-by: Mariatta Wijaya * Fix indentation and add missing newline Co-authored-by: Ezio Melotti Co-authored-by: Mariatta Wijaya Co-authored-by: Ezio Melotti --- Doc/reference/expressions.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 11f49a8c33dc88..28c17566009fbd 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1741,6 +1741,12 @@ returns a boolean value regardless of the type of its argument (for example, ``not 'foo'`` produces ``False`` rather than ``''``.) +.. index:: + single: := (colon equals) + single: assignment expression + single: walrus operator + single: named expression + Assignment expressions ====================== From d2abe90da388d8863dfd219e9d87f1055c9b52df Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 8 Oct 2022 09:11:38 +0200 Subject: [PATCH 129/151] Add `@ezio-melotti` as codeowner for `.github/`. (#98079) --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2568560c074f64..585589d6ce3bf7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,9 @@ # It uses the same pattern rule for gitignore file # https://git-scm.com/docs/gitignore#_pattern_format +# GitHub +.github/** @ezio-melotti + # asyncio **/*asyncio* @1st1 @asvetlov @gvanrossum From b31a5ee104059ac616958e821466ce89d03d58f8 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 8 Oct 2022 11:13:34 +0300 Subject: [PATCH 130/151] GitHub Workflows security hardening (#96492) * Update project-updater.yml Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com> * Update project-updater.yml repository-projects: write is not needed because a separate secrets.ADD_TO_PROJECT_PAT is used Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com> --- .github/workflows/project-updater.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/project-updater.yml b/.github/workflows/project-updater.yml index 77e55ed019b25a..99c7a05ae8cab0 100644 --- a/.github/workflows/project-updater.yml +++ b/.github/workflows/project-updater.yml @@ -6,6 +6,9 @@ on: - opened - labeled +permissions: + contents: read + jobs: add-to-project: name: Add issues to projects From 65d36baf209ef1a52ea435af16deb1b7d94d0c28 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Sat, 8 Oct 2022 07:57:09 -0700 Subject: [PATCH 131/151] gh-97922: Run the GC only on eval breaker (#97920) --- Doc/whatsnew/3.12.rst | 7 +++++ Include/internal/pycore_gc.h | 2 ++ Include/internal/pycore_interp.h | 2 ++ Lib/test/test_frame.py | 2 +- ...2-10-05-11-37-15.gh-issue-97922.Zu9Bge.rst | 5 ++++ Modules/gcmodule.c | 27 +++++++++++++++-- Modules/signalmodule.c | 13 ++++++++ Python/ceval_gil.c | 30 ++++++++++++------- 8 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-37-15.gh-issue-97922.Zu9Bge.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index f873974b3e78fe..341e85103a3cf7 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -93,6 +93,13 @@ Other Language Changes when parsing source code containing null bytes. (Contributed by Pablo Galindo in :gh:`96670`.) +* The Garbage Collector now runs only on the eval breaker mechanism of the + Python bytecode evaluation loop instead on object allocations. The GC can + also run when :c:func:`PyErr_CheckSignals` is called so C extensions that + need to run for a long time without executing any Python code also have a + chance to execute the GC periodically. (Contributed by Pablo Galindo in + :gh:`97922`.) + New Modules =========== diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index bfab0adfffc9ff..b3abe2030a03da 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -202,6 +202,8 @@ extern void _PyList_ClearFreeList(PyInterpreterState *interp); extern void _PyDict_ClearFreeList(PyInterpreterState *interp); extern void _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp); extern void _PyContext_ClearFreeList(PyInterpreterState *interp); +extern void _Py_ScheduleGC(PyInterpreterState *interp); +extern void _Py_RunGC(PyThreadState *tstate); #ifdef __cplusplus } diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 812101c532c7f6..f58cee899977c5 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -49,6 +49,8 @@ struct _ceval_state { _Py_atomic_int eval_breaker; /* Request for dropping the GIL */ _Py_atomic_int gil_drop_request; + /* The GC is ready to be executed */ + _Py_atomic_int gc_scheduled; struct _pending_calls pending; }; diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 4b86a60d2f4c36..4b5bb7f94ac469 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -277,7 +277,7 @@ def callback(phase, info): frame! """ nonlocal sneaky_frame_object - sneaky_frame_object = sys._getframe().f_back + sneaky_frame_object = sys._getframe().f_back.f_back # We're done here: gc.callbacks.remove(callback) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-37-15.gh-issue-97922.Zu9Bge.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-37-15.gh-issue-97922.Zu9Bge.rst new file mode 100644 index 00000000000000..bf78709362f464 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-05-11-37-15.gh-issue-97922.Zu9Bge.rst @@ -0,0 +1,5 @@ +The Garbage Collector now runs only on the eval breaker mechanism of the +Python bytecode evaluation loop instead on object allocations. The GC can +also run when :c:func:`PyErr_CheckSignals` is called so C extensions that +need to run for a long time without executing any Python code also have a +chance to execute the GC periodically. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 97cb6e6e1efb1f..75832e9dd3da63 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2252,6 +2252,20 @@ PyObject_IS_GC(PyObject *obj) return _PyObject_IS_GC(obj); } +void +_Py_ScheduleGC(PyInterpreterState *interp) +{ + GCState *gcstate = &interp->gc; + if (gcstate->collecting == 1) { + return; + } + struct _ceval_state *ceval = &interp->ceval; + if (!_Py_atomic_load_relaxed(&ceval->gc_scheduled)) { + _Py_atomic_store_relaxed(&ceval->gc_scheduled, 1); + _Py_atomic_store_relaxed(&ceval->eval_breaker, 1); + } +} + void _PyObject_GC_Link(PyObject *op) { @@ -2269,12 +2283,19 @@ _PyObject_GC_Link(PyObject *op) !gcstate->collecting && !_PyErr_Occurred(tstate)) { - gcstate->collecting = 1; - gc_collect_generations(tstate); - gcstate->collecting = 0; + _Py_ScheduleGC(tstate->interp); } } +void +_Py_RunGC(PyThreadState *tstate) +{ + GCState *gcstate = &tstate->interp->gc; + gcstate->collecting = 1; + gc_collect_generations(tstate); + gcstate->collecting = 0; +} + static PyObject * gc_alloc(size_t basicsize, size_t presize) { diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 0f30b4da036313..b85d6d19e8cd05 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1798,6 +1798,19 @@ int PyErr_CheckSignals(void) { PyThreadState *tstate = _PyThreadState_GET(); + + /* Opportunistically check if the GC is scheduled to run and run it + if we have a request. This is done here because native code needs + to call this API if is going to run for some time without executing + Python code to ensure signals are handled. Checking for the GC here + allows long running native code to clean cycles created using the C-API + even if it doesn't run the evaluation loop */ + struct _ceval_state *interp_ceval_state = &tstate->interp->ceval; + if (_Py_atomic_load_relaxed(&interp_ceval_state->gc_scheduled)) { + _Py_atomic_store_relaxed(&interp_ceval_state->gc_scheduled, 0); + _Py_RunGC(tstate); + } + if (!_Py_ThreadCanHandleSignals(tstate->interp)) { return 0; } diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index fd737b5738e889..9b9d7dc1d1af1e 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -5,6 +5,7 @@ #include "pycore_pyerrors.h" // _PyErr_Fetch() #include "pycore_pylifecycle.h" // _PyErr_Print() #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_interp.h" // _Py_RunGC() #include "pycore_pymem.h" // _PyMem_IsPtrFreed() /* @@ -69,7 +70,8 @@ COMPUTE_EVAL_BREAKER(PyInterpreterState *interp, && _Py_ThreadCanHandleSignals(interp)) | (_Py_atomic_load_relaxed_int32(&ceval2->pending.calls_to_do) && _Py_ThreadCanHandlePendingCalls()) - | ceval2->pending.async_exc); + | ceval2->pending.async_exc + | _Py_atomic_load_relaxed_int32(&ceval2->gc_scheduled)); } @@ -938,6 +940,7 @@ _Py_HandlePending(PyThreadState *tstate) { _PyRuntimeState * const runtime = &_PyRuntime; struct _ceval_runtime_state *ceval = &runtime->ceval; + struct _ceval_state *interp_ceval_state = &tstate->interp->ceval; /* Pending signals */ if (_Py_atomic_load_relaxed_int32(&ceval->signals_pending)) { @@ -947,20 +950,26 @@ _Py_HandlePending(PyThreadState *tstate) } /* Pending calls */ - struct _ceval_state *ceval2 = &tstate->interp->ceval; - if (_Py_atomic_load_relaxed_int32(&ceval2->pending.calls_to_do)) { + if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->pending.calls_to_do)) { if (make_pending_calls(tstate->interp) != 0) { return -1; } } + /* GC scheduled to run */ + if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->gc_scheduled)) { + _Py_atomic_store_relaxed(&interp_ceval_state->gc_scheduled, 0); + COMPUTE_EVAL_BREAKER(tstate->interp, ceval, interp_ceval_state); + _Py_RunGC(tstate); + } + /* GIL drop request */ - if (_Py_atomic_load_relaxed_int32(&ceval2->gil_drop_request)) { + if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->gil_drop_request)) { /* Give another thread a chance */ if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) { Py_FatalError("tstate mix-up"); } - drop_gil(ceval, ceval2, tstate); + drop_gil(ceval, interp_ceval_state, tstate); /* Other threads may run now */ @@ -981,16 +990,17 @@ _Py_HandlePending(PyThreadState *tstate) return -1; } -#ifdef MS_WINDOWS - // bpo-42296: On Windows, _PyEval_SignalReceived() can be called in a - // different thread than the Python thread, in which case + + // It is possible that some of the conditions that trigger the eval breaker + // are called in a different thread than the Python thread. An example of + // this is bpo-42296: On Windows, _PyEval_SignalReceived() can be called in + // a different thread than the Python thread, in which case // _Py_ThreadCanHandleSignals() is wrong. Recompute eval_breaker in the // current Python thread with the correct _Py_ThreadCanHandleSignals() // value. It prevents to interrupt the eval loop at every instruction if // the current Python thread cannot handle signals (if // _Py_ThreadCanHandleSignals() is false). - COMPUTE_EVAL_BREAKER(tstate->interp, ceval, ceval2); -#endif + COMPUTE_EVAL_BREAKER(tstate->interp, ceval, interp_ceval_state); return 0; } From 423a6277923a9ac0d650e4bd9978c69f848121c4 Mon Sep 17 00:00:00 2001 From: Joannah Nanjekye <33177550+nanjekyejoannah@users.noreply.github.com> Date: Sat, 8 Oct 2022 07:57:47 -0700 Subject: [PATCH 132/151] gh-68686: Retire eptag ptag scripts (#98064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Retire eptag ptag scripts * 📜🤖 Added by blurb_it. * fix news entry error Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- ...2-10-07-22-06-11.gh-issue-68686.6KNIQ4.rst | 1 + Tools/scripts/README | 1 - Tools/scripts/eptags.py | 57 ------------------- Tools/scripts/ptags.py | 54 ------------------ 4 files changed, 1 insertion(+), 112 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2022-10-07-22-06-11.gh-issue-68686.6KNIQ4.rst delete mode 100755 Tools/scripts/eptags.py delete mode 100755 Tools/scripts/ptags.py diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-10-07-22-06-11.gh-issue-68686.6KNIQ4.rst b/Misc/NEWS.d/next/Tools-Demos/2022-10-07-22-06-11.gh-issue-68686.6KNIQ4.rst new file mode 100644 index 00000000000000..a4289d675703b3 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2022-10-07-22-06-11.gh-issue-68686.6KNIQ4.rst @@ -0,0 +1 @@ +Remove ptags and eptags scripts. diff --git a/Tools/scripts/README b/Tools/scripts/README index b53b0f21d7c293..70ea5f4cd0feae 100644 --- a/Tools/scripts/README +++ b/Tools/scripts/README @@ -5,7 +5,6 @@ useful while building, extending or managing Python. abitype.py Converts a C file to use the PEP 384 type definition API combinerefs.py A helper for analyzing PYTHONDUMPREFS output diff.py Print file diffs in context, unified, or ndiff formats -eptags.py Create Emacs TAGS file for Python modules gprof2html.py Transform gprof(1) output into useful HTML idle3 Main program to start IDLE md5sum.py Print MD5 checksums of argument files diff --git a/Tools/scripts/eptags.py b/Tools/scripts/eptags.py deleted file mode 100755 index 7f8059ba71adf3..00000000000000 --- a/Tools/scripts/eptags.py +++ /dev/null @@ -1,57 +0,0 @@ -#! /usr/bin/env python3 -"""Create a TAGS file for Python programs, usable with GNU Emacs. - -usage: eptags pyfiles... - -The output TAGS file is usable with Emacs version 18, 19, 20. -Tagged are: - - functions (even inside other defs or classes) - - classes - -eptags warns about files it cannot open. -eptags will not give warnings about duplicate tags. - -BUGS: - Because of tag duplication (methods with the same name in different - classes), TAGS files are not very useful for most object-oriented - python projects. -""" -import sys,re - -expr = r'^[ \t]*(def|class)[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*[:\(]' -matcher = re.compile(expr) - -def treat_file(filename, outfp): - """Append tags found in file named 'filename' to the open file 'outfp'""" - try: - fp = open(filename, 'r') - except OSError: - sys.stderr.write('Cannot open %s\n'%filename) - return - with fp: - charno = 0 - lineno = 0 - tags = [] - size = 0 - while 1: - line = fp.readline() - if not line: - break - lineno = lineno + 1 - m = matcher.search(line) - if m: - tag = m.group(0) + '\177%d,%d\n' % (lineno, charno) - tags.append(tag) - size = size + len(tag) - charno = charno + len(line) - outfp.write('\f\n%s,%d\n' % (filename,size)) - for tag in tags: - outfp.write(tag) - -def main(): - with open('TAGS', 'w') as outfp: - for filename in sys.argv[1:]: - treat_file(filename, outfp) - -if __name__=="__main__": - main() diff --git a/Tools/scripts/ptags.py b/Tools/scripts/ptags.py deleted file mode 100755 index eedd411702c199..00000000000000 --- a/Tools/scripts/ptags.py +++ /dev/null @@ -1,54 +0,0 @@ -#! /usr/bin/env python3 - -# ptags -# -# Create a tags file for Python programs, usable with vi. -# Tagged are: -# - functions (even inside other defs or classes) -# - classes -# - filenames -# Warns about files it cannot open. -# No warnings about duplicate tags. - -import sys, re, os - -tags = [] # Modified global variable! - -def main(): - args = sys.argv[1:] - for filename in args: - treat_file(filename) - if tags: - with open('tags', 'w') as fp: - tags.sort() - for s in tags: fp.write(s) - - -expr = r'^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*[:\(]' -matcher = re.compile(expr) - -def treat_file(filename): - try: - fp = open(filename, 'r') - except: - sys.stderr.write('Cannot open %s\n' % filename) - return - with fp: - base = os.path.basename(filename) - if base[-3:] == '.py': - base = base[:-3] - s = base + '\t' + filename + '\t' + '1\n' - tags.append(s) - while 1: - line = fp.readline() - if not line: - break - m = matcher.match(line) - if m: - content = m.group(0) - name = m.group(2) - s = name + '\t' + filename + '\t/^' + content + '/\n' - tags.append(s) - -if __name__ == '__main__': - main() From 1e204b11eb2564cfe93c7ff3d4d0215fd31489af Mon Sep 17 00:00:00 2001 From: Noam Cohen Date: Sat, 8 Oct 2022 21:31:57 +0300 Subject: [PATCH 133/151] gh-95011: Migrate syslog module to Argument Clinic (GH-95012) --- Include/internal/pycore_global_strings.h | 3 + .../internal/pycore_runtime_init_generated.h | 21 ++ Modules/clinic/syslogmodule.c.h | 257 ++++++++++++++++++ Modules/syslogmodule.c | 168 +++++++----- 4 files changed, 377 insertions(+), 72 deletions(-) create mode 100644 Modules/clinic/syslogmodule.c.h diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 2966b60e0cd830..f646979910c887 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -350,6 +350,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(exception) STRUCT_FOR_ID(exp) STRUCT_FOR_ID(extend) + STRUCT_FOR_ID(facility) STRUCT_FOR_ID(factory) STRUCT_FOR_ID(family) STRUCT_FOR_ID(fanout) @@ -392,6 +393,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(hi) STRUCT_FOR_ID(hook) STRUCT_FOR_ID(id) + STRUCT_FOR_ID(ident) STRUCT_FOR_ID(ignore) STRUCT_FOR_ID(imag) STRUCT_FOR_ID(importlib) @@ -447,6 +449,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(lo) STRUCT_FOR_ID(locale) STRUCT_FOR_ID(locals) + STRUCT_FOR_ID(logoption) STRUCT_FOR_ID(loop) STRUCT_FOR_ID(mapping) STRUCT_FOR_ID(match) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 617582f96e33f5..bd1fedebd65cf5 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -859,6 +859,7 @@ extern "C" { INIT_ID(exception), \ INIT_ID(exp), \ INIT_ID(extend), \ + INIT_ID(facility), \ INIT_ID(factory), \ INIT_ID(family), \ INIT_ID(fanout), \ @@ -901,6 +902,7 @@ extern "C" { INIT_ID(hi), \ INIT_ID(hook), \ INIT_ID(id), \ + INIT_ID(ident), \ INIT_ID(ignore), \ INIT_ID(imag), \ INIT_ID(importlib), \ @@ -956,6 +958,7 @@ extern "C" { INIT_ID(lo), \ INIT_ID(locale), \ INIT_ID(locals), \ + INIT_ID(logoption), \ INIT_ID(loop), \ INIT_ID(mapping), \ INIT_ID(match), \ @@ -2026,6 +2029,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(extend); PyUnicode_InternInPlace(&string); + string = &_Py_ID(facility); + PyUnicode_InternInPlace(&string); string = &_Py_ID(factory); PyUnicode_InternInPlace(&string); string = &_Py_ID(family); @@ -2110,6 +2115,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(id); PyUnicode_InternInPlace(&string); + string = &_Py_ID(ident); + PyUnicode_InternInPlace(&string); string = &_Py_ID(ignore); PyUnicode_InternInPlace(&string); string = &_Py_ID(imag); @@ -2220,6 +2227,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(locals); PyUnicode_InternInPlace(&string); + string = &_Py_ID(logoption); + PyUnicode_InternInPlace(&string); string = &_Py_ID(loop); PyUnicode_InternInPlace(&string); string = &_Py_ID(mapping); @@ -5981,6 +5990,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(extend)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(facility)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(facility)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(factory)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(factory)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); @@ -6149,6 +6162,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(id)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(ident)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(ident)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(ignore)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(ignore)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); @@ -6369,6 +6386,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(locals)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(logoption)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(logoption)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(loop)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(loop)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); diff --git a/Modules/clinic/syslogmodule.c.h b/Modules/clinic/syslogmodule.c.h new file mode 100644 index 00000000000000..0ce66ad4e1a490 --- /dev/null +++ b/Modules/clinic/syslogmodule.c.h @@ -0,0 +1,257 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif + + +PyDoc_STRVAR(syslog_openlog__doc__, +"openlog($module, /, ident=, logoption=0,\n" +" facility=LOG_USER)\n" +"--\n" +"\n" +"Set logging options of subsequent syslog() calls."); + +#define SYSLOG_OPENLOG_METHODDEF \ + {"openlog", _PyCFunction_CAST(syslog_openlog), METH_FASTCALL|METH_KEYWORDS, syslog_openlog__doc__}, + +static PyObject * +syslog_openlog_impl(PyObject *module, PyObject *ident, long logopt, + long facility); + +static PyObject * +syslog_openlog(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(ident), &_Py_ID(logoption), &_Py_ID(facility), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"ident", "logoption", "facility", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "openlog", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *ident = NULL; + long logopt = 0; + long facility = LOG_USER; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 3, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("openlog", "argument 'ident'", "str", args[0]); + goto exit; + } + if (PyUnicode_READY(args[0]) == -1) { + goto exit; + } + ident = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + logopt = PyLong_AsLong(args[1]); + if (logopt == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + facility = PyLong_AsLong(args[2]); + if (facility == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = syslog_openlog_impl(module, ident, logopt, facility); + +exit: + return return_value; +} + +PyDoc_STRVAR(syslog_syslog__doc__, +"syslog([priority=LOG_INFO,] message)\n" +"Send the string message to the system logger."); + +#define SYSLOG_SYSLOG_METHODDEF \ + {"syslog", (PyCFunction)syslog_syslog, METH_VARARGS, syslog_syslog__doc__}, + +static PyObject * +syslog_syslog_impl(PyObject *module, int group_left_1, int priority, + const char *message); + +static PyObject * +syslog_syslog(PyObject *module, PyObject *args) +{ + PyObject *return_value = NULL; + int group_left_1 = 0; + int priority = LOG_INFO; + const char *message; + + switch (PyTuple_GET_SIZE(args)) { + case 1: + if (!PyArg_ParseTuple(args, "s:syslog", &message)) { + goto exit; + } + break; + case 2: + if (!PyArg_ParseTuple(args, "is:syslog", &priority, &message)) { + goto exit; + } + group_left_1 = 1; + break; + default: + PyErr_SetString(PyExc_TypeError, "syslog.syslog requires 1 to 2 arguments"); + goto exit; + } + return_value = syslog_syslog_impl(module, group_left_1, priority, message); + +exit: + return return_value; +} + +PyDoc_STRVAR(syslog_closelog__doc__, +"closelog($module, /)\n" +"--\n" +"\n" +"Reset the syslog module values and call the system library closelog()."); + +#define SYSLOG_CLOSELOG_METHODDEF \ + {"closelog", (PyCFunction)syslog_closelog, METH_NOARGS, syslog_closelog__doc__}, + +static PyObject * +syslog_closelog_impl(PyObject *module); + +static PyObject * +syslog_closelog(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return syslog_closelog_impl(module); +} + +PyDoc_STRVAR(syslog_setlogmask__doc__, +"setlogmask($module, maskpri, /)\n" +"--\n" +"\n" +"Set the priority mask to maskpri and return the previous mask value."); + +#define SYSLOG_SETLOGMASK_METHODDEF \ + {"setlogmask", (PyCFunction)syslog_setlogmask, METH_O, syslog_setlogmask__doc__}, + +static long +syslog_setlogmask_impl(PyObject *module, long maskpri); + +static PyObject * +syslog_setlogmask(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + long maskpri; + long _return_value; + + maskpri = PyLong_AsLong(arg); + if (maskpri == -1 && PyErr_Occurred()) { + goto exit; + } + _return_value = syslog_setlogmask_impl(module, maskpri); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(syslog_LOG_MASK__doc__, +"LOG_MASK($module, pri, /)\n" +"--\n" +"\n" +"Calculates the mask for the individual priority pri."); + +#define SYSLOG_LOG_MASK_METHODDEF \ + {"LOG_MASK", (PyCFunction)syslog_LOG_MASK, METH_O, syslog_LOG_MASK__doc__}, + +static long +syslog_LOG_MASK_impl(PyObject *module, long pri); + +static PyObject * +syslog_LOG_MASK(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + long pri; + long _return_value; + + pri = PyLong_AsLong(arg); + if (pri == -1 && PyErr_Occurred()) { + goto exit; + } + _return_value = syslog_LOG_MASK_impl(module, pri); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(syslog_LOG_UPTO__doc__, +"LOG_UPTO($module, pri, /)\n" +"--\n" +"\n" +"Calculates the mask for all priorities up to and including pri."); + +#define SYSLOG_LOG_UPTO_METHODDEF \ + {"LOG_UPTO", (PyCFunction)syslog_LOG_UPTO, METH_O, syslog_LOG_UPTO__doc__}, + +static long +syslog_LOG_UPTO_impl(PyObject *module, long pri); + +static PyObject * +syslog_LOG_UPTO(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + long pri; + long _return_value; + + pri = PyLong_AsLong(arg); + if (pri == -1 && PyErr_Occurred()) { + goto exit; + } + _return_value = syslog_LOG_UPTO_impl(module, pri); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong(_return_value); + +exit: + return return_value; +} +/*[clinic end generated code: output=3b1bdb16565b8fda input=a9049054013a1b77]*/ diff --git a/Modules/syslogmodule.c b/Modules/syslogmodule.c index 1593eea94a62bf..b6296ed0a69aa5 100644 --- a/Modules/syslogmodule.c +++ b/Modules/syslogmodule.c @@ -54,6 +54,13 @@ Revision history: #include +/*[clinic input] +module syslog +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=478f4ac94a1d4cae]*/ + +#include "clinic/syslogmodule.c.h" + /* only one instance, only one syslog, so globals should be ok */ static PyObject *S_ident_o = NULL; /* identifier, held by openlog() */ static char S_log_open = 0; @@ -113,19 +120,23 @@ syslog_get_argv(void) } +/*[clinic input] +syslog.openlog + + ident: unicode = NULL + logoption as logopt: long = 0 + facility: long(c_default="LOG_USER") = LOG_USER + +Set logging options of subsequent syslog() calls. +[clinic start generated code]*/ + static PyObject * -syslog_openlog(PyObject * self, PyObject * args, PyObject *kwds) +syslog_openlog_impl(PyObject *module, PyObject *ident, long logopt, + long facility) +/*[clinic end generated code: output=5476c12829b6eb75 input=8a987a96a586eee7]*/ { - long logopt = 0; - long facility = LOG_USER; - PyObject *ident = NULL; - static char *keywords[] = {"ident", "logoption", "facility", 0}; const char *ident_str = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|Ull:openlog", keywords, &ident, &logopt, &facility)) - return NULL; - if (ident) { Py_INCREF(ident); } @@ -158,48 +169,37 @@ syslog_openlog(PyObject * self, PyObject * args, PyObject *kwds) } -static PyObject * -syslog_syslog(PyObject * self, PyObject * args) -{ - PyObject *message_object; - const char *message; - int priority = LOG_INFO; - if (!PyArg_ParseTuple(args, "iU;[priority,] message string", - &priority, &message_object)) { - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "U;[priority,] message string", - &message_object)) - return NULL; - } +/*[clinic input] +syslog.syslog - message = PyUnicode_AsUTF8(message_object); - if (message == NULL) - return NULL; + [ + priority: int(c_default="LOG_INFO") = LOG_INFO + ] + + message: str + + / +Send the string message to the system logger. +[clinic start generated code]*/ + +static PyObject * +syslog_syslog_impl(PyObject *module, int group_left_1, int priority, + const char *message) +/*[clinic end generated code: output=c3dbc73445a0e078 input=ac83d92b12ea3d4e]*/ +{ if (PySys_Audit("syslog.syslog", "is", priority, message) < 0) { return NULL; } /* if log is not opened, open it now */ if (!S_log_open) { - PyObject *openargs; - - /* Continue even if PyTuple_New fails, because openlog(3) is optional. - * So, we can still do logging in the unlikely event things are so hosed - * that we can't do this tuple. - */ - if ((openargs = PyTuple_New(0))) { - PyObject *openlog_ret = syslog_openlog(self, openargs, NULL); - Py_DECREF(openargs); - if (openlog_ret == NULL) { - return NULL; - } - Py_DECREF(openlog_ret); - } - else { + PyObject *openlog_ret = syslog_openlog_impl(module, NULL, 0, LOG_USER); + if (openlog_ret == NULL) { return NULL; } + Py_DECREF(openlog_ret); } /* Incref ident, because it can be decrefed if syslog.openlog() is @@ -214,8 +214,16 @@ syslog_syslog(PyObject * self, PyObject * args) Py_RETURN_NONE; } + +/*[clinic input] +syslog.closelog + +Reset the syslog module values and call the system library closelog(). +[clinic start generated code]*/ + static PyObject * -syslog_closelog(PyObject *self, PyObject *unused) +syslog_closelog_impl(PyObject *module) +/*[clinic end generated code: output=97890a80a24b1b84 input=fb77a54d447acf07]*/ { if (PySys_Audit("syslog.closelog", NULL) < 0) { return NULL; @@ -228,51 +236,67 @@ syslog_closelog(PyObject *self, PyObject *unused) Py_RETURN_NONE; } -static PyObject * -syslog_setlogmask(PyObject *self, PyObject *args) -{ - long maskpri, omaskpri; +/*[clinic input] +syslog.setlogmask -> long - if (!PyArg_ParseTuple(args, "l;mask for priority", &maskpri)) - return NULL; + maskpri: long + / + +Set the priority mask to maskpri and return the previous mask value. +[clinic start generated code]*/ + +static long +syslog_setlogmask_impl(PyObject *module, long maskpri) +/*[clinic end generated code: output=d6ed163917b434bf input=adff2c2b76c7629c]*/ +{ if (PySys_Audit("syslog.setlogmask", "l", maskpri) < 0) { - return NULL; + return -1; } - omaskpri = setlogmask(maskpri); - return PyLong_FromLong(omaskpri); + + return setlogmask(maskpri); } -static PyObject * -syslog_log_mask(PyObject *self, PyObject *args) +/*[clinic input] +syslog.LOG_MASK -> long + + pri: long + / + +Calculates the mask for the individual priority pri. +[clinic start generated code]*/ + +static long +syslog_LOG_MASK_impl(PyObject *module, long pri) +/*[clinic end generated code: output=c4a5bbfcc74c7c94 input=534829cb7fb5f7d2]*/ { - long mask; - long pri; - if (!PyArg_ParseTuple(args, "l:LOG_MASK", &pri)) - return NULL; - mask = LOG_MASK(pri); - return PyLong_FromLong(mask); + return LOG_MASK(pri); } -static PyObject * -syslog_log_upto(PyObject *self, PyObject *args) +/*[clinic input] +syslog.LOG_UPTO -> long + + pri: long + / + +Calculates the mask for all priorities up to and including pri. +[clinic start generated code]*/ + +static long +syslog_LOG_UPTO_impl(PyObject *module, long pri) +/*[clinic end generated code: output=9eab083c90601d7e input=5e906d6c406b7458]*/ { - long mask; - long pri; - if (!PyArg_ParseTuple(args, "l:LOG_UPTO", &pri)) - return NULL; - mask = LOG_UPTO(pri); - return PyLong_FromLong(mask); + return LOG_UPTO(pri); } /* List of functions defined in the module */ static PyMethodDef syslog_methods[] = { - {"openlog", _PyCFunction_CAST(syslog_openlog), METH_VARARGS | METH_KEYWORDS}, - {"closelog", syslog_closelog, METH_NOARGS}, - {"syslog", syslog_syslog, METH_VARARGS}, - {"setlogmask", syslog_setlogmask, METH_VARARGS}, - {"LOG_MASK", syslog_log_mask, METH_VARARGS}, - {"LOG_UPTO", syslog_log_upto, METH_VARARGS}, + SYSLOG_OPENLOG_METHODDEF + SYSLOG_CLOSELOG_METHODDEF + SYSLOG_SYSLOG_METHODDEF + SYSLOG_SETLOGMASK_METHODDEF + SYSLOG_LOG_MASK_METHODDEF + SYSLOG_LOG_UPTO_METHODDEF {NULL, NULL, 0} }; From 5723b5354a5e55dc6ba3a98ed1ed97bf9a41fa83 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 8 Oct 2022 21:21:38 +0200 Subject: [PATCH 134/151] Auto-cancel old builds when new commit pushed to branch (#98009) * Auto-cancel old builds when new commit pushed to branch * Add a fallback Co-authored-by: Ezio Melotti * Use the same group for all workflows. Co-authored-by: Ezio Melotti --- .github/workflows/build.yml | 4 ++++ .github/workflows/build_msi.yml | 4 ++++ .github/workflows/doc.yml | 4 ++++ .github/workflows/verify-ensurepip-wheels.yml | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc91c0dbcb027b..8f5676eec08e77 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: check_source: name: 'Check for source changes' diff --git a/.github/workflows/build_msi.yml b/.github/workflows/build_msi.yml index 528679c0ac6b37..5f1dcae190efbc 100644 --- a/.github/workflows/build_msi.yml +++ b/.github/workflows/build_msi.yml @@ -18,6 +18,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: build: name: Windows Installer diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index d95d089ed66755..10e4cf074a590a 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -28,6 +28,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: build_doc: name: 'Docs' diff --git a/.github/workflows/verify-ensurepip-wheels.yml b/.github/workflows/verify-ensurepip-wheels.yml index 61e3d1adf534d5..9f4754f912b09f 100644 --- a/.github/workflows/verify-ensurepip-wheels.yml +++ b/.github/workflows/verify-ensurepip-wheels.yml @@ -16,6 +16,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: verify: runs-on: ubuntu-latest From 8e3d19b92064b3f6216cb425bfcfa7463fbcbef6 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sun, 9 Oct 2022 02:22:19 +0530 Subject: [PATCH 135/151] GH-94597: deprecate `SafeChildWatcher`, `FastChildWatcher` and `MultiLoopChildWatcher` child watchers (#98089) --- Doc/whatsnew/3.12.rst | 18 +++++++ Lib/asyncio/unix_events.py | 15 ++++++ Lib/test/test_asyncio/test_events.py | 10 ++-- Lib/test/test_asyncio/test_streams.py | 6 ++- Lib/test/test_asyncio/test_subprocess.py | 48 ++++++++----------- Lib/test/test_asyncio/test_unix_events.py | 21 ++++++-- ...2-10-08-06-59-46.gh-issue-94597.TsS0oT.rst | 1 + 7 files changed, 81 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-08-06-59-46.gh-issue-94597.TsS0oT.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 341e85103a3cf7..90353326100665 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -109,6 +109,24 @@ New Modules Improved Modules ================ +asyncio +------- + +* On Linux, :mod:`asyncio` uses :class:`~asyncio.PidfdChildWatcher` by default + if :func:`os.pidfd_open` is available and functional instead of + :class:`~asyncio.ThreadedChildWatcher`. + (Contributed by Kumar Aditya in :gh:`98024`.) + +* The child watcher classes :class:`~asyncio.MultiLoopChildWatcher`, + :class:`~asyncio.FastChildWatcher` and + :class:`~asyncio.SafeChildWatcher` are deprecated and + will be removed in Python 3.14. It is recommended to not manually + configure a child watcher as the event loop now uses the best available + child watcher for each platform (:class:`~asyncio.PidfdChildWatcher` + if supported and :class:`~asyncio.ThreadedChildWatcher` otherwise). + (Contributed by Kumar Aditya in :gh:`94597`.) + + pathlib ------- diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 7fc75cd17ef741..bdffc032e318fd 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1022,6 +1022,13 @@ class SafeChildWatcher(BaseChildWatcher): big number of children (O(n) each time SIGCHLD is raised) """ + def __init__(self): + super().__init__() + warnings._deprecated("SafeChildWatcher", + "{name!r} is deprecated as of Python 3.12 and will be " + "removed in Python {remove}.", + remove=(3, 14)) + def close(self): self._callbacks.clear() super().close() @@ -1100,6 +1107,10 @@ def __init__(self): self._lock = threading.Lock() self._zombies = {} self._forks = 0 + warnings._deprecated("FastChildWatcher", + "{name!r} is deprecated as of Python 3.12 and will be " + "removed in Python {remove}.", + remove=(3, 14)) def close(self): self._callbacks.clear() @@ -1212,6 +1223,10 @@ class MultiLoopChildWatcher(AbstractChildWatcher): def __init__(self): self._callbacks = {} self._saved_sighandler = None + warnings._deprecated("MultiLoopChildWatcher", + "{name!r} is deprecated as of Python 3.12 and will be " + "removed in Python {remove}.", + remove=(3, 14)) def is_active(self): return self._saved_sighandler is not None diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 80d7152128c469..98b55dec37f478 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -22,7 +22,7 @@ import unittest from unittest import mock import weakref - +import warnings if sys.platform not in ('win32', 'vxworks'): import tty @@ -2055,7 +2055,9 @@ def test_remove_fds_after_closing(self): class UnixEventLoopTestsMixin(EventLoopTestsMixin): def setUp(self): super().setUp() - watcher = asyncio.SafeChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + watcher = asyncio.SafeChildWatcher() watcher.attach_loop(self.loop) asyncio.set_child_watcher(watcher) @@ -2652,7 +2654,9 @@ def setUp(self): asyncio.set_event_loop(self.loop) if sys.platform != 'win32': - watcher = asyncio.SafeChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + watcher = asyncio.SafeChildWatcher() watcher.attach_loop(self.loop) asyncio.set_child_watcher(watcher) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 0c49099bc499a5..8fb9313e09dd0e 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -9,6 +9,7 @@ import threading import unittest from unittest import mock +import warnings from test.support import socket_helper try: import ssl @@ -791,8 +792,9 @@ def test_read_all_from_pipe_reader(self): protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop) transport, _ = self.loop.run_until_complete( self.loop.connect_read_pipe(lambda: protocol, pipe)) - - watcher = asyncio.SafeChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + watcher = asyncio.SafeChildWatcher() watcher.attach_loop(self.loop) try: asyncio.set_child_watcher(watcher) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 6ba889407b802e..915ad5587f0a48 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -4,7 +4,6 @@ import sys import unittest import warnings -import functools from unittest import mock import asyncio @@ -31,19 +30,6 @@ 'sys.stdout.buffer.write(data)'))] -@functools.cache -def _has_pidfd_support(): - if not hasattr(os, 'pidfd_open'): - return False - - try: - os.close(os.pidfd_open(os.getpid())) - except OSError: - return False - - return True - - def tearDownModule(): asyncio.set_event_loop_policy(None) @@ -688,7 +674,7 @@ def setUp(self): self.loop = policy.new_event_loop() self.set_event_loop(self.loop) - watcher = self.Watcher() + watcher = self._get_watcher() watcher.attach_loop(self.loop) policy.set_child_watcher(watcher) @@ -703,32 +689,38 @@ def tearDown(self): class SubprocessThreadedWatcherTests(SubprocessWatcherMixin, test_utils.TestCase): - Watcher = unix_events.ThreadedChildWatcher - - @unittest.skip("bpo-38323: MultiLoopChildWatcher has a race condition \ - and these tests can hang the test suite") - class SubprocessMultiLoopWatcherTests(SubprocessWatcherMixin, - test_utils.TestCase): - - Watcher = unix_events.MultiLoopChildWatcher + def _get_watcher(self): + return unix_events.ThreadedChildWatcher() class SubprocessSafeWatcherTests(SubprocessWatcherMixin, test_utils.TestCase): - Watcher = unix_events.SafeChildWatcher + def _get_watcher(self): + with self.assertWarns(DeprecationWarning): + return unix_events.SafeChildWatcher() + + class MultiLoopChildWatcherTests(test_utils.TestCase): + + def test_warns(self): + with self.assertWarns(DeprecationWarning): + unix_events.MultiLoopChildWatcher() class SubprocessFastWatcherTests(SubprocessWatcherMixin, test_utils.TestCase): - Watcher = unix_events.FastChildWatcher + def _get_watcher(self): + with self.assertWarns(DeprecationWarning): + return unix_events.FastChildWatcher() @unittest.skipUnless( - _has_pidfd_support(), + unix_events.can_use_pidfd(), "operating system does not support pidfds", ) class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, test_utils.TestCase): - Watcher = unix_events.PidfdChildWatcher + + def _get_watcher(self): + return unix_events.PidfdChildWatcher() class GenericWatcherTests(test_utils.TestCase): @@ -758,7 +750,7 @@ async def execute(): @unittest.skipUnless( - _has_pidfd_support(), + unix_events.can_use_pidfd(), "operating system does not support pidfds", ) def test_create_subprocess_with_pidfd(self): diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 03fb5e649d8e97..025da0f20ed47e 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -12,6 +12,7 @@ import threading import unittest from unittest import mock +import warnings from test.support import os_helper from test.support import socket_helper @@ -1686,12 +1687,16 @@ def test_close(self, m_waitpid): class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): def create_watcher(self): - return asyncio.SafeChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + return asyncio.SafeChildWatcher() class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): def create_watcher(self): - return asyncio.FastChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + return asyncio.FastChildWatcher() class PolicyTests(unittest.TestCase): @@ -1724,7 +1729,9 @@ def test_get_default_child_watcher(self): def test_get_child_watcher_after_set(self): policy = self.create_policy() - watcher = asyncio.FastChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + watcher = asyncio.FastChildWatcher() policy.set_child_watcher(watcher) self.assertIs(policy._watcher, watcher) @@ -1745,7 +1752,9 @@ def f(): policy.get_event_loop().close() policy = self.create_policy() - policy.set_child_watcher(asyncio.SafeChildWatcher()) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + policy.set_child_watcher(asyncio.SafeChildWatcher()) th = threading.Thread(target=f) th.start() @@ -1757,7 +1766,9 @@ def test_child_watcher_replace_mainloop_existing(self): # Explicitly setup SafeChildWatcher, # default ThreadedChildWatcher has no _loop property - watcher = asyncio.SafeChildWatcher() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + watcher = asyncio.SafeChildWatcher() policy.set_child_watcher(watcher) watcher.attach_loop(loop) diff --git a/Misc/NEWS.d/next/Library/2022-10-08-06-59-46.gh-issue-94597.TsS0oT.rst b/Misc/NEWS.d/next/Library/2022-10-08-06-59-46.gh-issue-94597.TsS0oT.rst new file mode 100644 index 00000000000000..f504ccf39ec948 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-08-06-59-46.gh-issue-94597.TsS0oT.rst @@ -0,0 +1 @@ +The child watcher classes :class:`~asyncio.MultiLoopChildWatcher`, :class:`~asyncio.FastChildWatcher` and :class:`~asyncio.SafeChildWatcher` are deprecated and will be removed in Python 3.14. Patch by Kumar Aditya. From 095c5220a02a764b8ec1312e1ddffd87dedc0434 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 8 Oct 2022 18:16:52 -0400 Subject: [PATCH 136/151] Fix link to Lifecycle of a Pull Request in CONTRIBUTING (#98102) * Fix link to Lifecycle of a Pull Request in CONTRIBUTING * Remove trailing backslash. Co-authored-by: Ezio Melotti --- .github/CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst index 627f57070d200b..f4affee76e1d44 100644 --- a/.github/CONTRIBUTING.rst +++ b/.github/CONTRIBUTING.rst @@ -38,7 +38,7 @@ also suggestions on how you can most effectively help the project. Please be aware that our workflow does deviate slightly from the typical GitHub project. Details on how to properly submit a pull request are covered in -`Lifecycle of a Pull Request `_. +`Lifecycle of a Pull Request `_. We utilize various bots and status checks to help with this, so do follow the comments they leave and their "Details" links, respectively. The key points of our workflow that are not covered by a bot or status check are: From cef2db973dbd2b6765ca154550fee2cf7d54344e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Sun, 9 Oct 2022 03:54:21 +0200 Subject: [PATCH 137/151] Minor edits to the Descriptor HowTo Guide (GH-24901) Co-authored-by: Raymond Hettinger --- Doc/howto/descriptor.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 91a6c31c33b8bd..74710d9b3fc2ed 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -847,7 +847,7 @@ afterwards, :meth:`__set_name__` will need to be called manually. ORM example ----------- -The following code is simplified skeleton showing how data descriptors could +The following code is a simplified skeleton showing how data descriptors could be used to implement an `object relational mapping `_. @@ -1535,6 +1535,8 @@ by member descriptors: def __get__(self, obj, objtype=None): 'Emulate member_get() in Objects/descrobject.c' # Also see PyMember_GetOne() in Python/structmember.c + if obj is None: + return self value = obj._slotvalues[self.offset] if value is null: raise AttributeError(self.name) @@ -1563,13 +1565,13 @@ variables: class Type(type): 'Simulate how the type metaclass adds member objects for slots' - def __new__(mcls, clsname, bases, mapping): + def __new__(mcls, clsname, bases, mapping, **kwargs): 'Emulate type_new() in Objects/typeobject.c' # type_new() calls PyTypeReady() which calls add_methods() slot_names = mapping.get('slot_names', []) for offset, name in enumerate(slot_names): mapping[name] = Member(name, clsname, offset) - return type.__new__(mcls, clsname, bases, mapping) + return type.__new__(mcls, clsname, bases, mapping, **kwargs) The :meth:`object.__new__` method takes care of creating instances that have slots instead of an instance dictionary. Here is a rough simulation in pure @@ -1580,7 +1582,7 @@ Python: class Object: 'Simulate how object.__new__() allocates memory for __slots__' - def __new__(cls, *args): + def __new__(cls, *args, **kwargs): 'Emulate object_new() in Objects/typeobject.c' inst = super().__new__(cls) if hasattr(cls, 'slot_names'): @@ -1593,7 +1595,7 @@ Python: cls = type(self) if hasattr(cls, 'slot_names') and name not in cls.slot_names: raise AttributeError( - f'{type(self).__name__!r} object has no attribute {name!r}' + f'{cls.__name__!r} object has no attribute {name!r}' ) super().__setattr__(name, value) @@ -1602,7 +1604,7 @@ Python: cls = type(self) if hasattr(cls, 'slot_names') and name not in cls.slot_names: raise AttributeError( - f'{type(self).__name__!r} object has no attribute {name!r}' + f'{cls.__name__!r} object has no attribute {name!r}' ) super().__delattr__(name) From 4165f801e97cf0d6768c587d9e22c9b78335cbb9 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 9 Oct 2022 20:16:33 +0900 Subject: [PATCH 138/151] gh-97841: Add methoddef for _filters_mutated (gh-98115) --- Python/_warnings.c | 11 ++++++++--- Python/clinic/_warnings.c.h | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Python/_warnings.c b/Python/_warnings.c index 0d4c50f769b03c..b46fbdca9db38c 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1086,8 +1086,14 @@ warnings_warn_explicit_impl(PyObject *module, PyObject *message, return returned; } +/*[clinic input] +_filters_mutated as warnings_filters_mutated + +[clinic start generated code]*/ + static PyObject * -warnings_filters_mutated(PyObject *self, PyObject *Py_UNUSED(args)) +warnings_filters_mutated_impl(PyObject *module) +/*[clinic end generated code: output=8ce517abd12b88f4 input=35ecbf08ee2491b2]*/ { PyInterpreterState *interp = get_current_interp(); if (interp == NULL) { @@ -1344,8 +1350,7 @@ _PyErr_WarnUnawaitedCoroutine(PyObject *coro) static PyMethodDef warnings_functions[] = { WARNINGS_WARN_METHODDEF WARNINGS_WARN_EXPLICIT_METHODDEF - {"_filters_mutated", _PyCFunction_CAST(warnings_filters_mutated), METH_NOARGS, - NULL}, + WARNINGS_FILTERS_MUTATED_METHODDEF /* XXX(brett.cannon): add showwarning? */ /* XXX(brett.cannon): Reasonable to add formatwarning? */ {NULL, NULL} /* sentinel */ diff --git a/Python/clinic/_warnings.c.h b/Python/clinic/_warnings.c.h index 13ebbf45b8e168..8838a42afc1c2a 100644 --- a/Python/clinic/_warnings.c.h +++ b/Python/clinic/_warnings.c.h @@ -199,4 +199,21 @@ warnings_warn_explicit(PyObject *module, PyObject *const *args, Py_ssize_t nargs exit: return return_value; } -/*[clinic end generated code: output=2eac4fabc87a4d56 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(warnings_filters_mutated__doc__, +"_filters_mutated($module, /)\n" +"--\n" +"\n"); + +#define WARNINGS_FILTERS_MUTATED_METHODDEF \ + {"_filters_mutated", (PyCFunction)warnings_filters_mutated, METH_NOARGS, warnings_filters_mutated__doc__}, + +static PyObject * +warnings_filters_mutated_impl(PyObject *module); + +static PyObject * +warnings_filters_mutated(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return warnings_filters_mutated_impl(module); +} +/*[clinic end generated code: output=0d264d1ddfc37100 input=a9049054013a1b77]*/ From 69c00cc669a74a1173113eb0a204878ccb8dd4c9 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 10 Oct 2022 00:29:25 +0200 Subject: [PATCH 139/151] Update whatsnew instructions for GitHub (#98124) --- Doc/whatsnew/3.12.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 90353326100665..f8122ed1dc44f0 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -37,11 +37,11 @@ * Credit the author of a patch or bugfix. Just the name is sufficient; the e-mail address isn't necessary. - * It's helpful to add the bug/patch number as a comment: + * It's helpful to add the issue number as a comment: XXX Describe the transmogrify() function added to the socket module. - (Contributed by P.Y. Developer in :issue:`12345`.) + (Contributed by P.Y. Developer in :gh:`12345`.) This saves the maintainer the effort of going through the VCS log when researching a change. From 4bc4dfd79466aae792960eb2c48e62fcccf75bd3 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Sun, 9 Oct 2022 17:51:02 -0700 Subject: [PATCH 140/151] gh-56133: copyreg docs: Clarify function/constructor parameter (#95497) --- Doc/library/copyreg.rst | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Doc/library/copyreg.rst b/Doc/library/copyreg.rst index dc35965be3e40d..866b180f4bc3b8 100644 --- a/Doc/library/copyreg.rst +++ b/Doc/library/copyreg.rst @@ -25,20 +25,17 @@ Such constructors may be factory functions or class instances. hence not valid as a constructor), raises :exc:`TypeError`. -.. function:: pickle(type, function, constructor=None) +.. function:: pickle(type, function, constructor_ob=None) Declares that *function* should be used as a "reduction" function for objects of type *type*. *function* should return either a string or a tuple - containing two or three elements. + containing two or three elements. See the :attr:`~pickle.Pickler.dispatch_table` + for more details on the interface of *function*. - The optional *constructor* parameter, if provided, is a callable object which - can be used to reconstruct the object when called with the tuple of arguments - returned by *function* at pickling time. A :exc:`TypeError` is raised if the - *constructor* is not callable. + The *constructor_ob* parameter is a legacy feature and is now ignored, but if + passed it must be a callable. - See the :mod:`pickle` module for more details on the interface - expected of *function* and *constructor*. Note that the - :attr:`~pickle.Pickler.dispatch_table` attribute of a pickler + Note that the :attr:`~pickle.Pickler.dispatch_table` attribute of a pickler object or subclass of :class:`pickle.Pickler` can also be used for declaring reduction functions. From a15d14e7ce76db5828ad8cb546cdbd4ca360e2bb Mon Sep 17 00:00:00 2001 From: da-woods Date: Mon, 10 Oct 2022 01:55:53 +0100 Subject: [PATCH 141/151] Fix types in buffer/memoryview docs (#98118) The definition of obj in the `Py_buffer` struct is as a PyObject* https://github.com/python/cpython/blob/ec091bd47e2f968b0d1631b9a8104283a7beeb1b/Include/pybuffer.h#L22 PyMemoryView_GET_BASE returns `.obj` - thus its return type should be a PyObject* (or at least a void*). It definitely doesn't return `Py_buffer` --- Doc/c-api/buffer.rst | 2 +- Doc/c-api/memoryview.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index 05e131d06b909d..a04062fb2a68f1 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -99,7 +99,7 @@ a buffer, see :c:func:`PyObject_GetBuffer`. For :term:`contiguous` arrays, the value points to the beginning of the memory block. - .. c:member:: void *obj + .. c:member:: PyObject *obj A new reference to the exporting object. The reference is owned by the consumer and automatically decremented and set to ``NULL`` by diff --git a/Doc/c-api/memoryview.rst b/Doc/c-api/memoryview.rst index 4d94b3f545f327..ebd5c7760437bf 100644 --- a/Doc/c-api/memoryview.rst +++ b/Doc/c-api/memoryview.rst @@ -55,7 +55,7 @@ any other object. *mview* **must** be a memoryview instance; this macro doesn't check its type, you must do it yourself or you will risk crashes. -.. c:function:: Py_buffer *PyMemoryView_GET_BASE(PyObject *mview) +.. c:function:: PyObject *PyMemoryView_GET_BASE(PyObject *mview) Return either a pointer to the exporting object that the memoryview is based on or ``NULL`` if the memoryview has been created by one of the functions From ad713e88ee5313df9fd27ba631f43f64dd39bb14 Mon Sep 17 00:00:00 2001 From: Tiger Date: Sun, 9 Oct 2022 20:31:53 -0500 Subject: [PATCH 142/151] gh-98083: Fix URLs in `README.rst` (#98082) --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 4ae5dfd1bb2525..8227ae748d3934 100644 --- a/README.rst +++ b/README.rst @@ -65,7 +65,7 @@ Building a complete Python installation requires the use of various additional third-party libraries, depending on your build platform and configure options. Not all standard library modules are buildable or useable on all platforms. Refer to the -`Install dependencies `_ +`Install dependencies `_ section of the `Developer Guide`_ for current detailed information on dependencies for various Linux distributions and macOS. @@ -135,7 +135,7 @@ What's New We have a comprehensive overview of the changes in the `What's New in Python 3.12 `_ document. For a more detailed change log, read `Misc/NEWS -`_, but a full +`_, but a full accounting of changes can only be gleaned from the `commit history `_. @@ -189,7 +189,7 @@ your environment, you can `file a bug report `_ and include relevant output from that command to show the issue. -See `Running & Writing Tests `_ +See `Running & Writing Tests `_ for more on running tests. Installing multiple versions From a87db59be9090bd5b365e12dfa8f6d8ffacdd845 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 10 Oct 2022 03:59:07 +0200 Subject: [PATCH 143/151] bpo-43564: preserve original exception in args of FTP URLError (#24938) * bpo-43564: preserve original error in args of FTP URLError * Add NEWS blurb Co-authored-by: Carl Meyer --- Lib/urllib/request.py | 2 +- .../next/Library/2022-10-09-12-12-38.gh-issue-87730.ClgP3f.rst | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2022-10-09-12-12-38.gh-issue-87730.ClgP3f.rst diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index e2d5b8c5c59a3c..278aa3a14bfeea 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1582,7 +1582,7 @@ def ftp_open(self, req): headers = email.message_from_string(headers) return addinfourl(fp, headers, req.full_url) except ftplib.all_errors as exp: - raise URLError(f'ftp error: {exp}') from exp + raise URLError(exp) from exp def connect_ftp(self, user, passwd, host, port, dirs, timeout): return ftpwrapper(user, passwd, host, port, dirs, timeout, diff --git a/Misc/NEWS.d/next/Library/2022-10-09-12-12-38.gh-issue-87730.ClgP3f.rst b/Misc/NEWS.d/next/Library/2022-10-09-12-12-38.gh-issue-87730.ClgP3f.rst new file mode 100644 index 00000000000000..6c63fa4928c62e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-10-09-12-12-38.gh-issue-87730.ClgP3f.rst @@ -0,0 +1,3 @@ +Wrap network errors consistently in urllib FTP support, so the test suite +doesn't fail when a network is available but the public internet is not +reachable. From 096a6a038679612a97d3ef177d711cbc95b64a14 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Mon, 10 Oct 2022 09:01:16 +0200 Subject: [PATCH 144/151] doc: remove a misleading statement. (GH-98093) --- Doc/tutorial/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst index ba0f4770529783..558b1c3eec60ed 100644 --- a/Doc/tutorial/introduction.rst +++ b/Doc/tutorial/introduction.rst @@ -70,8 +70,8 @@ the ones with a fractional part (e.g. ``5.0``, ``1.6``) have type :class:`float`. We will see more about numeric types later in the tutorial. Division (``/``) always returns a float. To do :term:`floor division` and -get an integer result (discarding any fractional result) you can use the ``//`` -operator; to calculate the remainder you can use ``%``:: +get an integer result you can use the ``//`` operator; to calculate +the remainder you can use ``%``:: >>> 17 / 3 # classic division returns a float 5.666666666666667 From 70a270863562c30e3f7b580605546960aeeb1aa0 Mon Sep 17 00:00:00 2001 From: Stanley <46876382+slateny@users.noreply.github.com> Date: Mon, 10 Oct 2022 06:43:01 -0700 Subject: [PATCH 145/151] gh-83940: os docs: Improve wording for getenv/getenvb (#98113) --- Doc/library/os.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 8727f811def1c0..43066fa1e13d74 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -304,8 +304,8 @@ process and user. .. function:: getenv(key, default=None) - Return the value of the environment variable *key* if it exists, or - *default* if it doesn't. *key*, *default* and the result are str. Note that + Return the value of the environment variable *key* as a string if it exists, or + *default* if it doesn't. *key* is a string. Note that since :func:`getenv` uses :data:`os.environ`, the mapping of :func:`getenv` is similarly also captured on import, and the function may not reflect future environment changes. @@ -319,8 +319,8 @@ process and user. .. function:: getenvb(key, default=None) - Return the value of the environment variable *key* if it exists, or - *default* if it doesn't. *key*, *default* and the result are bytes. Note that + Return the value of the environment variable *key* as bytes if it exists, or + *default* if it doesn't. *key* must be bytes. Note that since :func:`getenvb` uses :data:`os.environb`, the mapping of :func:`getenvb` is similarly also captured on import, and the function may not reflect future environment changes. From fbfd13b6af90c78433b864932b80c00b66072d45 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 10 Oct 2022 11:28:41 -0400 Subject: [PATCH 146/151] gh-94808: Add coverage for bytesarray_setitem (#95802) --- Lib/test/test_bytes.py | 111 ++++++++++++++++++++++++-------------- Modules/_testcapimodule.c | 15 ++++++ 2 files changed, 85 insertions(+), 41 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 53ba1ad6f5911f..7c62b722059d12 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1225,6 +1225,8 @@ class SubBytes(bytes): class ByteArrayTest(BaseBytesTest, unittest.TestCase): type2test = bytearray + _testcapi = import_helper.import_module('_testcapi') + def test_getitem_error(self): b = bytearray(b'python') msg = "bytearray indices must be integers or slices" @@ -1317,47 +1319,73 @@ def by(s): self.assertEqual(re.findall(br"\w+", b), [by("Hello"), by("world")]) def test_setitem(self): - b = bytearray([1, 2, 3]) - b[1] = 100 - self.assertEqual(b, bytearray([1, 100, 3])) - b[-1] = 200 - self.assertEqual(b, bytearray([1, 100, 200])) - b[0] = Indexable(10) - self.assertEqual(b, bytearray([10, 100, 200])) - try: - b[3] = 0 - self.fail("Didn't raise IndexError") - except IndexError: - pass - try: - b[-10] = 0 - self.fail("Didn't raise IndexError") - except IndexError: - pass - try: - b[0] = 256 - self.fail("Didn't raise ValueError") - except ValueError: - pass - try: - b[0] = Indexable(-1) - self.fail("Didn't raise ValueError") - except ValueError: - pass - try: - b[0] = None - self.fail("Didn't raise TypeError") - except TypeError: - pass + def setitem_as_mapping(b, i, val): + b[i] = val + + def setitem_as_sequence(b, i, val): + self._testcapi.sequence_setitem(b, i, val) + + def do_tests(setitem): + b = bytearray([1, 2, 3]) + setitem(b, 1, 100) + self.assertEqual(b, bytearray([1, 100, 3])) + setitem(b, -1, 200) + self.assertEqual(b, bytearray([1, 100, 200])) + setitem(b, 0, Indexable(10)) + self.assertEqual(b, bytearray([10, 100, 200])) + try: + setitem(b, 3, 0) + self.fail("Didn't raise IndexError") + except IndexError: + pass + try: + setitem(b, -10, 0) + self.fail("Didn't raise IndexError") + except IndexError: + pass + try: + setitem(b, 0, 256) + self.fail("Didn't raise ValueError") + except ValueError: + pass + try: + setitem(b, 0, Indexable(-1)) + self.fail("Didn't raise ValueError") + except ValueError: + pass + try: + setitem(b, 0, None) + self.fail("Didn't raise TypeError") + except TypeError: + pass + + with self.subTest("tp_as_mapping"): + do_tests(setitem_as_mapping) + + with self.subTest("tp_as_sequence"): + do_tests(setitem_as_sequence) def test_delitem(self): - b = bytearray(range(10)) - del b[0] - self.assertEqual(b, bytearray(range(1, 10))) - del b[-1] - self.assertEqual(b, bytearray(range(1, 9))) - del b[4] - self.assertEqual(b, bytearray([1, 2, 3, 4, 6, 7, 8])) + def del_as_mapping(b, i): + del b[i] + + def del_as_sequence(b, i): + self._testcapi.sequence_delitem(b, i) + + def do_tests(delete): + b = bytearray(range(10)) + delete(b, 0) + self.assertEqual(b, bytearray(range(1, 10))) + delete(b, -1) + self.assertEqual(b, bytearray(range(1, 9))) + delete(b, 4) + self.assertEqual(b, bytearray([1, 2, 3, 4, 6, 7, 8])) + + with self.subTest("tp_as_mapping"): + do_tests(del_as_mapping) + + with self.subTest("tp_as_sequence"): + do_tests(del_as_sequence) def test_setslice(self): b = bytearray(range(10)) @@ -1729,6 +1757,8 @@ def test_repeat_after_setslice(self): self.assertEqual(b3, b'xcxcxc') def test_mutating_index(self): + # See gh-91153 + class Boom: def __index__(self): b.clear() @@ -1740,10 +1770,9 @@ def __index__(self): b[0] = Boom() with self.subTest("tp_as_sequence"): - _testcapi = import_helper.import_module('_testcapi') b = bytearray(b'Now you see me...') with self.assertRaises(IndexError): - _testcapi.sequence_setitem(b, 0, Boom()) + self._testcapi.sequence_setitem(b, 0, Boom()) class AssortedBytesTest(unittest.TestCase): diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index d54f706e5bd852..59ed1618baf928 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4846,6 +4846,20 @@ sequence_setitem(PyObject *self, PyObject *args) } +static PyObject * +sequence_delitem(PyObject *self, PyObject *args) +{ + Py_ssize_t i; + PyObject *seq; + if (!PyArg_ParseTuple(args, "On", &seq, &i)) { + return NULL; + } + if (PySequence_DelItem(seq, i)) { + return NULL; + } + Py_RETURN_NONE; +} + static PyObject * hasattr_string(PyObject *self, PyObject* args) { @@ -5885,6 +5899,7 @@ static PyMethodDef TestMethods[] = { {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS}, {"sequence_getitem", sequence_getitem, METH_VARARGS}, {"sequence_setitem", sequence_setitem, METH_VARARGS}, + {"sequence_delitem", sequence_delitem, METH_VARARGS}, {"hasattr_string", hasattr_string, METH_VARARGS}, {"meth_varargs", meth_varargs, METH_VARARGS}, {"meth_varargs_keywords", _PyCFunction_CAST(meth_varargs_keywords), METH_VARARGS|METH_KEYWORDS}, From 6d919462516409f718d2df519e75c9b1198676b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=B6rgens?= Date: Tue, 11 Oct 2022 00:12:29 +0800 Subject: [PATCH 147/151] gh-96821: Fix undefined behaviour in `audioop.c` (#96923) * gh-96821: Fix undefined behaviour in `audioop.c` Left-shifting negative numbers is undefined behaviour. Fortunately, multiplication works just as well, is defined behaviour, and gets compiled to the same machine code as before by optimizing compilers. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- ...2-09-19-03-35-01.gh-issue-96821.izK6JA.rst | 1 + Modules/audioop.c | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-09-19-03-35-01.gh-issue-96821.izK6JA.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-19-03-35-01.gh-issue-96821.izK6JA.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-19-03-35-01.gh-issue-96821.izK6JA.rst new file mode 100644 index 00000000000000..73d0c76f0297ca --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-19-03-35-01.gh-issue-96821.izK6JA.rst @@ -0,0 +1 @@ +Fix undefined behaviour in ``audioop.c``. diff --git a/Modules/audioop.c b/Modules/audioop.c index d74e634ac44889..c29a3e8df4099d 100644 --- a/Modules/audioop.c +++ b/Modules/audioop.c @@ -59,6 +59,8 @@ static const int16_t seg_uend[8] = { static int16_t search(int16_t val, const int16_t *table, int size) { + assert(0 <= size); + assert(size < INT16_MAX); int i; for (i = 0; i < size; i++) { @@ -170,6 +172,7 @@ st_14linear2ulaw(int16_t pcm_val) /* 2's complement (14-bit range) */ if (seg >= 8) /* out of range, return maximum value. */ return (unsigned char) (0x7F ^ mask); else { + assert(seg >= 0); uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); return (uval ^ mask); } @@ -300,13 +303,13 @@ static const int stepsizeTable[89] = { #ifdef WORDS_BIGENDIAN #define GETINT24(cp, i) ( \ ((unsigned char *)(cp) + (i))[2] + \ - (((unsigned char *)(cp) + (i))[1] << 8) + \ - (((signed char *)(cp) + (i))[0] << 16) ) + (((unsigned char *)(cp) + (i))[1] * (1 << 8)) + \ + (((signed char *)(cp) + (i))[0] * (1 << 16)) ) #else #define GETINT24(cp, i) ( \ ((unsigned char *)(cp) + (i))[0] + \ - (((unsigned char *)(cp) + (i))[1] << 8) + \ - (((signed char *)(cp) + (i))[2] << 16) ) + (((unsigned char *)(cp) + (i))[1] * (1 << 8)) + \ + (((signed char *)(cp) + (i))[2] * (1 << 16)) ) #endif @@ -347,10 +350,10 @@ static const int stepsizeTable[89] = { } while(0) -#define GETSAMPLE32(size, cp, i) ( \ - (size == 1) ? (int)GETINT8((cp), (i)) << 24 : \ - (size == 2) ? (int)GETINT16((cp), (i)) << 16 : \ - (size == 3) ? (int)GETINT24((cp), (i)) << 8 : \ +#define GETSAMPLE32(size, cp, i) ( \ + (size == 1) ? (int)GETINT8((cp), (i)) * (1 << 24) : \ + (size == 2) ? (int)GETINT16((cp), (i)) * (1 << 16) : \ + (size == 3) ? (int)GETINT24((cp), (i)) * (1 << 8) : \ (int)GETINT32((cp), (i))) #define SETSAMPLE32(size, cp, i, val) do { \ @@ -1558,7 +1561,7 @@ audioop_ulaw2lin_impl(PyObject *module, Py_buffer *fragment, int width) cp = fragment->buf; for (i = 0; i < fragment->len*width; i += width) { - int val = st_ulaw2linear16(*cp++) << 16; + int val = st_ulaw2linear16(*cp++) * (1 << 16); SETSAMPLE32(width, ncp, i, val); } return rv; @@ -1632,7 +1635,7 @@ audioop_alaw2lin_impl(PyObject *module, Py_buffer *fragment, int width) cp = fragment->buf; for (i = 0; i < fragment->len*width; i += width) { - val = st_alaw2linear16(*cp++) << 16; + val = st_alaw2linear16(*cp++) * (1 << 16); SETSAMPLE32(width, ncp, i, val); } return rv; @@ -1757,7 +1760,7 @@ audioop_lin2adpcm_impl(PyObject *module, Py_buffer *fragment, int width, /* Step 6 - Output value */ if ( bufferstep ) { - outputbuffer = (delta << 4) & 0xf0; + outputbuffer = (delta * (1 << 4)) & 0xf0; } else { *ncp++ = (delta & 0x0f) | outputbuffer; } @@ -1875,7 +1878,7 @@ audioop_adpcm2lin_impl(PyObject *module, Py_buffer *fragment, int width, step = stepsizeTable[index]; /* Step 6 - Output value */ - SETSAMPLE32(width, ncp, i, valpred << 16); + SETSAMPLE32(width, ncp, i, valpred * (1 << 16)); } rv = Py_BuildValue("(O(ii))", str, valpred, index); From 9eda93b8ecc06d2d58524b89661895bcf4108a3b Mon Sep 17 00:00:00 2001 From: Matt Page Date: Mon, 10 Oct 2022 10:20:05 -0700 Subject: [PATCH 148/151] Rename function event callbacks to match dict watchers --- Include/cpython/funcobject.h | 6 +++--- Include/internal/pycore_interp.h | 2 +- Lib/test/test_func_events.py | 8 ++++---- Modules/_testcapi/func_events.c | 17 +++++++++-------- Objects/funcobject.c | 12 ++++++------ Python/pystate.c | 2 +- 6 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 26f407919e7c1c..ea368f6f194777 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -156,7 +156,7 @@ typedef enum { * contain a borrowed reference to the new value that is about to be stored in * the function. Otherwise the third argument is NULL. */ -typedef void(*PyFunction_EventCallback)( +typedef void(*PyFunction_WatchCallback)( PyFunction_Event event, PyFunctionObject *func, PyObject *new_value); @@ -167,8 +167,8 @@ typedef void(*PyFunction_EventCallback)( * * Pass NULL to clear the callback. */ -PyAPI_FUNC(void) PyFunction_SetEventCallback(PyFunction_EventCallback callback); -PyAPI_FUNC(PyFunction_EventCallback) PyFunction_GetEventCallback(void); +PyAPI_FUNC(void) PyFunction_SetWatchCallback(PyFunction_WatchCallback callback); +PyAPI_FUNC(PyFunction_WatchCallback) PyFunction_GetWatchCallback(void); #ifdef __cplusplus } diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index f58cee899977c5..80c55572551e07 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -180,7 +180,7 @@ struct _is { struct callable_cache callable_cache; - PyFunction_EventCallback func_event_callback; + PyFunction_WatchCallback func_watch_callback; /* The following fields are here to avoid allocation during init. The data is exposed through PyInterpreterState pointer fields. diff --git a/Lib/test/test_func_events.py b/Lib/test/test_func_events.py index 418f3b9a18377a..f35c226b74a4c7 100644 --- a/Lib/test/test_func_events.py +++ b/Lib/test/test_func_events.py @@ -5,8 +5,8 @@ PYFUNC_EVENT_MODIFY_CODE, PYFUNC_EVENT_MODIFY_DEFAULTS, PYFUNC_EVENT_MODIFY_KWDEFAULTS, - restore_func_event_callback, - set_func_event_callback, + restore_func_watch_callback, + set_func_watch_callback, ) @@ -15,7 +15,7 @@ def test_func_events_dispatched(self): events = [] def handle_func_event(*args): events.append(args) - set_func_event_callback(handle_func_event) + set_func_watch_callback(handle_func_event) try: def myfunc(): @@ -40,4 +40,4 @@ def myfunc(): del myfunc self.assertIn((PYFUNC_EVENT_DESTROY, myfunc_id, None), events) finally: - restore_func_event_callback() + restore_func_watch_callback() diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c index af218b045427c2..f3a4f98b2757f2 100644 --- a/Modules/_testcapi/func_events.c +++ b/Modules/_testcapi/func_events.c @@ -1,7 +1,7 @@ #include "parts.h" static PyObject *pyfunc_callback = NULL; -static PyFunction_EventCallback orig_callback = NULL; +static PyFunction_WatchCallback orig_callback = NULL; static PyObject *get_id(PyObject *obj) { PyObject *builtins = PyEval_GetBuiltins(); @@ -68,7 +68,7 @@ add_event(PyObject *module, const char *name, PyFunction_Event event) } static PyObject * -set_func_event_callback(PyObject *self, PyObject *func) +set_func_watch_callback(PyObject *self, PyObject *func) { if (!PyFunction_Check(func)) { PyErr_SetString(PyExc_TypeError, "'func' must be a function"); @@ -80,26 +80,26 @@ set_func_event_callback(PyObject *self, PyObject *func) } Py_INCREF(func); pyfunc_callback = func; - orig_callback = PyFunction_GetEventCallback(); - PyFunction_SetEventCallback(call_pyfunc_callback); + orig_callback = PyFunction_GetWatchCallback(); + PyFunction_SetWatchCallback(call_pyfunc_callback); Py_RETURN_NONE; } static PyObject * -restore_func_event_callback(PyObject *self, PyObject *Py_UNUSED(ignored)) { +restore_func_watch_callback(PyObject *self, PyObject *Py_UNUSED(ignored)) { if (pyfunc_callback == NULL) { PyErr_SetString(PyExc_RuntimeError, "nothing to restore"); return NULL; } - PyFunction_SetEventCallback(orig_callback); + PyFunction_SetWatchCallback(orig_callback); orig_callback = NULL; Py_CLEAR(pyfunc_callback); Py_RETURN_NONE; } static PyMethodDef TestMethods[] = { - {"set_func_event_callback", set_func_event_callback, METH_O}, - {"restore_func_event_callback", restore_func_event_callback, METH_NOARGS}, + {"set_func_watch_callback", set_func_watch_callback, METH_O}, + {"restore_func_watch_callback", restore_func_watch_callback, METH_NOARGS}, {NULL}, }; @@ -118,4 +118,5 @@ _PyTestCapi_Init_FuncEvents(PyObject *m) { #undef ADD_EVENT return 0; + } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 7704a515eb2185..1f7ff3033b8196 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -13,7 +13,7 @@ static void handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) { PyThreadState *tstate = _PyThreadState_GET(); - PyFunction_EventCallback handle_event = tstate->interp->func_event_callback; + PyFunction_WatchCallback handle_event = tstate->interp->func_watch_callback; if (handle_event == NULL) { return; } @@ -21,19 +21,19 @@ handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_ } void -PyFunction_SetEventCallback(PyFunction_EventCallback callback) +PyFunction_SetWatchCallback(PyFunction_WatchCallback callback) { PyThreadState *tstate = _PyThreadState_GET(); assert(tstate->interp->_initialized); - tstate->interp->func_event_callback = callback; + tstate->interp->func_watch_callback = callback; } -PyFunction_EventCallback -PyFunction_GetEventCallback() +PyFunction_WatchCallback +PyFunction_GetWatchCallback() { PyThreadState *tstate = _PyThreadState_GET(); assert(tstate->interp->_initialized); - return tstate->interp->func_event_callback; + return tstate->interp->func_watch_callback; } PyFunctionObject * diff --git a/Python/pystate.c b/Python/pystate.c index 42a8d073f1bbd3..60332f7aa8d56e 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -430,7 +430,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->after_forkers_parent); Py_CLEAR(interp->after_forkers_child); #endif - interp->func_event_callback = NULL; + interp->func_watch_callback = NULL; _PyAST_Fini(interp); _PyWarnings_Fini(interp); From ebf15c7ab8d0d5c8f94617bcd7b3d3a75098cb34 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Mon, 10 Oct 2022 15:37:02 -0700 Subject: [PATCH 149/151] Support multiple watchers --- Include/cpython/funcobject.h | 17 ++++-- Include/internal/pycore_function.h | 2 + Include/internal/pycore_interp.h | 5 +- Lib/test/test_func_events.py | 68 +++++++++++++++++++--- Modules/_testcapi/func_events.c | 92 ++++++++++++++++++++++-------- Objects/funcobject.c | 48 +++++++++++----- Python/pystate.c | 5 +- 7 files changed, 183 insertions(+), 54 deletions(-) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index ea368f6f194777..06eb2b54ccf048 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -156,19 +156,26 @@ typedef enum { * contain a borrowed reference to the new value that is about to be stored in * the function. Otherwise the third argument is NULL. */ -typedef void(*PyFunction_WatchCallback)( +typedef int (*PyFunction_WatchCallback)( PyFunction_Event event, PyFunctionObject *func, PyObject *new_value); /* - * Set the per-interpreter callback that will be invoked for function lifecycle + * Register a per-interpreter callback that will be invoked for function lifecycle * events. * - * Pass NULL to clear the callback. + * Returns a handle that may be passed to PyFunction_ClearWatcher on success, + * or -1 and sets an error if no more handles are available. */ -PyAPI_FUNC(void) PyFunction_SetWatchCallback(PyFunction_WatchCallback callback); -PyAPI_FUNC(PyFunction_WatchCallback) PyFunction_GetWatchCallback(void); +PyAPI_FUNC(int) PyFunction_AddWatcher(PyFunction_WatchCallback callback); + +/* + * Clear the watcher associated with the watcher_id handle. + * + * Returns 0 on success or -1 if no watcher exists for the supplied id. + */ +PyAPI_FUNC(int) PyFunction_ClearWatcher(int watcher_id); #ifdef __cplusplus } diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h index 1c87aa31ddb611..9d4928e4a30bec 100644 --- a/Include/internal/pycore_function.h +++ b/Include/internal/pycore_function.h @@ -8,6 +8,8 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#define FUNC_MAX_WATCHERS 8 + extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor *constr); extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 80c55572551e07..da9cbd8333436e 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -17,6 +17,7 @@ extern "C" { #include "pycore_dict.h" // struct _Py_dict_state #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_floatobject.h" // struct _Py_float_state +#include "pycore_function.h" // FUNC_MAX_WATCHERS #include "pycore_genobject.h" // struct _Py_async_gen_state #include "pycore_gc.h" // struct _gc_runtime_state #include "pycore_list.h" // struct _Py_list_state @@ -147,6 +148,7 @@ struct _is { _PyFrameEvalFunction eval_frame; PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS]; + PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS]; Py_ssize_t co_extra_user_count; freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; @@ -179,9 +181,6 @@ struct _is { struct types_state types; struct callable_cache callable_cache; - - PyFunction_WatchCallback func_watch_callback; - /* The following fields are here to avoid allocation during init. The data is exposed through PyInterpreterState pointer fields. These fields should not be accessed directly outside of init. diff --git a/Lib/test/test_func_events.py b/Lib/test/test_func_events.py index f35c226b74a4c7..c709c3d2830710 100644 --- a/Lib/test/test_func_events.py +++ b/Lib/test/test_func_events.py @@ -1,23 +1,36 @@ import unittest + +from contextlib import contextmanager +from test.support import catch_unraisable_exception, import_helper + +_testcapi = import_helper.import_module("_testcapi") + from _testcapi import ( PYFUNC_EVENT_CREATED, PYFUNC_EVENT_DESTROY, PYFUNC_EVENT_MODIFY_CODE, PYFUNC_EVENT_MODIFY_DEFAULTS, PYFUNC_EVENT_MODIFY_KWDEFAULTS, - restore_func_watch_callback, - set_func_watch_callback, + _add_func_watcher, + _clear_func_watcher, ) class FuncEventsTest(unittest.TestCase): + @contextmanager + def add_watcher(self, func): + wid = _add_func_watcher(func) + try: + yield + finally: + _clear_func_watcher(wid) + def test_func_events_dispatched(self): events = [] - def handle_func_event(*args): + def watcher(*args): events.append(args) - set_func_watch_callback(handle_func_event) - try: + with self.add_watcher(watcher): def myfunc(): pass self.assertIn((PYFUNC_EVENT_CREATED, myfunc, None), events) @@ -39,5 +52,46 @@ def myfunc(): events = [] del myfunc self.assertIn((PYFUNC_EVENT_DESTROY, myfunc_id, None), events) - finally: - restore_func_watch_callback() + + def test_multiple_watchers(self): + events0 = [] + def first_watcher(*args): + events0.append(args) + + events1 = [] + def second_watcher(*args): + events1.append(args) + + with self.add_watcher(first_watcher): + with self.add_watcher(second_watcher): + def myfunc(): + pass + + event = (PYFUNC_EVENT_CREATED, myfunc, None) + self.assertIn(event, events0) + self.assertIn(event, events1) + + def test_watcher_raises_error(self): + class MyError(Exception): + pass + + def watcher(*args): + raise MyError("testing 123") + + with self.add_watcher(watcher): + with catch_unraisable_exception() as cm: + def myfunc(): + pass + + self.assertIs(cm.unraisable.object, myfunc) + self.assertIsInstance(cm.unraisable.exc_value, MyError) + + def test_clear_out_of_range_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"Invalid func watcher ID -1"): + _clear_func_watcher(-1) + with self.assertRaisesRegex(ValueError, r"Invalid func watcher ID 8"): + _clear_func_watcher(8) # FUNC_MAX_WATCHERS = 8 + + def test_clear_unassigned_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"No func watcher set for ID 1"): + _clear_func_watcher(1) diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c index f3a4f98b2757f2..e920e8abdf30ee 100644 --- a/Modules/_testcapi/func_events.c +++ b/Modules/_testcapi/func_events.c @@ -1,7 +1,8 @@ #include "parts.h" -static PyObject *pyfunc_callback = NULL; -static PyFunction_WatchCallback orig_callback = NULL; +static const int num_watchers = 2; +static PyObject *pyfunc_watchers[num_watchers]; +static int watcher_ids[num_watchers] = {-1, -1}; static PyObject *get_id(PyObject *obj) { PyObject *builtins = PyEval_GetBuiltins(); @@ -23,12 +24,12 @@ static PyObject *get_id(PyObject *obj) { return id; } -static void -call_pyfunc_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +static int +call_pyfunc_watcher(PyObject *watcher, PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) { PyObject *event_obj = PyLong_FromLong(event); if (event_obj == NULL) { - return; + return -1; } if (new_value == NULL) { new_value = Py_None; @@ -41,20 +42,39 @@ call_pyfunc_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *n if (func_or_id == NULL) { Py_DECREF(event_obj); Py_DECREF(new_value); - return; + return -1; } } else { Py_INCREF(func); func_or_id = (PyObject *) func; } PyObject *stack[] = {event_obj, func_or_id, new_value}; - PyObject *res = PyObject_Vectorcall(pyfunc_callback, stack, 3, NULL); + PyObject *res = PyObject_Vectorcall(watcher, stack, 3, NULL); + int st = (res == NULL) ? -1 : 0; Py_XDECREF(res); Py_DECREF(new_value); Py_DECREF(event_obj); Py_DECREF(func_or_id); + return st; +} + +static int +first_watcher_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +{ + return call_pyfunc_watcher(pyfunc_watchers[0], event, func, new_value); } +static int +second_watcher_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +{ + return call_pyfunc_watcher(pyfunc_watchers[1], event, func, new_value); +} + +static PyFunction_WatchCallback watcher_callbacks[num_watchers] = { + first_watcher_callback, + second_watcher_callback +}; + static int add_event(PyObject *module, const char *name, PyFunction_Event event) { @@ -68,38 +88,65 @@ add_event(PyObject *module, const char *name, PyFunction_Event event) } static PyObject * -set_func_watch_callback(PyObject *self, PyObject *func) +add_watcher(PyObject *self, PyObject *func) { if (!PyFunction_Check(func)) { PyErr_SetString(PyExc_TypeError, "'func' must be a function"); return NULL; } - if (pyfunc_callback != NULL) { - PyErr_SetString(PyExc_RuntimeError, "already set callback"); + int idx = -1; + for (int i = 0; i < num_watchers; i++) { + if (watcher_ids[i] == -1) { + idx = i; + break; + } + } + if (idx == -1) { + PyErr_SetString(PyExc_RuntimeError, "no free watchers"); + return NULL; + } + PyObject *result = PyLong_FromLong(idx); + if (result == NULL) { + return NULL; + } + watcher_ids[idx] = PyFunction_AddWatcher(watcher_callbacks[idx]); + if (watcher_ids[idx] < 0) { + Py_DECREF(result); return NULL; } + pyfunc_watchers[idx] = func; Py_INCREF(func); - pyfunc_callback = func; - orig_callback = PyFunction_GetWatchCallback(); - PyFunction_SetWatchCallback(call_pyfunc_callback); - Py_RETURN_NONE; + return result; } static PyObject * -restore_func_watch_callback(PyObject *self, PyObject *Py_UNUSED(ignored)) { - if (pyfunc_callback == NULL) { - PyErr_SetString(PyExc_RuntimeError, "nothing to restore"); +clear_watcher(PyObject *self, PyObject *watcher_id_obj) +{ + long watcher_id = PyLong_AsLong(watcher_id_obj); + if ((watcher_id < INT_MIN) || (watcher_id > INT_MAX)) { + PyErr_SetString(PyExc_ValueError, "invalid watcher id"); + return NULL; + } + int wid = (int) watcher_id; + if (PyFunction_ClearWatcher(wid) < 0) { return NULL; } - PyFunction_SetWatchCallback(orig_callback); - orig_callback = NULL; - Py_CLEAR(pyfunc_callback); + int idx = -1; + for (int i = 0; i < num_watchers; i++) { + if (watcher_ids[i] == wid) { + idx = i; + break; + } + } + assert(idx != -1); + Py_CLEAR(pyfunc_watchers[idx]); + watcher_ids[idx] = -1; Py_RETURN_NONE; } static PyMethodDef TestMethods[] = { - {"set_func_watch_callback", set_func_watch_callback, METH_O}, - {"restore_func_watch_callback", restore_func_watch_callback, METH_NOARGS}, + {"_add_func_watcher", add_watcher, METH_O}, + {"_clear_func_watcher", clear_watcher, METH_O}, {NULL}, }; @@ -118,5 +165,4 @@ _PyTestCapi_Init_FuncEvents(PyObject *m) { #undef ADD_EVENT return 0; - } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 1f7ff3033b8196..271ed8b77ca1a8 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals() +#include "pycore_function.h" // FUNC_MAX_WATCHERS #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "structmember.h" // PyMemberDef @@ -12,28 +13,45 @@ static uint32_t next_func_version = 1; static void handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) { - PyThreadState *tstate = _PyThreadState_GET(); - PyFunction_WatchCallback handle_event = tstate->interp->func_watch_callback; - if (handle_event == NULL) { - return; + PyInterpreterState *interp = _PyInterpreterState_GET(); + for (int i = 0; i < FUNC_MAX_WATCHERS; i++) { + PyFunction_WatchCallback cb = interp->func_watchers[i]; + if ((cb != NULL) && (cb(event, func, new_value) < 0)) { + PyErr_WriteUnraisable((PyObject *) func); + } } - handle_event(event, func, new_value); } -void -PyFunction_SetWatchCallback(PyFunction_WatchCallback callback) +int +PyFunction_AddWatcher(PyFunction_WatchCallback callback) { - PyThreadState *tstate = _PyThreadState_GET(); - assert(tstate->interp->_initialized); - tstate->interp->func_watch_callback = callback; + PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(interp->_initialized); + for (int i = 0; i < FUNC_MAX_WATCHERS; i++) { + if (interp->func_watchers[i] == NULL) { + interp->func_watchers[i] = callback; + return i; + } + } + PyErr_SetString(PyExc_RuntimeError, "no more func watcher IDs available"); + return -1; + } -PyFunction_WatchCallback -PyFunction_GetWatchCallback() +int +PyFunction_ClearWatcher(int watcher_id) { - PyThreadState *tstate = _PyThreadState_GET(); - assert(tstate->interp->_initialized); - return tstate->interp->func_watch_callback; + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (watcher_id < 0 || watcher_id >= FUNC_MAX_WATCHERS) { + PyErr_Format(PyExc_ValueError, "Invalid func watcher ID %d", watcher_id); + return -1; + } + if (!interp->func_watchers[watcher_id]) { + PyErr_Format(PyExc_ValueError, "No func watcher set for ID %d", watcher_id); + return -1; + } + interp->func_watchers[watcher_id] = NULL; + return 0; } PyFunctionObject * diff --git a/Python/pystate.c b/Python/pystate.c index 60332f7aa8d56e..e94a72b6287f4f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -430,7 +430,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->after_forkers_parent); Py_CLEAR(interp->after_forkers_child); #endif - interp->func_watch_callback = NULL; _PyAST_Fini(interp); _PyWarnings_Fini(interp); @@ -456,6 +455,10 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) interp->dict_watchers[i] = NULL; } + for (int i=0; i < FUNC_MAX_WATCHERS; i++) { + interp->func_watchers[i] = NULL; + } + // XXX Once we have one allocator per interpreter (i.e. // per-interpreter GC) we must ensure that all of the interpreter's // objects have been cleaned up at the point. From f357352ab718562f7ac51df82cf24a706734f99c Mon Sep 17 00:00:00 2001 From: Matt Page Date: Mon, 10 Oct 2022 16:46:49 -0700 Subject: [PATCH 150/151] Rename function events to match naming scheme used by dict watchers --- Include/cpython/funcobject.h | 6 +++--- Modules/_testcapi/func_events.c | 12 ++++++------ Objects/funcobject.c | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 06eb2b54ccf048..90582ff0c7616b 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -139,10 +139,10 @@ PyAPI_FUNC(PyObject *) PyStaticMethod_New(PyObject *); V(MODIFY_KWDEFAULTS) typedef enum { - #define DEF_EVENT(EVENT) PYFUNC_EVENT_##EVENT, + #define DEF_EVENT(EVENT) PyFunction_EVENT_##EVENT, FOREACH_FUNC_EVENT(DEF_EVENT) #undef DEF_EVENT -} PyFunction_Event; +} PyFunction_WatchEvent; /* * A callback that is invoked for different events in a function's lifecycle. @@ -157,7 +157,7 @@ typedef enum { * the function. Otherwise the third argument is NULL. */ typedef int (*PyFunction_WatchCallback)( - PyFunction_Event event, + PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value); diff --git a/Modules/_testcapi/func_events.c b/Modules/_testcapi/func_events.c index e920e8abdf30ee..6d9ba43749b344 100644 --- a/Modules/_testcapi/func_events.c +++ b/Modules/_testcapi/func_events.c @@ -25,7 +25,7 @@ static PyObject *get_id(PyObject *obj) { } static int -call_pyfunc_watcher(PyObject *watcher, PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +call_pyfunc_watcher(PyObject *watcher, PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) { PyObject *event_obj = PyLong_FromLong(event); if (event_obj == NULL) { @@ -36,7 +36,7 @@ call_pyfunc_watcher(PyObject *watcher, PyFunction_Event event, PyFunctionObject } Py_INCREF(new_value); PyObject *func_or_id = NULL; - if (event == PYFUNC_EVENT_DESTROY) { + if (event == PyFunction_EVENT_DESTROY) { /* Don't expose a function that's about to be destroyed to managed code */ func_or_id = get_id((PyObject *) func); if (func_or_id == NULL) { @@ -59,13 +59,13 @@ call_pyfunc_watcher(PyObject *watcher, PyFunction_Event event, PyFunctionObject } static int -first_watcher_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +first_watcher_callback(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) { return call_pyfunc_watcher(pyfunc_watchers[0], event, func, new_value); } static int -second_watcher_callback(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +second_watcher_callback(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) { return call_pyfunc_watcher(pyfunc_watchers[1], event, func, new_value); } @@ -76,7 +76,7 @@ static PyFunction_WatchCallback watcher_callbacks[num_watchers] = { }; static int -add_event(PyObject *module, const char *name, PyFunction_Event event) +add_event(PyObject *module, const char *name, PyFunction_WatchEvent event) { PyObject *value = PyLong_FromLong(event); if (value == NULL) { @@ -158,7 +158,7 @@ _PyTestCapi_Init_FuncEvents(PyObject *m) { /* Expose each event as an attribute on the module */ #define ADD_EVENT(event) \ - if (add_event(m, "PYFUNC_EVENT_" #event, PYFUNC_EVENT_##event)) { \ + if (add_event(m, "PYFUNC_EVENT_" #event, PyFunction_EVENT_##event)) { \ return -1; \ } FOREACH_FUNC_EVENT(ADD_EVENT); diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 271ed8b77ca1a8..c19b08ac9748be 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -11,7 +11,7 @@ static uint32_t next_func_version = 1; static void -handle_func_event(PyFunction_Event event, PyFunctionObject *func, PyObject *new_value) +handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) { PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < FUNC_MAX_WATCHERS; i++) { @@ -85,7 +85,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->vectorcall = _PyFunction_Vectorcall; op->func_version = 0; _PyObject_GC_TRACK(op); - handle_func_event(PYFUNC_EVENT_CREATED, op, NULL); + handle_func_event(PyFunction_EVENT_CREATED, op, NULL); return op; } @@ -162,7 +162,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->vectorcall = _PyFunction_Vectorcall; op->func_version = 0; _PyObject_GC_TRACK(op); - handle_func_event(PYFUNC_EVENT_CREATED, op, NULL); + handle_func_event(PyFunction_EVENT_CREATED, op, NULL); return (PyObject *)op; error: @@ -448,7 +448,7 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored)) nclosure, nfree); return -1; } - handle_func_event(PYFUNC_EVENT_MODIFY_CODE, op, value); + handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value); op->func_version = 0; Py_INCREF(value); Py_XSETREF(op->func_code, value); @@ -534,7 +534,7 @@ func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored return -1; } - handle_func_event(PYFUNC_EVENT_MODIFY_DEFAULTS, op, value); + handle_func_event(PyFunction_EVENT_MODIFY_DEFAULTS, op, value); op->func_version = 0; Py_XINCREF(value); Py_XSETREF(op->func_defaults, value); @@ -577,7 +577,7 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor return -1; } - handle_func_event(PYFUNC_EVENT_MODIFY_KWDEFAULTS, op, value); + handle_func_event(PyFunction_EVENT_MODIFY_KWDEFAULTS, op, value); op->func_version = 0; Py_XINCREF(value); Py_XSETREF(op->func_kwdefaults, value); @@ -765,7 +765,7 @@ func_clear(PyFunctionObject *op) static void func_dealloc(PyFunctionObject *op) { - handle_func_event(PYFUNC_EVENT_DESTROY, op, NULL); + handle_func_event(PyFunction_EVENT_DESTROY, op, NULL); _PyObject_GC_UNTRACK(op); if (op->func_weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) op); From 08877e651b37a59609ff39f5661b83dcfac9aca2 Mon Sep 17 00:00:00 2001 From: Matt Page Date: Mon, 10 Oct 2022 17:01:15 -0700 Subject: [PATCH 151/151] Add documentation for the new C-API functions and types --- Doc/c-api/function.rst | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index df88e85e518829..a161fb59b91765 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -118,3 +118,56 @@ There are a few functions specific to Python functions. must be a dictionary or ``Py_None``. Raises :exc:`SystemError` and returns ``-1`` on failure. + + +.. c:function:: int PyFunction_AddWatcher(PyFunction_WatchCallback callback) + + Register *callback* as a function watcher for the current interpreter. Returns + an id which may be passed to :c:func:`PyFunction_ClearWatcher`. In case + of error (e.g. no more watcher IDs available), return ``-1`` and set an + exception. + + .. versionadded:: 3.12 + + +.. c:function:: int PyFunction_ClearWatcher(int watcher_id) + + Clear watcher identified by *watcher_id* previously returned from + :c:func:`PyFunction_AddWatcher` for the current interpreter. Return ``0`` on + success or ``-1`` on error (e.g. if the given *watcher_id* was never + registered.) + + .. versionadded:: 3.12 + + +.. c:type:: PyFunction_WatchEvent + + Enumeration of possible function watcher events: ``PyFunction_EVENT_CREATED``, + ``PyFunction_EVENT_DESTROY``, ``PyFunction_EVENT_MODIFY_CODE``, + ``PyFunction_EVENT_MODIFY_DEFAULTS``, or ``PyFunction_EVENT_MODIFY_KWDEFAULTS``. + + .. versionadded:: 3.12 + + +.. c:type:: int (*PyFunction_WatchCallback)(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) + + Type of a function watcher callback function. + + If *event* is ``PyFunction_EVENT_CREATED`` or ``PyFunction_EVENT_DESTROY`` + then *new_value* will be ``NULL``. Otherwise, *new_value* will hold a + borrowed reference to the new value that is about to be stored in *func* for + the attribute that is being modified. + + The callback may inspect but must not modify *func*; doing so could have + unpredictable effects, including infinite recursion. + + If *event* is ``PyFunction_EVENT_CREATED``, then the callback is invoked + after `func` has been fully initialized. Otherwise, the callback is invoked + before the modification to *func* takes place, so the prior state of *func* + can be inspected. + + If the callback returns with an exception set, it must return ``-1``; this + exception will be printed as an unraisable exception using + :c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``. + + .. versionadded:: 3.12