aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Modules/_interpchannelsmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_interpchannelsmodule.c')
-rw-r--r--Modules/_interpchannelsmodule.c344
1 files changed, 274 insertions, 70 deletions
diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c
index f0447475c49..a8b4a8d76b0 100644
--- a/Modules/_interpchannelsmodule.c
+++ b/Modules/_interpchannelsmodule.c
@@ -18,7 +18,9 @@
#endif
#define REGISTERS_HEAP_TYPES
+#define HAS_UNBOUND_ITEMS
#include "_interpreters_common.h"
+#undef HAS_UNBOUND_ITEMS
#undef REGISTERS_HEAP_TYPES
@@ -511,8 +513,14 @@ _waiting_finish_releasing(_waiting_t *waiting)
struct _channelitem;
typedef struct _channelitem {
+ /* The interpreter that added the item to the queue.
+ The actual bound interpid is found in item->data.
+ This is necessary because item->data might be NULL,
+ meaning the interpreter has been destroyed. */
+ int64_t interpid;
_PyCrossInterpreterData *data;
_waiting_t *waiting;
+ int unboundop;
struct _channelitem *next;
} _channelitem;
@@ -524,11 +532,22 @@ _channelitem_ID(_channelitem *item)
static void
_channelitem_init(_channelitem *item,
- _PyCrossInterpreterData *data, _waiting_t *waiting)
+ int64_t interpid, _PyCrossInterpreterData *data,
+ _waiting_t *waiting, int unboundop)
{
+ if (interpid < 0) {
+ interpid = _get_interpid(data);
+ }
+ else {
+ assert(data == NULL
+ || _PyCrossInterpreterData_INTERPID(data) < 0
+ || interpid == _PyCrossInterpreterData_INTERPID(data));
+ }
*item = (_channelitem){
+ .interpid = interpid,
.data = data,
.waiting = waiting,
+ .unboundop = unboundop,
};
if (waiting != NULL) {
waiting->itemid = _channelitem_ID(item);
@@ -536,17 +555,15 @@ _channelitem_init(_channelitem *item,
}
static void
-_channelitem_clear(_channelitem *item)
+_channelitem_clear_data(_channelitem *item, int removed)
{
- item->next = NULL;
-
if (item->data != NULL) {
// It was allocated in channel_send().
(void)_release_xid_data(item->data, XID_IGNORE_EXC & XID_FREE);
item->data = NULL;
}
- if (item->waiting != NULL) {
+ if (item->waiting != NULL && removed) {
if (item->waiting->status == WAITING_ACQUIRED) {
_waiting_release(item->waiting, 0);
}
@@ -554,15 +571,23 @@ _channelitem_clear(_channelitem *item)
}
}
+static void
+_channelitem_clear(_channelitem *item)
+{
+ item->next = NULL;
+ _channelitem_clear_data(item, 1);
+}
+
static _channelitem *
-_channelitem_new(_PyCrossInterpreterData *data, _waiting_t *waiting)
+_channelitem_new(int64_t interpid, _PyCrossInterpreterData *data,
+ _waiting_t *waiting, int unboundop)
{
_channelitem *item = GLOBAL_MALLOC(_channelitem);
if (item == NULL) {
PyErr_NoMemory();
return NULL;
}
- _channelitem_init(item, data, waiting);
+ _channelitem_init(item, interpid, data, waiting, unboundop);
return item;
}
@@ -585,17 +610,48 @@ _channelitem_free_all(_channelitem *item)
static void
_channelitem_popped(_channelitem *item,
- _PyCrossInterpreterData **p_data, _waiting_t **p_waiting)
+ _PyCrossInterpreterData **p_data, _waiting_t **p_waiting,
+ int *p_unboundop)
{
assert(item->waiting == NULL || item->waiting->status == WAITING_ACQUIRED);
*p_data = item->data;
*p_waiting = item->waiting;
+ *p_unboundop = item->unboundop;
// We clear them here, so they won't be released in _channelitem_clear().
item->data = NULL;
item->waiting = NULL;
_channelitem_free(item);
}
+static int
+_channelitem_clear_interpreter(_channelitem *item)
+{
+ assert(item->interpid >= 0);
+ if (item->data == NULL) {
+ // Its interpreter was already cleared (or it was never bound).
+ // For UNBOUND_REMOVE it should have been freed at that time.
+ assert(item->unboundop != UNBOUND_REMOVE);
+ return 0;
+ }
+ assert(_PyCrossInterpreterData_INTERPID(item->data) == item->interpid);
+
+ switch (item->unboundop) {
+ case UNBOUND_REMOVE:
+ // The caller must free/clear it.
+ return 1;
+ case UNBOUND_ERROR:
+ case UNBOUND_REPLACE:
+ // We won't need the cross-interpreter data later
+ // so we completely throw it away.
+ _channelitem_clear_data(item, 0);
+ return 0;
+ default:
+ Py_FatalError("not reachable");
+ return -1;
+ }
+}
+
+
typedef struct _channelqueue {
int64_t count;
_channelitem *first;
@@ -634,9 +690,10 @@ _channelqueue_free(_channelqueue *queue)
static int
_channelqueue_put(_channelqueue *queue,
- _PyCrossInterpreterData *data, _waiting_t *waiting)
+ int64_t interpid, _PyCrossInterpreterData *data,
+ _waiting_t *waiting, int unboundop)
{
- _channelitem *item = _channelitem_new(data, waiting);
+ _channelitem *item = _channelitem_new(interpid, data, waiting, unboundop);
if (item == NULL) {
return -1;
}
@@ -659,7 +716,8 @@ _channelqueue_put(_channelqueue *queue,
static int
_channelqueue_get(_channelqueue *queue,
- _PyCrossInterpreterData **p_data, _waiting_t **p_waiting)
+ _PyCrossInterpreterData **p_data, _waiting_t **p_waiting,
+ int *p_unboundop)
{
_channelitem *item = queue->first;
if (item == NULL) {
@@ -671,7 +729,7 @@ _channelqueue_get(_channelqueue *queue,
}
queue->count -= 1;
- _channelitem_popped(item, p_data, p_waiting);
+ _channelitem_popped(item, p_data, p_waiting, p_unboundop);
return 0;
}
@@ -737,7 +795,8 @@ _channelqueue_remove(_channelqueue *queue, _channelitem_id_t itemid,
}
queue->count -= 1;
- _channelitem_popped(item, p_data, p_waiting);
+ int unboundop;
+ _channelitem_popped(item, p_data, p_waiting, &unboundop);
}
static void
@@ -748,14 +807,17 @@ _channelqueue_clear_interpreter(_channelqueue *queue, int64_t interpid)
while (next != NULL) {
_channelitem *item = next;
next = item->next;
- if (_PyCrossInterpreterData_INTERPID(item->data) == interpid) {
+ int remove = (item->interpid == interpid)
+ ? _channelitem_clear_interpreter(item)
+ : 0;
+ if (remove) {
+ _channelitem_free(item);
if (prev == NULL) {
- queue->first = item->next;
+ queue->first = next;
}
else {
- prev->next = item->next;
+ prev->next = next;
}
- _channelitem_free(item);
queue->count -= 1;
}
else {
@@ -1018,12 +1080,15 @@ typedef struct _channel {
PyThread_type_lock mutex;
_channelqueue *queue;
_channelends *ends;
+ struct {
+ int unboundop;
+ } defaults;
int open;
struct _channel_closing *closing;
} _channel_state;
static _channel_state *
-_channel_new(PyThread_type_lock mutex)
+_channel_new(PyThread_type_lock mutex, int unboundop)
{
_channel_state *chan = GLOBAL_MALLOC(_channel_state);
if (chan == NULL) {
@@ -1041,6 +1106,7 @@ _channel_new(PyThread_type_lock mutex)
GLOBAL_FREE(chan);
return NULL;
}
+ chan->defaults.unboundop = unboundop;
chan->open = 1;
chan->closing = NULL;
return chan;
@@ -1061,7 +1127,8 @@ _channel_free(_channel_state *chan)
static int
_channel_add(_channel_state *chan, int64_t interpid,
- _PyCrossInterpreterData *data, _waiting_t *waiting)
+ _PyCrossInterpreterData *data, _waiting_t *waiting,
+ int unboundop)
{
int res = -1;
PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
@@ -1075,7 +1142,7 @@ _channel_add(_channel_state *chan, int64_t interpid,
goto done;
}
- if (_channelqueue_put(chan->queue, data, waiting) != 0) {
+ if (_channelqueue_put(chan->queue, interpid, data, waiting, unboundop) != 0) {
goto done;
}
// Any errors past this point must cause a _waiting_release() call.
@@ -1088,7 +1155,8 @@ done:
static int
_channel_next(_channel_state *chan, int64_t interpid,
- _PyCrossInterpreterData **p_data, _waiting_t **p_waiting)
+ _PyCrossInterpreterData **p_data, _waiting_t **p_waiting,
+ int *p_unboundop)
{
int err = 0;
PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
@@ -1102,11 +1170,15 @@ _channel_next(_channel_state *chan, int64_t interpid,
goto done;
}
- int empty = _channelqueue_get(chan->queue, p_data, p_waiting);
- assert(empty == 0 || empty == ERR_CHANNEL_EMPTY);
+ int empty = _channelqueue_get(chan->queue, p_data, p_waiting, p_unboundop);
assert(!PyErr_Occurred());
- if (empty && chan->closing != NULL) {
- chan->open = 0;
+ if (empty) {
+ assert(empty == ERR_CHANNEL_EMPTY);
+ if (chan->closing != NULL) {
+ chan->open = 0;
+ }
+ err = ERR_CHANNEL_EMPTY;
+ goto done;
}
done:
@@ -1528,18 +1600,27 @@ done:
PyThread_release_lock(channels->mutex);
}
-static int64_t *
+struct channel_id_and_info {
+ int64_t id;
+ int unboundop;
+};
+
+static struct channel_id_and_info *
_channels_list_all(_channels *channels, int64_t *count)
{
- int64_t *cids = NULL;
+ struct channel_id_and_info *cids = NULL;
PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
- int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen));
+ struct channel_id_and_info *ids =
+ PyMem_NEW(struct channel_id_and_info, (Py_ssize_t)(channels->numopen));
if (ids == NULL) {
goto done;
}
_channelref *ref = channels->head;
for (int64_t i=0; ref != NULL; ref = ref->next, i++) {
- ids[i] = ref->cid;
+ ids[i] = (struct channel_id_and_info){
+ .id = ref->cid,
+ .unboundop = ref->chan->defaults.unboundop,
+ };
}
*count = channels->numopen;
@@ -1624,13 +1705,13 @@ _channel_finish_closing(_channel_state *chan) {
// Create a new channel.
static int64_t
-channel_create(_channels *channels)
+channel_create(_channels *channels, int unboundop)
{
PyThread_type_lock mutex = PyThread_allocate_lock();
if (mutex == NULL) {
return ERR_CHANNEL_MUTEX_INIT;
}
- _channel_state *chan = _channel_new(mutex);
+ _channel_state *chan = _channel_new(mutex, unboundop);
if (chan == NULL) {
PyThread_free_lock(mutex);
return -1;
@@ -1662,7 +1743,7 @@ channel_destroy(_channels *channels, int64_t cid)
// Optionally request to be notified when it is received.
static int
channel_send(_channels *channels, int64_t cid, PyObject *obj,
- _waiting_t *waiting)
+ _waiting_t *waiting, int unboundop)
{
PyInterpreterState *interp = _get_current_interp();
if (interp == NULL) {
@@ -1698,7 +1779,7 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj,
}
// Add the data to the channel.
- int res = _channel_add(chan, interpid, data, waiting);
+ int res = _channel_add(chan, interpid, data, waiting, unboundop);
PyThread_release_lock(mutex);
if (res != 0) {
// We may chain an exception here:
@@ -1735,7 +1816,7 @@ channel_clear_sent(_channels *channels, int64_t cid, _waiting_t *waiting)
// Like channel_send(), but strictly wait for the object to be received.
static int
channel_send_wait(_channels *channels, int64_t cid, PyObject *obj,
- PY_TIMEOUT_T timeout)
+ int unboundop, PY_TIMEOUT_T timeout)
{
// We use a stack variable here, so we must ensure that &waiting
// is not held by any channel item at the point this function exits.
@@ -1746,7 +1827,7 @@ channel_send_wait(_channels *channels, int64_t cid, PyObject *obj,
}
/* Queue up the object. */
- int res = channel_send(channels, cid, obj, &waiting);
+ int res = channel_send(channels, cid, obj, &waiting, unboundop);
if (res < 0) {
assert(waiting.status == WAITING_NO_STATUS);
goto finally;
@@ -1788,7 +1869,7 @@ finally:
// The current interpreter gets associated with the recv end of the channel.
// XXX Support a "wait" mutex?
static int
-channel_recv(_channels *channels, int64_t cid, PyObject **res)
+channel_recv(_channels *channels, int64_t cid, PyObject **res, int *p_unboundop)
{
int err;
*res = NULL;
@@ -1816,13 +1897,15 @@ channel_recv(_channels *channels, int64_t cid, PyObject **res)
// Pop off the next item from the channel.
_PyCrossInterpreterData *data = NULL;
_waiting_t *waiting = NULL;
- err = _channel_next(chan, interpid, &data, &waiting);
+ err = _channel_next(chan, interpid, &data, &waiting, p_unboundop);
PyThread_release_lock(mutex);
if (err != 0) {
return err;
}
else if (data == NULL) {
+ // The item was unbound.
assert(!PyErr_Occurred());
+ *res = NULL;
return 0;
}
@@ -1915,6 +1998,23 @@ channel_is_associated(_channels *channels, int64_t cid, int64_t interpid,
return (end != NULL && end->open);
}
+static int
+_channel_get_count(_channels *channels, int64_t cid, Py_ssize_t *p_count)
+{
+ PyThread_type_lock mutex = NULL;
+ _channel_state *chan = NULL;
+ int err = _channels_lookup(channels, cid, &mutex, &chan);
+ if (err != 0) {
+ return err;
+ }
+ assert(chan != NULL);
+ int64_t count = chan->queue->count;
+ PyThread_release_lock(mutex);
+
+ *p_count = (Py_ssize_t)count;
+ return 0;
+}
+
/* channel info */
@@ -2767,9 +2867,22 @@ clear_interpreter(void *data)
static PyObject *
-channelsmod_create(PyObject *self, PyObject *Py_UNUSED(ignored))
+channelsmod_create(PyObject *self, PyObject *args, PyObject *kwds)
{
- int64_t cid = channel_create(&_globals.channels);
+ static char *kwlist[] = {"unboundop", NULL};
+ int unboundop;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:create", kwlist,
+ &unboundop))
+ {
+ return NULL;
+ }
+ if (!check_unbound(unboundop)) {
+ PyErr_Format(PyExc_ValueError,
+ "unsupported unboundop %d", unboundop);
+ return NULL;
+ }
+
+ int64_t cid = channel_create(&_globals.channels, unboundop);
if (cid < 0) {
(void)handle_channel_error(-1, self, cid);
return NULL;
@@ -2796,7 +2909,7 @@ channelsmod_create(PyObject *self, PyObject *Py_UNUSED(ignored))
}
PyDoc_STRVAR(channelsmod_create_doc,
-"channel_create() -> cid\n\
+"channel_create(unboundop) -> cid\n\
\n\
Create a new cross-interpreter channel and return a unique generated ID.");
@@ -2831,7 +2944,8 @@ static PyObject *
channelsmod_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))
{
int64_t count = 0;
- int64_t *cids = _channels_list_all(&_globals.channels, &count);
+ struct channel_id_and_info *cids =
+ _channels_list_all(&_globals.channels, &count);
if (cids == NULL) {
if (count == 0) {
return PyList_New(0);
@@ -2848,19 +2962,26 @@ channelsmod_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))
ids = NULL;
goto finally;
}
- int64_t *cur = cids;
+ struct channel_id_and_info *cur = cids;
for (int64_t i=0; i < count; cur++, i++) {
PyObject *cidobj = NULL;
- int err = newchannelid(state->ChannelIDType, *cur, 0,
+ int err = newchannelid(state->ChannelIDType, cur->id, 0,
&_globals.channels, 0, 0,
(channelid **)&cidobj);
- if (handle_channel_error(err, self, *cur)) {
+ if (handle_channel_error(err, self, cur->id)) {
assert(cidobj == NULL);
Py_SETREF(ids, NULL);
break;
}
assert(cidobj != NULL);
- PyList_SET_ITEM(ids, (Py_ssize_t)i, cidobj);
+
+ PyObject *item = Py_BuildValue("Oi", cidobj, cur->unboundop);
+ Py_DECREF(cidobj);
+ if (item == NULL) {
+ Py_SETREF(ids, NULL);
+ break;
+ }
+ PyList_SET_ITEM(ids, (Py_ssize_t)i, item);
}
finally:
@@ -2942,16 +3063,24 @@ receive end.");
static PyObject *
channelsmod_send(PyObject *self, PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"cid", "obj", "blocking", "timeout", NULL};
+ static char *kwlist[] = {"cid", "obj", "unboundop", "blocking", "timeout",
+ NULL};
struct channel_id_converter_data cid_data = {
.module = self,
};
PyObject *obj;
+ int unboundop = UNBOUND_REPLACE;
int blocking = 1;
PyObject *timeout_obj = NULL;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|$pO:channel_send", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|i$pO:channel_send", kwlist,
channel_id_converter, &cid_data, &obj,
- &blocking, &timeout_obj)) {
+ &unboundop, &blocking, &timeout_obj))
+ {
+ return NULL;
+ }
+ if (!check_unbound(unboundop)) {
+ PyErr_Format(PyExc_ValueError,
+ "unsupported unboundop %d", unboundop);
return NULL;
}
@@ -2964,10 +3093,10 @@ channelsmod_send(PyObject *self, PyObject *args, PyObject *kwds)
/* Queue up the object. */
int err = 0;
if (blocking) {
- err = channel_send_wait(&_globals.channels, cid, obj, timeout);
+ err = channel_send_wait(&_globals.channels, cid, obj, unboundop, timeout);
}
else {
- err = channel_send(&_globals.channels, cid, obj, NULL);
+ err = channel_send(&_globals.channels, cid, obj, NULL, unboundop);
}
if (handle_channel_error(err, self, cid)) {
return NULL;
@@ -2985,17 +3114,24 @@ By default this waits for the object to be received.");
static PyObject *
channelsmod_send_buffer(PyObject *self, PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"cid", "obj", "blocking", "timeout", NULL};
+ static char *kwlist[] = {"cid", "obj", "unboundop", "blocking", "timeout",
+ NULL};
struct channel_id_converter_data cid_data = {
.module = self,
};
PyObject *obj;
+ int unboundop = UNBOUND_REPLACE;
int blocking = 1;
PyObject *timeout_obj = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O&O|$pO:channel_send_buffer", kwlist,
+ "O&O|i$pO:channel_send_buffer", kwlist,
channel_id_converter, &cid_data, &obj,
- &blocking, &timeout_obj)) {
+ &unboundop, &blocking, &timeout_obj)) {
+ return NULL;
+ }
+ if (!check_unbound(unboundop)) {
+ PyErr_Format(PyExc_ValueError,
+ "unsupported unboundop %d", unboundop);
return NULL;
}
@@ -3013,10 +3149,11 @@ channelsmod_send_buffer(PyObject *self, PyObject *args, PyObject *kwds)
/* Queue up the object. */
int err = 0;
if (blocking) {
- err = channel_send_wait(&_globals.channels, cid, tempobj, timeout);
+ err = channel_send_wait(
+ &_globals.channels, cid, tempobj, unboundop, timeout);
}
else {
- err = channel_send(&_globals.channels, cid, tempobj, NULL);
+ err = channel_send(&_globals.channels, cid, tempobj, NULL, unboundop);
}
Py_DECREF(tempobj);
if (handle_channel_error(err, self, cid)) {
@@ -3048,25 +3185,28 @@ channelsmod_recv(PyObject *self, PyObject *args, PyObject *kwds)
cid = cid_data.cid;
PyObject *obj = NULL;
- int err = channel_recv(&_globals.channels, cid, &obj);
- if (handle_channel_error(err, self, cid)) {
- return NULL;
- }
- Py_XINCREF(dflt);
- if (obj == NULL) {
+ int unboundop = 0;
+ int err = channel_recv(&_globals.channels, cid, &obj, &unboundop);
+ if (err == ERR_CHANNEL_EMPTY && dflt != NULL) {
// Use the default.
- if (dflt == NULL) {
- (void)handle_channel_error(ERR_CHANNEL_EMPTY, self, cid);
- return NULL;
- }
obj = Py_NewRef(dflt);
+ err = 0;
}
- Py_XDECREF(dflt);
- return obj;
+ else if (handle_channel_error(err, self, cid)) {
+ return NULL;
+ }
+ else if (obj == NULL) {
+ // The item was unbound.
+ return Py_BuildValue("Oi", Py_None, unboundop);
+ }
+
+ PyObject *res = Py_BuildValue("OO", obj, Py_None);
+ Py_DECREF(obj);
+ return res;
}
PyDoc_STRVAR(channelsmod_recv_doc,
-"channel_recv(cid, [default]) -> obj\n\
+"channel_recv(cid, [default]) -> (obj, unboundop)\n\
\n\
Return a new object from the data at the front of the channel's queue.\n\
\n\
@@ -3168,6 +3308,34 @@ Close the channel for the current interpreter. 'send' and 'recv'\n\
ends are closed. Closing an already closed end is a noop.");
static PyObject *
+channelsmod_get_count(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cid", NULL};
+ struct channel_id_converter_data cid_data = {
+ .module = self,
+ };
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O&:get_count", kwlist,
+ channel_id_converter, &cid_data)) {
+ return NULL;
+ }
+ int64_t cid = cid_data.cid;
+
+ Py_ssize_t count = -1;
+ int err = _channel_get_count(&_globals.channels, cid, &count);
+ if (handle_channel_error(err, self, cid)) {
+ return NULL;
+ }
+ assert(count >= 0);
+ return PyLong_FromSsize_t(count);
+}
+
+PyDoc_STRVAR(channelsmod_get_count_doc,
+"get_count(cid)\n\
+\n\
+Return the number of items in the channel.");
+
+static PyObject *
channelsmod_get_info(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"cid", NULL};
@@ -3195,6 +3363,38 @@ PyDoc_STRVAR(channelsmod_get_info_doc,
Return details about the channel.");
static PyObject *
+channelsmod_get_channel_defaults(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cid", NULL};
+ struct channel_id_converter_data cid_data = {
+ .module = self,
+ };
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O&:get_channel_defaults", kwlist,
+ channel_id_converter, &cid_data)) {
+ return NULL;
+ }
+ int64_t cid = cid_data.cid;
+
+ PyThread_type_lock mutex = NULL;
+ _channel_state *channel = NULL;
+ int err = _channels_lookup(&_globals.channels, cid, &mutex, &channel);
+ if (handle_channel_error(err, self, cid)) {
+ return NULL;
+ }
+ int unboundop = channel->defaults.unboundop;
+ PyThread_release_lock(mutex);
+
+ PyObject *defaults = Py_BuildValue("i", unboundop);
+ return defaults;
+}
+
+PyDoc_STRVAR(channelsmod_get_channel_defaults_doc,
+"get_channel_defaults(cid)\n\
+\n\
+Return the channel's default values, set when it was created.");
+
+static PyObject *
channelsmod__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
{
module_state *state = get_module_state(self);
@@ -3240,8 +3440,8 @@ channelsmod__register_end_types(PyObject *self, PyObject *args, PyObject *kwds)
}
static PyMethodDef module_functions[] = {
- {"create", channelsmod_create,
- METH_NOARGS, channelsmod_create_doc},
+ {"create", _PyCFunction_CAST(channelsmod_create),
+ METH_VARARGS | METH_KEYWORDS, channelsmod_create_doc},
{"destroy", _PyCFunction_CAST(channelsmod_destroy),
METH_VARARGS | METH_KEYWORDS, channelsmod_destroy_doc},
{"list_all", channelsmod_list_all,
@@ -3258,8 +3458,12 @@ static PyMethodDef module_functions[] = {
METH_VARARGS | METH_KEYWORDS, channelsmod_close_doc},
{"release", _PyCFunction_CAST(channelsmod_release),
METH_VARARGS | METH_KEYWORDS, channelsmod_release_doc},
+ {"get_count", _PyCFunction_CAST(channelsmod_get_count),
+ METH_VARARGS | METH_KEYWORDS, channelsmod_get_count_doc},
{"get_info", _PyCFunction_CAST(channelsmod_get_info),
METH_VARARGS | METH_KEYWORDS, channelsmod_get_info_doc},
+ {"get_channel_defaults", _PyCFunction_CAST(channelsmod_get_channel_defaults),
+ METH_VARARGS | METH_KEYWORDS, channelsmod_get_channel_defaults_doc},
{"_channel_id", _PyCFunction_CAST(channelsmod__channel_id),
METH_VARARGS | METH_KEYWORDS, NULL},
{"_register_end_types", _PyCFunction_CAST(channelsmod__register_end_types),