Code

Ticket #17671: django-ticket17671.diff

File django-ticket17671.diff, 3.7 KB (added by manfre, 2 years ago)

context manager shortcut without pass-thru

Line 
1diff --git a/django/db/backends/util.py b/django/db/backends/util.py
2index 32e0f4f..30f0a16 100644
3--- a/django/db/backends/util.py
4+++ b/django/db/backends/util.py
5@@ -30,6 +30,13 @@ class CursorWrapper(object):
6     def __iter__(self):
7         return iter(self.cursor)
8 
9+    def __enter__(self):
10+        return self
11+
12+    def __exit__(self, type, value, traceback):
13+        # Ticket #17671 - Close instead of passing thru to avoid backend
14+        # specific behavior.
15+        self.cursor.close()
16 
17 class CursorDebugWrapper(CursorWrapper):
18 
19diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
20index 33f5003..900f692 100644
21--- a/docs/releases/1.5.txt
22+++ b/docs/releases/1.5.txt
23@@ -144,6 +144,25 @@ Unicode parameters (``password``, ``salt`` or ``encoded``). If any of the
24 hashing methods need byte strings, you can use the
25 :func:`~django.utils.encoding.smart_str` utility to encode the strings.
26 
27+Using database cursors as context managers
28+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29+
30+Prior to Python 2.6, database cursors could be used as a context manager. The
31+specific backend's cursor defined the behavior of the context manager. The
32+behavior of magic method lookups was changed with Python 2.7 and cursors were
33+no longer usable as context managers.
34+
35+Django 1.5 allows a cursor to be used as a context manager that is a shortcut
36+for the following, instead of backend specific behavior.
37+
38+.. code-block:: python
39+
40+    c = connection.cursor()
41+    try:
42+        c.execute(...)
43+    finally:
44+        c.close()
45+
46 Features deprecated in 1.5
47 ==========================
48 
49diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt
50index 19daffd..577c58b 100644
51--- a/docs/topics/db/sql.txt
52+++ b/docs/topics/db/sql.txt
53@@ -262,6 +262,7 @@ Here is an example of the difference between the two::
54     [{'parent_id': None, 'id': 54360982L}, {'parent_id': None, 'id': 54360880L}]
55 
56 
57+
58 .. _transactions-and-raw-sql:
59 
60 Transactions and raw SQL
61@@ -290,3 +291,30 @@ technique, the underlying database library will automatically add quotes and
62 escaping to your parameter(s) as necessary. (Also note that Django expects the
63 ``"%s"`` placeholder, *not* the ``"?"`` placeholder, which is used by the SQLite
64 Python bindings. This is for the sake of consistency and sanity.)
65+
66+.. versionchanged:: 1.5
67+
68+:pep:`249` does not state whether a cursor should be usable as a context
69+manager. Prior to Python 2.7, a cursor was usable as a context manager due
70+an unexpected behavior in magic method lookups (`Python ticket #9220`_).
71+Django 1.5 explicitly added support to allow using a cursor as context
72+manager.
73+
74+.. _`Python ticket #9220`: http://bugs.python.org/issue9220
75+
76+Using a cursor as a context manager:
77+
78+.. code-block:: python
79+
80+    with connection.cursor() as c:
81+        c.execute(...)
82+
83+is equivalent to:
84+
85+.. code-block:: python
86+
87+    c = connection.cursor()
88+    try:
89+        c.execute(...)
90+    finally:
91+        c.close()
92diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
93index 109e1a5..2258388 100644
94--- a/tests/regressiontests/backends/tests.py
95+++ b/tests/regressiontests/backends/tests.py
96@@ -416,6 +416,14 @@ class BackendTestCase(TestCase):
97         with self.assertRaises(DatabaseError):
98             cursor.execute(query)
99 
100+    def test_cursor_contextmanager(self):
101+        """
102+        Test that cursors can be used as a context manager
103+        """
104+        with connection.cursor() as cursor:
105+            from django.db.backends.util import CursorWrapper
106+            self.assertTrue(isinstance(cursor, CursorWrapper))
107+
108 # We don't make these tests conditional because that means we would need to
109 # check and differentiate between:
110 # * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do).