bpo-38005: Fixed comparing and creating of InterpreterID and ChannelID. (GH-15652)
* Fix a crash in comparing with float (and maybe other crashes).
* They are now never equal to strings and non-integer numbers.
* Comparison with a large number no longer raises OverflowError.
* Arbitrary exceptions no longer silenced in constructors and comparisons.
* TypeError raised in the constructor contains now the name of the type.
* Accept only ChannelID and int-like objects in channel functions.
* Accept only InterpreterId, int-like objects and str in the InterpreterId constructor.
* Accept int-like objects, not just int in interpreter related functions.
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 19d98fd..7842947 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -1405,6 +1405,34 @@
_channels *channels;
} channelid;
+static int
+channel_id_converter(PyObject *arg, void *ptr)
+{
+ int64_t cid;
+ if (PyObject_TypeCheck(arg, &ChannelIDtype)) {
+ cid = ((channelid *)arg)->id;
+ }
+ else if (PyIndex_Check(arg)) {
+ cid = PyLong_AsLongLong(arg);
+ if (cid == -1 && PyErr_Occurred()) {
+ return 0;
+ }
+ if (cid < 0) {
+ PyErr_Format(PyExc_ValueError,
+ "channel ID must be a non-negative int, got %R", arg);
+ return 0;
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "channel ID must be an int, got %.100s",
+ arg->ob_type->tp_name);
+ return 0;
+ }
+ *(int64_t *)ptr = cid;
+ return 1;
+}
+
static channelid *
newchannelid(PyTypeObject *cls, int64_t cid, int end, _channels *channels,
int force, int resolve)
@@ -1437,28 +1465,16 @@
channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL};
- PyObject *id;
+ int64_t cid;
int send = -1;
int recv = -1;
int force = 0;
int resolve = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O|$pppp:ChannelID.__new__", kwlist,
- &id, &send, &recv, &force, &resolve))
+ "O&|$pppp:ChannelID.__new__", kwlist,
+ channel_id_converter, &cid, &send, &recv, &force, &resolve))
return NULL;
- // Coerce and check the ID.
- int64_t cid;
- if (PyObject_TypeCheck(id, &ChannelIDtype)) {
- cid = ((channelid *)id)->id;
- }
- else {
- cid = _Py_CoerceID(id);
- if (cid < 0) {
- return NULL;
- }
- }
-
// Handle "send" and "recv".
if (send == 0 && recv == 0) {
PyErr_SetString(PyExc_ValueError,
@@ -1592,30 +1608,28 @@
int equal;
if (PyObject_TypeCheck(other, &ChannelIDtype)) {
channelid *othercid = (channelid *)other;
- if (cid->end != othercid->end) {
- equal = 0;
- }
- else {
- equal = (cid->id == othercid->id);
- }
+ equal = (cid->end == othercid->end) && (cid->id == othercid->id);
}
- else {
- other = PyNumber_Long(other);
- if (other == NULL) {
- PyErr_Clear();
- Py_RETURN_NOTIMPLEMENTED;
- }
- int64_t othercid = PyLong_AsLongLong(other);
- Py_DECREF(other);
- if (othercid == -1 && PyErr_Occurred() != NULL) {
+ else if (PyLong_Check(other)) {
+ /* Fast path */
+ int overflow;
+ long long othercid = PyLong_AsLongLongAndOverflow(other, &overflow);
+ if (othercid == -1 && PyErr_Occurred()) {
return NULL;
}
- if (othercid < 0) {
- equal = 0;
+ equal = !overflow && (othercid >= 0) && (cid->id == othercid);
+ }
+ else if (PyNumber_Check(other)) {
+ PyObject *pyid = PyLong_FromLongLong(cid->id);
+ if (pyid == NULL) {
+ return NULL;
}
- else {
- equal = (cid->id == othercid);
- }
+ PyObject *res = PyObject_RichCompare(pyid, other, op);
+ Py_DECREF(pyid);
+ return res;
+ }
+ else {
+ Py_RETURN_NOTIMPLEMENTED;
}
if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) {
@@ -1754,8 +1768,7 @@
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
- Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
channelid_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -2017,10 +2030,6 @@
"O:destroy", kwlist, &id)) {
return NULL;
}
- if (!PyLong_Check(id)) {
- PyErr_SetString(PyExc_TypeError, "ID must be an int");
- return NULL;
- }
// Look up the interpreter.
PyInterpreterState *interp = _PyInterpreterID_LookUp(id);
@@ -2145,10 +2154,6 @@
&id, &code, &shared)) {
return NULL;
}
- if (!PyLong_Check(id)) {
- PyErr_SetString(PyExc_TypeError, "first arg (ID) must be an int");
- return NULL;
- }
// Look up the interpreter.
PyInterpreterState *interp = _PyInterpreterID_LookUp(id);
@@ -2216,10 +2221,6 @@
"O:is_running", kwlist, &id)) {
return NULL;
}
- if (!PyLong_Check(id)) {
- PyErr_SetString(PyExc_TypeError, "ID must be an int");
- return NULL;
- }
PyInterpreterState *interp = _PyInterpreterID_LookUp(id);
if (interp == NULL) {
@@ -2268,13 +2269,9 @@
channel_destroy(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"cid", NULL};
- PyObject *id;
- if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O:channel_destroy", kwlist, &id)) {
- return NULL;
- }
- int64_t cid = _Py_CoerceID(id);
- if (cid < 0) {
+ int64_t cid;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_destroy", kwlist,
+ channel_id_converter, &cid)) {
return NULL;
}
@@ -2331,14 +2328,10 @@
channel_send(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"cid", "obj", NULL};
- PyObject *id;
+ int64_t cid;
PyObject *obj;
- if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "OO:channel_send", kwlist, &id, &obj)) {
- return NULL;
- }
- int64_t cid = _Py_CoerceID(id);
- if (cid < 0) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O:channel_send", kwlist,
+ channel_id_converter, &cid, &obj)) {
return NULL;
}
@@ -2357,13 +2350,9 @@
channel_recv(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"cid", NULL};
- PyObject *id;
- if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O:channel_recv", kwlist, &id)) {
- return NULL;
- }
- int64_t cid = _Py_CoerceID(id);
- if (cid < 0) {
+ int64_t cid;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_recv", kwlist,
+ channel_id_converter, &cid)) {
return NULL;
}
@@ -2379,17 +2368,13 @@
channel_close(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"cid", "send", "recv", "force", NULL};
- PyObject *id;
+ int64_t cid;
int send = 0;
int recv = 0;
int force = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O|$ppp:channel_close", kwlist,
- &id, &send, &recv, &force)) {
- return NULL;
- }
- int64_t cid = _Py_CoerceID(id);
- if (cid < 0) {
+ "O&|$ppp:channel_close", kwlist,
+ channel_id_converter, &cid, &send, &recv, &force)) {
return NULL;
}
@@ -2431,17 +2416,13 @@
{
// Note that only the current interpreter is affected.
static char *kwlist[] = {"cid", "send", "recv", "force", NULL};
- PyObject *id;
+ int64_t cid;
int send = 0;
int recv = 0;
int force = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O|$ppp:channel_release", kwlist,
- &id, &send, &recv, &force)) {
- return NULL;
- }
- int64_t cid = _Py_CoerceID(id);
- if (cid < 0) {
+ "O&|$ppp:channel_release", kwlist,
+ channel_id_converter, &cid, &send, &recv, &force)) {
return NULL;
}
if (send == 0 && recv == 0) {
@@ -2538,7 +2519,6 @@
}
/* Initialize types */
- ChannelIDtype.tp_base = &PyLong_Type;
if (PyType_Ready(&ChannelIDtype) != 0) {
return NULL;
}