Skip to content

Commit

Permalink
gh-69093: Add context manager support to sqlite3.Blob (GH-91562)
Browse files Browse the repository at this point in the history
  • Loading branch information
Erlend Egeberg Aasland committed Apr 16, 2022
1 parent 4e661cd commit a861756
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 11 deletions.
16 changes: 10 additions & 6 deletions Doc/includes/sqlite3/blob.py
Expand Up @@ -4,9 +4,13 @@
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'"
9 changes: 5 additions & 4 deletions Doc/library/sqlite3.rst
Expand Up @@ -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.
Expand Down Expand Up @@ -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:

Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Expand Up @@ -1170,6 +1170,25 @@ def test_blob_sequence_not_supported(self):
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)")
Expand All @@ -1186,6 +1205,10 @@ def test_blob_closed(self):
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:
Expand Down
@@ -0,0 +1,2 @@
Add :term:`context manager` support to :class:`sqlite3.Blob`.
Patch by Aviv Palivoda and Erlend E. Aasland.
43 changes: 43 additions & 0 deletions Modules/_sqlite/blob.c
Expand Up @@ -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
Expand Down
53 changes: 52 additions & 1 deletion Modules/_sqlite/clinic/blob.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a861756

Please sign in to comment.