Skip to content

Commit 24c4715

Browse files
committed
Rename get_transaction to get_cursor
This keeps us closer to the DB-API 2.0 object model, where there is no transaction object per se. Rather a transaction is a function of the way the connection object underlying a cursor is used.
1 parent c0b11a6 commit 24c4715

File tree

2 files changed

+73
-73
lines changed

2 files changed

+73
-73
lines changed

postgres/__init__.py

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -86,37 +86,38 @@
8686
you. Everything in :py:mod:`postgres`, both the simple API and the context
8787
managers, uses this connection pool.
8888
89-
Use the :py:func:`~postgres.Postgres.get_transaction` context manager to work
89+
Use the :py:func:`~postgres.Postgres.get_cursor` context manager to work
9090
directly with a :py:mod:`psycogpg2` `cursor
9191
<http://initd.org/psycopg/docs/cursor.html>`_ while still taking advantage of
9292
connection pooling and automatic transaction management:
9393
94-
>>> with db.get_transaction() as txn:
95-
... txn.execute("INSERT INTO foo VALUES ('blam')")
96-
... txn.execute("SELECT * FROM foo ORDER BY bar")
97-
... txn.fetchall()
94+
>>> with db.get_cursor() as cursor:
95+
... cursor.execute("INSERT INTO foo VALUES ('blam')")
96+
... cursor.execute("SELECT * FROM foo ORDER BY bar")
97+
... cursor.fetchall()
9898
...
9999
[Record(bar='bit', baz=537), Record(bar='blam', baz=None), Record(bar='buz', baz=42)]
100100
101101
Note that other calls won't see the changes on your transaction until the end
102102
of your code block, when the context manager commits the transaction for you::
103103
104104
>>> db.run("DELETE FROM foo WHERE bar='blam'")
105-
>>> with db.get_transaction() as txn:
106-
... txn.execute("INSERT INTO foo VALUES ('blam')")
105+
>>> with db.get_cursor() as cursor:
106+
... cursor.execute("INSERT INTO foo VALUES ('blam')")
107107
... db.all("SELECT * FROM foo ORDER BY bar")
108108
...
109109
[Record(bar='bit', baz=537), Record(bar='buz', baz=42)]
110110
>>> db.all("SELECT * FROM foo ORDER BY bar")
111111
[Record(bar='bit', baz=537), Record(bar='blam', baz=None), Record(bar='buz', baz=42)]
112112
113-
The :py:func:`~postgres.Postgres.get_transaction` manager gives you a cursor
114-
with :py:attr:`autocommit` turned off on its connection. If the block under
115-
management raises an exception, the connection is rolled back. Otherwise it's
116-
committed. Use this when you want a series of statements to be part of one
117-
transaction, but you don't need fine-grained control over the transaction. For
118-
fine-grained control, use :py:func:`~postgres.Postgres.get_connection` to get a
119-
connection straight from the connection pool:
113+
The :py:func:`~postgres.Postgres.get_cursor` method gives you a context manager
114+
that wraps a cursor. It has :py:attr:`autocommit` turned off on its connection.
115+
If the block under management raises an exception, the connection is rolled
116+
back. Otherwise it's committed. Use this when you want a series of statements
117+
to be part of one transaction, but you don't need fine-grained control over the
118+
transaction. For fine-grained control, use
119+
:py:func:`~postgres.Postgres.get_connection` to get a connection straight from
120+
the connection pool:
120121
121122
>>> db.run("DELETE FROM foo WHERE bar='blam'")
122123
>>> with db.get_connection() as connection:
@@ -295,7 +296,7 @@ class Postgres(object):
295296
you set here, you can override that default on a per-call basis by passing
296297
:py:attr:`record_type` or :py:attr:`cursor_factory` to
297298
:py:meth:`~postgres.Postgres.one`, :py:meth:`~postgres.Postgres.all`, and
298-
:py:meth:`~postgres.Postgres.get_transaction`.
299+
:py:meth:`~postgres.Postgres.get_cursor`.
299300
300301
The names in our simple API, :py:meth:`~postgres.Postgres.run`,
301302
:py:meth:`~postgres.Postgres.one`, and :py:meth:`~postgres.Postgres.all`,
@@ -351,10 +352,8 @@ def run(self, sql, parameters=None, *a, **kw):
351352
:param string sql: the SQL statement to execute
352353
:param parameters: the bind parameters for the SQL statement
353354
:type parameters: dict or tuple
354-
:param a: passed through to
355-
:py:meth:`~postgres.Postgres.get_transaction`
356-
:param kw: passed through to
357-
:py:meth:`~postgres.Postgres.get_transaction`
355+
:param a: passed through to :py:meth:`~postgres.Postgres.get_cursor`
356+
:param kw: passed through to :py:meth:`~postgres.Postgres.get_cursor`
358357
:returns: :py:const:`None`
359358
360359
>>> db.run("DROP TABLE IF EXISTS foo CASCADE")
@@ -363,8 +362,8 @@ def run(self, sql, parameters=None, *a, **kw):
363362
>>> db.run("INSERT INTO foo VALUES ('bit', 537)")
364363
365364
"""
366-
with self.get_transaction(*a, **kw) as txn:
367-
txn.execute(sql, parameters)
365+
with self.get_cursor(*a, **kw) as cursor:
366+
cursor.execute(sql, parameters)
368367

369368

370369
def one(self, sql, parameters=None, record_type=None, default=None, \
@@ -378,9 +377,9 @@ def one(self, sql, parameters=None, record_type=None, default=None, \
378377
:type record_type: type or string
379378
:param default: the value to return if no results are found
380379
:param a: passed through to
381-
:py:meth:`~postgres.Postgres.get_transaction`
380+
:py:meth:`~postgres.Postgres.get_cursor`
382381
:param kw: passed through to
383-
:py:meth:`~postgres.Postgres.get_transaction`
382+
:py:meth:`~postgres.Postgres.get_cursor`
384383
:returns: a single record or value or the value of the
385384
:py:attr:`default` argument
386385
:raises: :py:exc:`~postgres.TooFew` or :py:exc:`~postgres.TooMany`
@@ -481,9 +480,9 @@ def all(self, sql, parameters=None, record_type=None, *a, **kw):
481480
:param record_type: the type of record to return
482481
:type record_type: type or string
483482
:param a: passed through to
484-
:py:meth:`~postgres.Postgres.get_transaction`
483+
:py:meth:`~postgres.Postgres.get_cursor`
485484
:param kw: passed through to
486-
:py:meth:`~postgres.Postgres.get_transaction`
485+
:py:meth:`~postgres.Postgres.get_cursor`
487486
:returns: :py:class:`list` of records or :py:class:`list` of single
488487
values
489488
@@ -527,9 +526,9 @@ def all(self, sql, parameters=None, record_type=None, *a, **kw):
527526
[537, 42]
528527
529528
"""
530-
with self.get_transaction(record_type=record_type, *a, **kw) as txn:
531-
txn.execute(sql, parameters)
532-
recs = txn.fetchall()
529+
with self.get_cursor(record_type=record_type, *a, **kw) as cursor:
530+
cursor.execute(sql, parameters)
531+
recs = cursor.fetchall()
533532
if recs and len(recs[0]) == 1: # dereference
534533
if hasattr(recs[0], 'values'): # mapping
535534
recs = [list(rec.values())[0] for rec in recs]
@@ -545,56 +544,57 @@ def _some(self, sql, parameters, lo, hi, record_type, *a, **kw):
545544
# had those two methods. Help yourself to _some now that you've found
546545
# it. :^)
547546

548-
with self.get_transaction(record_type=record_type, *a, **kw) as txn:
549-
txn.execute(sql, parameters)
547+
with self.get_cursor(record_type=record_type, *a, **kw) as cursor:
548+
cursor.execute(sql, parameters)
550549

551-
if txn.rowcount < lo:
552-
raise TooFew(txn.rowcount, lo, hi)
553-
elif txn.rowcount > hi:
554-
raise TooMany(txn.rowcount, lo, hi)
550+
if cursor.rowcount < lo:
551+
raise TooFew(cursor.rowcount, lo, hi)
552+
elif cursor.rowcount > hi:
553+
raise TooMany(cursor.rowcount, lo, hi)
555554

556-
return txn.fetchone()
555+
return cursor.fetchone()
557556

558557

559-
def get_transaction(self, *a, **kw):
560-
"""Return a :py:class:`~postgres.TransactionContextManager` that uses
558+
def get_cursor(self, *a, **kw):
559+
"""Return a :py:class:`~postgres.CursorContextManager` that uses
561560
our connection pool.
562561
562+
>>> with db.get_cursor() as cursor:
563+
... cursor.execute("SELECT * FROM foo")
564+
... cursor.fetchall()
565+
...
566+
[Record(bar='buz', baz=42), Record(bar='bit', baz=537)]
567+
563568
This gets you a cursor with :py:attr:`autocommit` turned off on its
564569
connection. If your code block inside the :py:obj:`with` statement
565570
raises an exception, the transaction will be rolled back. Otherwise,
566571
it'll be committed. The context manager closes the cursor when the
567-
block ends.
572+
block ends, resets :py:attr:`autocommit` to off on the connection, and
573+
puts the connection back in the pool.
568574
569575
Use this when you want a series of statements to be part of one
570576
transaction, but you don't need fine-grained control over the
571577
transaction.
572578
573-
>>> with db.get_transaction() as txn:
574-
... txn.execute("SELECT * FROM foo")
575-
... txn.fetchall()
576-
...
577-
[Record(bar='buz', baz=42), Record(bar='bit', baz=537)]
578-
579579
"""
580-
return TransactionContextManager(self.pool, *a, **kw)
580+
return CursorContextManager(self.pool, *a, **kw)
581581

582582

583583
def get_connection(self):
584584
"""Return a :py:class:`~postgres.ConnectionContextManager` that uses
585585
our connection pool.
586586
587-
Use this when you want to take advantage of connection pooling, but
588-
otherwise need full control, for example, to do complex things with
589-
transactions.
590-
591587
>>> with db.get_connection() as connection:
592588
... cursor = connection.cursor()
593589
... cursor.execute("SELECT * FROM foo")
594590
... cursor.fetchall()
595591
...
596592
[Record(bar='buz', baz=42), Record(bar='bit', baz=537)]
597593
594+
Use this when you want to take advantage of connection pooling, but
595+
otherwise need full control, for example, to do complex things with
596+
transactions.
597+
598598
"""
599599
return ConnectionContextManager(self.pool)
600600

@@ -669,13 +669,13 @@ def unregister_model(self, ModelSubclass):
669669
# Context Managers
670670
# ================
671671

672-
class TransactionContextManager(object):
673-
"""Instantiated once per :py:func:`~postgres.Postgres.get_transaction`
672+
class CursorContextManager(object):
673+
"""Instantiated once per :py:func:`~postgres.Postgres.get_cursor`
674674
call.
675675
676676
:param pool: a :py:class:`psycopg2.pool.*ConnectionPool`
677677
678-
The return value of :py:func:`TransactionContextManager.__enter__` is a
678+
The return value of :py:func:`CursorContextManager.__enter__` is a
679679
:py:mod:`psycopg2` cursor. Any positional and keyword arguments to our
680680
constructor are passed through to the cursor constructor. If you pass
681681
:py:attr:`record_type` as a keyword argument then we'll infer a
@@ -741,7 +741,7 @@ def compute_cursor_factory(self, **kw):
741741
# Pull record_type out of kw.
742742
# ===========================
743743
# If we leave it in psycopg2 will complain. Our internal calls to
744-
# get_transaction always have it but external use might not.
744+
# get_cursor always have it but external use might not.
745745

746746
record_type = kw.pop('record_type', None)
747747

tests.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -157,49 +157,49 @@ def test_with_strict_True_one_raises_TooMany(self):
157157
self.assertRaises(TooMany, self.db.one, "SELECT * FROM foo")
158158

159159

160-
# db.get_transaction
161-
# ==================
160+
# db.get_cursor
161+
# =============
162162

163-
class TestTransaction(WithData):
163+
class TestCursor(WithData):
164164

165-
def test_get_transaction_gets_a_transaction(self):
166-
with self.db.get_transaction() as txn:
167-
txn.execute("INSERT INTO foo VALUES ('blam')")
168-
txn.execute("SELECT * FROM foo ORDER BY bar")
169-
actual = txn.fetchall()
165+
def test_get_cursor_gets_a_cursor(self):
166+
with self.db.get_cursor() as cursor:
167+
cursor.execute("INSERT INTO foo VALUES ('blam')")
168+
cursor.execute("SELECT * FROM foo ORDER BY bar")
169+
actual = cursor.fetchall()
170170
assert actual == [{"bar": "baz"}, {"bar": "blam"}, {"bar": "buz"}]
171171

172172
def test_transaction_is_isolated(self):
173-
with self.db.get_transaction() as txn:
174-
txn.execute("INSERT INTO foo VALUES ('blam')")
175-
txn.execute("SELECT * FROM foo ORDER BY bar")
173+
with self.db.get_cursor() as cursor:
174+
cursor.execute("INSERT INTO foo VALUES ('blam')")
175+
cursor.execute("SELECT * FROM foo ORDER BY bar")
176176
actual = self.db.all("SELECT * FROM foo ORDER BY bar")
177177
assert actual == ["baz", "buz"]
178178

179179
def test_transaction_commits_on_success(self):
180-
with self.db.get_transaction() as txn:
181-
txn.execute("INSERT INTO foo VALUES ('blam')")
182-
txn.execute("SELECT * FROM foo ORDER BY bar")
180+
with self.db.get_cursor() as cursor:
181+
cursor.execute("INSERT INTO foo VALUES ('blam')")
182+
cursor.execute("SELECT * FROM foo ORDER BY bar")
183183
actual = self.db.all("SELECT * FROM foo ORDER BY bar")
184184
assert actual == ["baz", "blam", "buz"]
185185

186186
def test_transaction_rolls_back_on_failure(self):
187187
class Heck(Exception): pass
188188
try:
189-
with self.db.get_transaction() as txn:
190-
txn.execute("INSERT INTO foo VALUES ('blam')")
191-
txn.execute("SELECT * FROM foo ORDER BY bar")
189+
with self.db.get_cursor() as cursor:
190+
cursor.execute("INSERT INTO foo VALUES ('blam')")
191+
cursor.execute("SELECT * FROM foo ORDER BY bar")
192192
raise Heck
193193
except Heck:
194194
pass
195195
actual = self.db.all("SELECT * FROM foo ORDER BY bar")
196196
assert actual == ["baz", "buz"]
197197

198198
def test_we_close_the_cursor(self):
199-
with self.db.get_transaction() as txn:
200-
txn.execute("SELECT * FROM foo ORDER BY bar")
199+
with self.db.get_cursor() as cursor:
200+
cursor.execute("SELECT * FROM foo ORDER BY bar")
201201
self.assertRaises( InterfaceError
202-
, txn.fetchall
202+
, cursor.fetchall
203203
)
204204

205205

0 commit comments

Comments
 (0)