mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
gh-69093: Add context manager support to sqlite3.Blob (GH-91562)
This commit is contained in:
parent
4e661cd691
commit
a861756675
6 changed files with 135 additions and 11 deletions
|
@ -4,9 +4,13 @@ con = sqlite3.connect(":memory:")
|
|||
con.execute("create table test(blob_col blob)")
|
||||
con.execute("insert into test(blob_col) values (zeroblob(10))")
|
||||
|
||||
blob = con.blobopen("test", "blob_col", 1)
|
||||
blob.write(b"Hello")
|
||||
blob.write(b"World")
|
||||
blob.seek(0)
|
||||
print(blob.read()) # will print b"HelloWorld"
|
||||
blob.close()
|
||||
# Write to our blob, using two write operations:
|
||||
with con.blobopen("test", "blob_col", 1) as blob:
|
||||
blob.write(b"Hello")
|
||||
blob.write(b"World")
|
||||
|
||||
# Read the contents of our blob
|
||||
with con.blobopen("test", "blob_col", 1) as blob:
|
||||
greeting = blob.read()
|
||||
|
||||
print(greeting) # outputs "b'HelloWorld'"
|
||||
|
|
|
@ -1115,6 +1115,11 @@ Blob Objects
|
|||
data in an SQLite :abbr:`BLOB (Binary Large OBject)`. Call ``len(blob)`` to
|
||||
get the size (number of bytes) of the blob.
|
||||
|
||||
Use the :class:`Blob` as a :term:`context manager` to ensure that the blob
|
||||
handle is closed after use.
|
||||
|
||||
.. literalinclude:: ../includes/sqlite3/blob.py
|
||||
|
||||
.. method:: close()
|
||||
|
||||
Close the blob.
|
||||
|
@ -1149,10 +1154,6 @@ Blob Objects
|
|||
current position) and :data:`os.SEEK_END` (seek relative to the blob’s
|
||||
end).
|
||||
|
||||
:class:`Blob` example:
|
||||
|
||||
.. literalinclude:: ../includes/sqlite3/blob.py
|
||||
|
||||
|
||||
.. _sqlite3-types:
|
||||
|
||||
|
|
|
@ -1170,6 +1170,25 @@ class BlobTests(unittest.TestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
b"a" in self.blob
|
||||
|
||||
def test_blob_context_manager(self):
|
||||
data = b"a" * 50
|
||||
with self.cx.blobopen("test", "b", 1) as blob:
|
||||
blob.write(data)
|
||||
actual = self.cx.execute("select b from test").fetchone()[0]
|
||||
self.assertEqual(actual, data)
|
||||
|
||||
# Check that __exit__ closed the blob
|
||||
with self.assertRaisesRegex(sqlite.ProgrammingError, "closed blob"):
|
||||
blob.read()
|
||||
|
||||
def test_blob_context_manager_reraise_exceptions(self):
|
||||
class DummyException(Exception):
|
||||
pass
|
||||
with self.assertRaisesRegex(DummyException, "reraised"):
|
||||
with self.cx.blobopen("test", "b", 1) as blob:
|
||||
raise DummyException("reraised")
|
||||
|
||||
|
||||
def test_blob_closed(self):
|
||||
with memory_database() as cx:
|
||||
cx.execute("create table test(b blob)")
|
||||
|
@ -1186,6 +1205,10 @@ class BlobTests(unittest.TestCase):
|
|||
blob.seek(0)
|
||||
with self.assertRaisesRegex(sqlite.ProgrammingError, msg):
|
||||
blob.tell()
|
||||
with self.assertRaisesRegex(sqlite.ProgrammingError, msg):
|
||||
blob.__enter__()
|
||||
with self.assertRaisesRegex(sqlite.ProgrammingError, msg):
|
||||
blob.__exit__(None, None, None)
|
||||
|
||||
def test_blob_closed_db_read(self):
|
||||
with memory_database() as cx:
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add :term:`context manager` support to :class:`sqlite3.Blob`.
|
||||
Patch by Aviv Palivoda and Erlend E. Aasland.
|
|
@ -307,8 +307,51 @@ blob_tell_impl(pysqlite_Blob *self)
|
|||
}
|
||||
|
||||
|
||||
/*[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 PyMethodDef blob_methods[] = {
|
||||
BLOB_CLOSE_METHODDEF
|
||||
BLOB_ENTER_METHODDEF
|
||||
BLOB_EXIT_METHODDEF
|
||||
BLOB_READ_METHODDEF
|
||||
BLOB_SEEK_METHODDEF
|
||||
BLOB_TELL_METHODDEF
|
||||
|
|
53
Modules/_sqlite/clinic/blob.c.h
generated
53
Modules/_sqlite/clinic/blob.c.h
generated
|
@ -162,4 +162,55 @@ blob_tell(pysqlite_Blob *self, PyObject *Py_UNUSED(ignored))
|
|||
{
|
||||
return blob_tell_impl(self);
|
||||
}
|
||||
/*[clinic end generated code: output=d3a02b127f2cfa58 input=a9049054013a1b77]*/
|
||||
|
||||
PyDoc_STRVAR(blob_enter__doc__,
|
||||
"__enter__($self, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Blob context manager enter.");
|
||||
|
||||
#define BLOB_ENTER_METHODDEF \
|
||||
{"__enter__", (PyCFunction)blob_enter, METH_NOARGS, blob_enter__doc__},
|
||||
|
||||
static PyObject *
|
||||
blob_enter_impl(pysqlite_Blob *self);
|
||||
|
||||
static PyObject *
|
||||
blob_enter(pysqlite_Blob *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return blob_enter_impl(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(blob_exit__doc__,
|
||||
"__exit__($self, type, val, tb, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Blob context manager exit.");
|
||||
|
||||
#define BLOB_EXIT_METHODDEF \
|
||||
{"__exit__", (PyCFunction)(void(*)(void))blob_exit, METH_FASTCALL, blob_exit__doc__},
|
||||
|
||||
static PyObject *
|
||||
blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val,
|
||||
PyObject *tb);
|
||||
|
||||
static PyObject *
|
||||
blob_exit(pysqlite_Blob *self, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
PyObject *type;
|
||||
PyObject *val;
|
||||
PyObject *tb;
|
||||
|
||||
if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) {
|
||||
goto exit;
|
||||
}
|
||||
type = args[0];
|
||||
val = args[1];
|
||||
tb = args[2];
|
||||
return_value = blob_exit_impl(self, type, val, tb);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=ca2400862c18dadb input=a9049054013a1b77]*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue