mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 18:28:49 +00:00 
			
		
		
		
	 9ea9078ec7
			
		
	
	
		9ea9078ec7
		
			
		
	
	
	
	
		
			
			- get index now returns an int - set index now requires an int in range(0, 256) Resolves #92019
		
			
				
	
	
		
			617 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			617 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "blob.h"
 | |
| #include "util.h"
 | |
| 
 | |
| #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
 | |
| #include "clinic/blob.c.h"
 | |
| #undef clinic_state
 | |
| 
 | |
| /*[clinic input]
 | |
| module _sqlite3
 | |
| class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType"
 | |
| [clinic start generated code]*/
 | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=908d3e16a45f8da7]*/
 | |
| 
 | |
| static void
 | |
| close_blob(pysqlite_Blob *self)
 | |
| {
 | |
|     if (self->blob) {
 | |
|         sqlite3_blob *blob = self->blob;
 | |
|         self->blob = NULL;
 | |
| 
 | |
|         Py_BEGIN_ALLOW_THREADS
 | |
|         sqlite3_blob_close(blob);
 | |
|         Py_END_ALLOW_THREADS
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int
 | |
| blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg)
 | |
| {
 | |
|     Py_VISIT(Py_TYPE(self));
 | |
|     Py_VISIT(self->connection);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| blob_clear(pysqlite_Blob *self)
 | |
| {
 | |
|     Py_CLEAR(self->connection);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| blob_dealloc(pysqlite_Blob *self)
 | |
| {
 | |
|     PyTypeObject *tp = Py_TYPE(self);
 | |
|     PyObject_GC_UnTrack(self);
 | |
| 
 | |
|     close_blob(self);
 | |
| 
 | |
|     if (self->in_weakreflist != NULL) {
 | |
|         PyObject_ClearWeakRefs((PyObject*)self);
 | |
|     }
 | |
|     tp->tp_clear((PyObject *)self);
 | |
|     tp->tp_free(self);
 | |
|     Py_DECREF(tp);
 | |
| }
 | |
| 
 | |
| // Return 1 if the blob object is usable, 0 if not.
 | |
| static int
 | |
| check_blob(pysqlite_Blob *self)
 | |
| {
 | |
|     if (!pysqlite_check_connection(self->connection) ||
 | |
|         !pysqlite_check_thread(self->connection)) {
 | |
|         return 0;
 | |
|     }
 | |
|     if (self->blob == NULL) {
 | |
|         pysqlite_state *state = self->connection->state;
 | |
|         PyErr_SetString(state->ProgrammingError,
 | |
|                         "Cannot operate on a closed blob.");
 | |
|         return 0;
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.close as blob_close
 | |
| 
 | |
| Close the blob.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_close_impl(pysqlite_Blob *self)
 | |
| /*[clinic end generated code: output=848accc20a138d1b input=7bc178a402a40bd8]*/
 | |
| {
 | |
|     if (!pysqlite_check_connection(self->connection) ||
 | |
|         !pysqlite_check_thread(self->connection))
 | |
|     {
 | |
|         return NULL;
 | |
|     }
 | |
|     close_blob(self);
 | |
|     Py_RETURN_NONE;
 | |
| };
 | |
| 
 | |
| void
 | |
| pysqlite_close_all_blobs(pysqlite_Connection *self)
 | |
| {
 | |
|     for (int i = 0; i < PyList_GET_SIZE(self->blobs); i++) {
 | |
|         PyObject *weakref = PyList_GET_ITEM(self->blobs, i);
 | |
|         PyObject *blob = PyWeakref_GetObject(weakref);
 | |
|         if (!Py_IsNone(blob)) {
 | |
|             close_blob((pysqlite_Blob *)blob);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| blob_seterror(pysqlite_Blob *self, int rc)
 | |
| {
 | |
|     assert(self->connection != NULL);
 | |
| #if SQLITE_VERSION_NUMBER < 3008008
 | |
|     // SQLite pre 3.8.8 does not set this blob error on the connection
 | |
|     if (rc == SQLITE_ABORT) {
 | |
|         PyErr_SetString(self->connection->OperationalError,
 | |
|                         "Cannot operate on an expired blob handle");
 | |
|         return;
 | |
|     }
 | |
| #endif
 | |
|     _pysqlite_seterror(self->connection->state, self->connection->db);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| read_single(pysqlite_Blob *self, Py_ssize_t offset)
 | |
| {
 | |
|     unsigned char buf = 0;
 | |
|     int rc;
 | |
|     Py_BEGIN_ALLOW_THREADS
 | |
|     rc = sqlite3_blob_read(self->blob, (void *)&buf, 1, (int)offset);
 | |
|     Py_END_ALLOW_THREADS
 | |
| 
 | |
|     if (rc != SQLITE_OK) {
 | |
|         blob_seterror(self, rc);
 | |
|         return NULL;
 | |
|     }
 | |
|     return PyLong_FromUnsignedLong((unsigned long)buf);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| read_multiple(pysqlite_Blob *self, Py_ssize_t length, Py_ssize_t offset)
 | |
| {
 | |
|     assert(length <= sqlite3_blob_bytes(self->blob));
 | |
|     assert(offset < sqlite3_blob_bytes(self->blob));
 | |
| 
 | |
|     PyObject *buffer = PyBytes_FromStringAndSize(NULL, length);
 | |
|     if (buffer == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     char *raw_buffer = PyBytes_AS_STRING(buffer);
 | |
|     int rc;
 | |
|     Py_BEGIN_ALLOW_THREADS
 | |
|     rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset);
 | |
|     Py_END_ALLOW_THREADS
 | |
| 
 | |
|     if (rc != SQLITE_OK) {
 | |
|         Py_DECREF(buffer);
 | |
|         blob_seterror(self, rc);
 | |
|         return NULL;
 | |
|     }
 | |
|     return buffer;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.read as blob_read
 | |
| 
 | |
|     length: int = -1
 | |
|         Read length in bytes.
 | |
|     /
 | |
| 
 | |
| Read data at the current offset position.
 | |
| 
 | |
| If the end of the blob is reached, the data up to end of file will be returned.
 | |
| When length is not specified, or is negative, Blob.read() will read until the
 | |
| end of the blob.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_read_impl(pysqlite_Blob *self, int length)
 | |
| /*[clinic end generated code: output=1fc99b2541360dde input=f2e4aa4378837250]*/
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Make sure we never read past "EOB". Also read the rest of the blob if a
 | |
|      * negative length is specified. */
 | |
|     int blob_len = sqlite3_blob_bytes(self->blob);
 | |
|     int max_read_len = blob_len - self->offset;
 | |
|     if (length < 0 || length > max_read_len) {
 | |
|         length = max_read_len;
 | |
|     }
 | |
| 
 | |
|     assert(length >= 0);
 | |
|     if (length == 0) {
 | |
|         return PyBytes_FromStringAndSize(NULL, 0);
 | |
|     }
 | |
| 
 | |
|     PyObject *buffer = read_multiple(self, length, self->offset);
 | |
|     if (buffer == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     self->offset += length;
 | |
|     return buffer;
 | |
| };
 | |
| 
 | |
| static int
 | |
| inner_write(pysqlite_Blob *self, const void *buf, Py_ssize_t len,
 | |
|             Py_ssize_t offset)
 | |
| {
 | |
|     Py_ssize_t blob_len = sqlite3_blob_bytes(self->blob);
 | |
|     Py_ssize_t remaining_len = blob_len - offset;
 | |
|     if (len > remaining_len) {
 | |
|         PyErr_SetString(PyExc_ValueError, "data longer than blob length");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     assert(offset <= blob_len);
 | |
|     int rc;
 | |
|     Py_BEGIN_ALLOW_THREADS
 | |
|     rc = sqlite3_blob_write(self->blob, buf, (int)len, (int)offset);
 | |
|     Py_END_ALLOW_THREADS
 | |
| 
 | |
|     if (rc != SQLITE_OK) {
 | |
|         blob_seterror(self, rc);
 | |
|         return -1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.write as blob_write
 | |
| 
 | |
|     data: Py_buffer
 | |
|     /
 | |
| 
 | |
| Write data at the current offset.
 | |
| 
 | |
| This function cannot change the blob length.  Writing beyond the end of the
 | |
| blob will result in an exception being raised.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_write_impl(pysqlite_Blob *self, Py_buffer *data)
 | |
| /*[clinic end generated code: output=b34cf22601b570b2 input=a84712f24a028e6d]*/
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     int rc = inner_write(self, data->buf, data->len, self->offset);
 | |
|     if (rc < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
|     self->offset += (int)data->len;
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.seek as blob_seek
 | |
| 
 | |
|     offset: int
 | |
|     origin: int = 0
 | |
|     /
 | |
| 
 | |
| Set the current access position to offset.
 | |
| 
 | |
| The origin argument defaults to os.SEEK_SET (absolute blob positioning).
 | |
| Other values for origin are os.SEEK_CUR (seek relative to the current position)
 | |
| and os.SEEK_END (seek relative to the blob's end).
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_seek_impl(pysqlite_Blob *self, int offset, int origin)
 | |
| /*[clinic end generated code: output=854c5a0e208547a5 input=5da9a07e55fe6bb6]*/
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     int blob_len = sqlite3_blob_bytes(self->blob);
 | |
|     switch (origin) {
 | |
|         case SEEK_SET:
 | |
|             break;
 | |
|         case SEEK_CUR:
 | |
|             if (offset > INT_MAX - self->offset) {
 | |
|                 goto overflow;
 | |
|             }
 | |
|             offset += self->offset;
 | |
|             break;
 | |
|         case SEEK_END:
 | |
|             if (offset > INT_MAX - blob_len) {
 | |
|                 goto overflow;
 | |
|             }
 | |
|             offset += blob_len;
 | |
|             break;
 | |
|         default:
 | |
|             PyErr_SetString(PyExc_ValueError,
 | |
|                             "'origin' should be os.SEEK_SET, os.SEEK_CUR, or "
 | |
|                             "os.SEEK_END");
 | |
|             return NULL;
 | |
|     }
 | |
| 
 | |
|     if (offset < 0 || offset > blob_len) {
 | |
|         PyErr_SetString(PyExc_ValueError, "offset out of blob range");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     self->offset = offset;
 | |
|     Py_RETURN_NONE;
 | |
| 
 | |
| overflow:
 | |
|     PyErr_SetString(PyExc_OverflowError, "seek offset results in overflow");
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.tell as blob_tell
 | |
| 
 | |
| Return the current access position for the blob.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_tell_impl(pysqlite_Blob *self)
 | |
| /*[clinic end generated code: output=3d3ba484a90b3a99 input=7e34057aa303612c]*/
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return PyLong_FromLong(self->offset);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.__enter__ as blob_enter
 | |
| 
 | |
| Blob context manager enter.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_enter_impl(pysqlite_Blob *self)
 | |
| /*[clinic end generated code: output=4fd32484b071a6cd input=fe4842c3c582d5a7]*/
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return Py_NewRef(self);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _sqlite3.Blob.__exit__ as blob_exit
 | |
| 
 | |
|     type: object
 | |
|     val: object
 | |
|     tb: object
 | |
|     /
 | |
| 
 | |
| Blob context manager exit.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val,
 | |
|                PyObject *tb)
 | |
| /*[clinic end generated code: output=fc86ceeb2b68c7b2 input=575d9ecea205f35f]*/
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     close_blob(self);
 | |
|     Py_RETURN_FALSE;
 | |
| }
 | |
| 
 | |
| static Py_ssize_t
 | |
| blob_length(pysqlite_Blob *self)
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return -1;
 | |
|     }
 | |
|     return sqlite3_blob_bytes(self->blob);
 | |
| };
 | |
| 
 | |
| static Py_ssize_t
 | |
| get_subscript_index(pysqlite_Blob *self, PyObject *item)
 | |
| {
 | |
|     Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
 | |
|     if (i == -1 && PyErr_Occurred()) {
 | |
|         return -1;
 | |
|     }
 | |
|     int blob_len = sqlite3_blob_bytes(self->blob);
 | |
|     if (i < 0) {
 | |
|         i += blob_len;
 | |
|     }
 | |
|     if (i < 0 || i >= blob_len) {
 | |
|         PyErr_SetString(PyExc_IndexError, "Blob index out of range");
 | |
|         return -1;
 | |
|     }
 | |
|     return i;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| subscript_index(pysqlite_Blob *self, PyObject *item)
 | |
| {
 | |
|     Py_ssize_t i = get_subscript_index(self, item);
 | |
|     if (i < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return read_single(self, i);
 | |
| }
 | |
| 
 | |
| static int
 | |
| get_slice_info(pysqlite_Blob *self, PyObject *item, Py_ssize_t *start,
 | |
|                Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelen)
 | |
| {
 | |
|     if (PySlice_Unpack(item, start, stop, step) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
|     int len = sqlite3_blob_bytes(self->blob);
 | |
|     *slicelen = PySlice_AdjustIndices(len, start, stop, *step);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| subscript_slice(pysqlite_Blob *self, PyObject *item)
 | |
| {
 | |
|     Py_ssize_t start, stop, step, len;
 | |
|     if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (step == 1) {
 | |
|         return read_multiple(self, len, start);
 | |
|     }
 | |
|     PyObject *blob = read_multiple(self, stop - start, start);
 | |
|     if (blob == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject *result = PyBytes_FromStringAndSize(NULL, len);
 | |
|     if (result != NULL) {
 | |
|         char *blob_buf = PyBytes_AS_STRING(blob);
 | |
|         char *res_buf = PyBytes_AS_STRING(result);
 | |
|         for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
 | |
|             res_buf[i] = blob_buf[j];
 | |
|         }
 | |
|         Py_DECREF(blob);
 | |
|     }
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| blob_subscript(pysqlite_Blob *self, PyObject *item)
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (PyIndex_Check(item)) {
 | |
|         return subscript_index(self, item);
 | |
|     }
 | |
|     if (PySlice_Check(item)) {
 | |
|         return subscript_slice(self, item);
 | |
|     }
 | |
| 
 | |
|     PyErr_SetString(PyExc_TypeError, "Blob indices must be integers");
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static int
 | |
| ass_subscript_index(pysqlite_Blob *self, PyObject *item, PyObject *value)
 | |
| {
 | |
|     if (value == NULL) {
 | |
|         PyErr_SetString(PyExc_TypeError,
 | |
|                         "Blob doesn't support item deletion");
 | |
|         return -1;
 | |
|     }
 | |
|     if (!PyLong_Check(value)) {
 | |
|         PyErr_Format(PyExc_TypeError,
 | |
|                      "'%s' object cannot be interpreted as an integer",
 | |
|                      Py_TYPE(value)->tp_name);
 | |
|         return -1;
 | |
|     }
 | |
|     Py_ssize_t i = get_subscript_index(self, item);
 | |
|     if (i < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     long val = PyLong_AsLong(value);
 | |
|     if (val == -1 && PyErr_Occurred()) {
 | |
|         PyErr_Clear();
 | |
|         val = -1;
 | |
|     }
 | |
|     if (val < 0 || val > 255) {
 | |
|         PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)");
 | |
|         return -1;
 | |
|     }
 | |
|     // Downcast to avoid endianness problems.
 | |
|     unsigned char byte = (unsigned char)val;
 | |
|     return inner_write(self, (const void *)&byte, 1, i);
 | |
| }
 | |
| 
 | |
| static int
 | |
| ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value)
 | |
| {
 | |
|     if (value == NULL) {
 | |
|         PyErr_SetString(PyExc_TypeError,
 | |
|                         "Blob doesn't support slice deletion");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     Py_ssize_t start, stop, step, len;
 | |
|     if (get_slice_info(self, item, &start, &stop, &step, &len) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (len == 0) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     Py_buffer vbuf;
 | |
|     if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     int rc = -1;
 | |
|     if (vbuf.len != len) {
 | |
|         PyErr_SetString(PyExc_IndexError,
 | |
|                         "Blob slice assignment is wrong size");
 | |
|     }
 | |
|     else if (step == 1) {
 | |
|         rc = inner_write(self, vbuf.buf, len, start);
 | |
|     }
 | |
|     else {
 | |
|         PyObject *blob_bytes = read_multiple(self, stop - start, start);
 | |
|         if (blob_bytes != NULL) {
 | |
|             char *blob_buf = PyBytes_AS_STRING(blob_bytes);
 | |
|             for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) {
 | |
|                 blob_buf[j] = ((char *)vbuf.buf)[i];
 | |
|             }
 | |
|             rc = inner_write(self, blob_buf, stop - start, start);
 | |
|             Py_DECREF(blob_bytes);
 | |
|         }
 | |
|     }
 | |
|     PyBuffer_Release(&vbuf);
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| static int
 | |
| blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value)
 | |
| {
 | |
|     if (!check_blob(self)) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (PyIndex_Check(item)) {
 | |
|         return ass_subscript_index(self, item, value);
 | |
|     }
 | |
|     if (PySlice_Check(item)) {
 | |
|         return ass_subscript_slice(self, item, value);
 | |
|     }
 | |
| 
 | |
|     PyErr_SetString(PyExc_TypeError, "Blob indices must be integers");
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| static PyMethodDef blob_methods[] = {
 | |
|     BLOB_CLOSE_METHODDEF
 | |
|     BLOB_ENTER_METHODDEF
 | |
|     BLOB_EXIT_METHODDEF
 | |
|     BLOB_READ_METHODDEF
 | |
|     BLOB_SEEK_METHODDEF
 | |
|     BLOB_TELL_METHODDEF
 | |
|     BLOB_WRITE_METHODDEF
 | |
|     {NULL, NULL}
 | |
| };
 | |
| 
 | |
| static struct PyMemberDef blob_members[] = {
 | |
|     {"__weaklistoffset__", T_PYSSIZET, offsetof(pysqlite_Blob, in_weakreflist), READONLY},
 | |
|     {NULL},
 | |
| };
 | |
| 
 | |
| static PyType_Slot blob_slots[] = {
 | |
|     {Py_tp_dealloc, blob_dealloc},
 | |
|     {Py_tp_traverse, blob_traverse},
 | |
|     {Py_tp_clear, blob_clear},
 | |
|     {Py_tp_methods, blob_methods},
 | |
|     {Py_tp_members, blob_members},
 | |
| 
 | |
|     // Mapping protocol
 | |
|     {Py_mp_length, blob_length},
 | |
|     {Py_mp_subscript, blob_subscript},
 | |
|     {Py_mp_ass_subscript, blob_ass_subscript},
 | |
|     {0, NULL},
 | |
| };
 | |
| 
 | |
| static PyType_Spec blob_spec = {
 | |
|     .name = MODULE_NAME ".Blob",
 | |
|     .basicsize = sizeof(pysqlite_Blob),
 | |
|     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
 | |
|               Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
 | |
|     .slots = blob_slots,
 | |
| };
 | |
| 
 | |
| int
 | |
| pysqlite_blob_setup_types(PyObject *mod)
 | |
| {
 | |
|     PyObject *type = PyType_FromModuleAndSpec(mod, &blob_spec, NULL);
 | |
|     if (type == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
|     pysqlite_state *state = pysqlite_get_state(mod);
 | |
|     state->BlobType = (PyTypeObject *)type;
 | |
|     return 0;
 | |
| }
 |