Skip to content

Commit bc8e672

Browse files
committed
Clean up back_as
We were calling handle_back_as in two places where we really only needed one.
1 parent 9faf3f0 commit bc8e672

File tree

3 files changed

+70
-76
lines changed

3 files changed

+70
-76
lines changed

postgres/__init__.py

+62-13
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,13 @@
165165

166166
except ImportError: # Python 3
167167
import urllib.parse as urlparse
168+
from collections import namedtuple
168169

169170
import psycopg2
170171
from postgres.context_managers import ConnectionContextManager
171172
from postgres.context_managers import CursorContextManager
172-
from postgres.context_managers import handle_back_as
173-
from postgres.cursors import SimpleNamedTupleCursor, SimpleCursorBase
173+
from postgres.cursors import SimpleTupleCursor, SimpleNamedTupleCursor
174+
from postgres.cursors import SimpleDictCursor, SimpleCursorBase
174175
from postgres.orm import Model
175176
from psycopg2.extras import register_composite, CompositeCaster
176177
from psycopg2.pool import ThreadedConnectionPool as ConnectionPool
@@ -236,6 +237,12 @@ class NotRegistered(Exception):
236237
def __str__(self):
237238
return "The model {} is not registered.".format(self.args[0].__name__)
238239

240+
class BadBackAs(Exception):
241+
def __str__(self):
242+
return "Bad back_as: {}. Available back_as values are: tuple, " \
243+
"namedtuple, dict, or None (to use the default)." \
244+
.format(self.args[0])
245+
239246

240247
# The Main Event
241248
# ==============
@@ -522,10 +529,15 @@ def all(self, sql, parameters=None, back_as=None, *a, **kw):
522529
return cursor.all(sql, parameters)
523530

524531

525-
def get_cursor(self, back_as=None, *a, **kw):
532+
def get_cursor(self, *a, **kw):
526533
"""Return a :py:class:`~postgres.CursorContextManager` that uses
527534
our connection pool.
528535
536+
:param a: passed through to the :py:meth:`cursor` method of instances
537+
of the class returned by :py:func:`~postgres.make_Connection`
538+
:param kw: passed through to the :py:meth:`cursor` method of instances
539+
of the class returned by :py:func:`~postgres.make_Connection`
540+
529541
>>> with db.get_cursor() as cursor:
530542
... cursor.all("SELECT * FROM foo")
531543
...
@@ -554,7 +566,7 @@ def get_cursor(self, back_as=None, *a, **kw):
554566
transaction.
555567
556568
"""
557-
return CursorContextManager(self.pool, back_as=back_as, *a, **kw)
569+
return CursorContextManager(self.pool, *a, **kw)
558570

559571

560572
def get_connection(self):
@@ -680,14 +692,26 @@ def make_Connection(postgres):
680692
:returns: a :py:class:`Connection` class
681693
682694
The class defined and returned here will be linked to the instance of
683-
:py:class:`~postgres.Postgres` that is passed in as :py:attr:`postgres` and
684-
will use the :py:attr:`default_cursor_factory` attribute of that object.
685-
The :py:class:`~postgres.Postgres` instance will use this class as the
686-
:py:attr:`connection_factory` for its connection pool. The
687-
:py:meth:`cursor` method of this class accepts a :py:attr:`back_as`
688-
argument, which is processed according to
689-
:py:func:`~postgres.handle_back_as`. We also set client encoding to
690-
``UTF-8``.
695+
:py:class:`~postgres.Postgres` that is passed in as :py:attr:`postgres`,
696+
which will use this class as the :py:attr:`connection_factory` for its
697+
connection pool.
698+
699+
The :py:meth:`cursor` method of this class accepts a :py:attr:`back_as`
700+
keyword argument. If a :py:attr:`cursor_factory` keyword argument is also
701+
given, then any :py:attr:`back_as` is ignored and discarded. Valid values
702+
for :py:attr:`back_as` are :py:class:`tuple`, :py:class:`namedtuple`,
703+
:py:class:`dict` (or the strings ``tuple``, ``namedtuple``, and ``dict``),
704+
and :py:class:`None`. If the value of :py:attr:`back_as` is
705+
:py:class:`None`, then we'll use the default :py:attr:`cursor_factory` with
706+
which our parent :py:class:`~postgres.Postgres` instance was instantiated.
707+
If :py:attr:`back_as` is not :py:class:`None`, then we'll specify a
708+
:py:attr:`cursor_factory` that will result in records of the designated
709+
type: :py:class:`postgres.cursor.SimpleTupleCursor` for :py:class:`tuple`,
710+
:py:class:`postgres.cursor.SimpleNamedTupleCursor` for
711+
:py:class:`namedtuple`, and :py:class:`postgres.cursor.SimpleDictCursor`
712+
for :py:class:`dict`.
713+
714+
We also set client encoding to ``UTF-8``.
691715
692716
"""
693717
class Connection(psycopg2.extensions.connection):
@@ -700,11 +724,36 @@ def __init__(self, *a, **kw):
700724
def cursor(self, *a, **kw):
701725
if 'back_as' in kw:
702726
back_as = kw.pop('back_as')
703-
kw = handle_back_as(back_as, **kw)
727+
kw = self.handle_back_as(back_as, **kw)
704728
if 'cursor_factory' not in kw:
705729
kw['cursor_factory'] = self.postgres.default_cursor_factory
706730
return psycopg2.extensions.connection.cursor(self, *a, **kw)
707731

732+
def handle_back_as(self, back_as, **kw):
733+
734+
if 'cursor_factory' not in kw:
735+
736+
# Compute cursor_factory from back_as.
737+
# ====================================
738+
739+
registry = { tuple: SimpleTupleCursor
740+
, 'tuple': SimpleTupleCursor
741+
, namedtuple: SimpleNamedTupleCursor
742+
, 'namedtuple': SimpleNamedTupleCursor
743+
, dict: SimpleDictCursor
744+
, 'dict': SimpleDictCursor
745+
, None: None
746+
}
747+
748+
if back_as not in registry:
749+
raise BadBackAs(back_as)
750+
751+
cursor_factory = registry[back_as]
752+
if cursor_factory is not None:
753+
kw['cursor_factory'] = cursor_factory
754+
755+
return kw
756+
708757
return Connection
709758

710759

postgres/context_managers.py

+3-58
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
from __future__ import print_function, unicode_literals
22

3-
from collections import namedtuple
4-
5-
from postgres.cursors import SimpleTupleCursor, SimpleNamedTupleCursor
6-
from postgres.cursors import SimpleDictCursor
7-
8-
9-
class BadRecordType(Exception):
10-
def __str__(self):
11-
return "Bad back_as: {}. Available back_as values are: tuple, " \
12-
"namedtuple, dict, or None (to use the default)." \
13-
.format(self.args[0])
14-
153

164
class CursorContextManager(object):
175
"""Instantiated once per :py:func:`~postgres.Postgres.get_cursor`
@@ -21,10 +9,7 @@ class CursorContextManager(object):
219
2210
The return value of :py:func:`CursorContextManager.__enter__` is a
2311
:py:mod:`psycopg2` cursor. Any positional and keyword arguments to our
24-
constructor are passed through to the cursor constructor. If you pass
25-
:py:attr:`back_as` as a keyword argument then we'll infer a
26-
:py:attr:`cursor_factory` from that, though any explicit
27-
:py:attr:`cursor_factory` keyword argument will take precedence.
12+
constructor are passed through to the cursor constructor.
2813
2914
When the block starts, a connection is checked out of the connection pool
3015
and :py:attr:`autocommit` is set to :py:const:`False`. Then a cursor is
@@ -38,10 +23,10 @@ class CursorContextManager(object):
3823
3924
"""
4025

41-
def __init__(self, pool, back_as=None, *a, **kw):
26+
def __init__(self, pool, *a, **kw):
4227
self.pool = pool
4328
self.a = a
44-
self.kw = handle_back_as(back_as, **kw)
29+
self.kw = kw
4530
self.conn = None
4631

4732
def __enter__(self):
@@ -95,43 +80,3 @@ def __exit__(self, *exc_info):
9580
self.conn.rollback()
9681
self.conn.autocommit = False
9782
self.pool.putconn(self.conn)
98-
99-
100-
def handle_back_as(back_as, **kw):
101-
"""Add :py:attr:`cursor_factory` to :py:attr:`kw`, maybe.
102-
103-
Valid values for :py:attr:`back_as` are :py:class:`tuple`,
104-
:py:class:`namedtuple`, :py:class:`dict` (or the strings ``tuple``,
105-
``namedtuple``, and ``dict``), and :py:class:`None`. If the value of
106-
:py:attr:`back_as` is :py:class:`None`, then we won't insert any
107-
:py:attr:`cursor_factory` keyword argument. Otherwise we'll specify a
108-
:py:attr:`cursor_factory` that will result in records of the designated
109-
type: :py:class:`postgres.cursor.SimpleTupleCursor` for :py:class:`tuple`,
110-
:py:class:`postgres.cursor.SimpleNamedTupleCursor` for
111-
:py:class:`namedtuple`, and :py:class:`postgres.cursor.SimpleDictCursor`
112-
for :py:class:`dict`.
113-
114-
"""
115-
116-
if 'cursor_factory' not in kw:
117-
118-
# Compute cursor_factory from back_as.
119-
# ====================================
120-
121-
cursor_factory_registry = { tuple: SimpleTupleCursor
122-
, 'tuple': SimpleTupleCursor
123-
, namedtuple: SimpleNamedTupleCursor
124-
, 'namedtuple': SimpleNamedTupleCursor
125-
, dict: SimpleDictCursor
126-
, 'dict': SimpleDictCursor
127-
, None: None
128-
}
129-
130-
if back_as not in cursor_factory_registry:
131-
raise BadRecordType(back_as)
132-
133-
cursor_factory = cursor_factory_registry[back_as]
134-
if cursor_factory is not None:
135-
kw['cursor_factory'] = cursor_factory
136-
137-
return kw

postgres/cursors.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from inspect import isclass
1111

12-
from psycopg2.extensions import cursor as RegularCursor
12+
from psycopg2.extensions import cursor as TupleCursor
1313
from psycopg2.extras import NamedTupleCursor, RealDictCursor
1414

1515

@@ -49,7 +49,7 @@ class SimpleCursorBase(object):
4949
:py:class:`~postgres.Postgres` must subclass this base.
5050
5151
>>> from psycopg2.extras import LoggingCursor
52-
>>> from postgres.cursor import SimpleCursorBase
52+
>>> from postgres.cursors import SimpleCursorBase
5353
>>> class SimpleLoggingCursor(LoggingCursor, SimpleCursorBase):
5454
... pass
5555
...
@@ -59,7 +59,7 @@ class SimpleCursorBase(object):
5959
... )
6060
6161
If you try to use a cursor that doesn't subclass
62-
:py:class:`~postgres.cursor.SimpleCursorBase` as the default
62+
:py:class:`~postgres.cursors.SimpleCursorBase` as the default
6363
:py:attr:`cursor_factory` for a :py:class:`~postgres.Postgres` instance, we
6464
won't let you:
6565
@@ -69,7 +69,7 @@ class SimpleCursorBase(object):
6969
...
7070
Traceback (most recent call last):
7171
...
72-
postgres.NotASimpleCursor: We can only work with subclasses of postgres.cursor.SimpleCursorBase. LoggingCursor doesn't fit the bill.
72+
postgres.NotASimpleCursor: We can only work with subclasses of postgres.cursors.SimpleCursorBase. LoggingCursor doesn't fit the bill.
7373
7474
However, we do allow you to use whatever you want as the
7575
:py:attr:`cursor_factory` argument for individual calls:
@@ -152,7 +152,7 @@ def all(self, sql, parameters=None):
152152
return recs
153153

154154

155-
class SimpleTupleCursor(RegularCursor, SimpleCursorBase):
155+
class SimpleTupleCursor(TupleCursor, SimpleCursorBase):
156156
"""A simple cursor that returns tuples.
157157
"""
158158

0 commit comments

Comments
 (0)