Skip to content

Commit 69749f7

Browse files
committed
Start documenting get_cursor
1 parent 53c4c9e commit 69749f7

File tree

2 files changed

+52
-30
lines changed

2 files changed

+52
-30
lines changed

postgres.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
""":py:mod:`postgres` is a high-value abstraction over `psycopg2`_.
22
3-
Features:
4-
5-
- Use URLs instead of just connection strings.
6-
- Connections are pooled.
7-
- Calls are isolated in transactions.
8-
- Cursors yield dictionaries.
9-
- Text is unicode instead of bytestrings.
10-
113
124
Installation
135
------------
@@ -20,12 +12,12 @@
2012
Tutorial
2113
--------
2214
23-
Instantiate a :py:class:`Postgres` object when your application starts::
15+
Instantiate a :py:class:`Postgres` object when your application starts:
2416
2517
>>> from postgres import Postgres
2618
>>> db = Postgres("postgres://jdoe@localhost/testdb")
2719
28-
Use it to run SQL statements::
20+
Use it to run SQL statements:
2921
3022
>>> db.execute("CREATE TABLE foo (bar text)")
3123
>>> db.execute("INSERT INTO foo VALUES ('baz')")
@@ -43,6 +35,11 @@
4335
>>> db.fetchone("SELECT * FROM foo WHERE bar='blam'")
4436
None
4537
38+
Work with a cursor directly:
39+
40+
>>> with db.get_cursor('SELECT * FROM foo ORDER BY bar') as cursor:
41+
... results = cursor.fetchall()
42+
4643
4744
API
4845
---
@@ -92,20 +89,28 @@ def url_to_dsn(url):
9289

9390

9491
class Postgres(object):
95-
"""Interact with a PostgreSQL datastore.
92+
"""Interact with a `PostgreSQL <http://www.postgresql.org/>`_ datastore.
9693
9794
This is the main object that :py:mod:`postgres` provides, and you should
9895
have one instance per process. Here are the arguments:
9996
100-
- url - A "postgres://" url or a `PostgreSQL connection string
97+
- ``url`` - A ``postgres://`` URL or a `PostgreSQL connection string
10198
<http://www.postgresql.org/docs/current/static/libpq-connect.html>`_
10299
103-
- minconn - The minimum size of the connection pool
100+
- ``minconn`` - The minimum size of the connection pool
101+
102+
- ``maxconn`` - The maximum size of the connection pool
103+
104+
When instantiated, this object creates a `thread-safe connection pool
105+
<http://initd.org/psycopg/docs/pool.html#psycopg2.pool.ThreadedConnectionPool>`_,
106+
which opens ``minconn`` connections immediately and up to ``maxconn``
107+
according to demand.
108+
109+
Features:
104110
105-
- maxconn - The maximum size of the connection pool
111+
- Calls are isolated in transactions.
112+
- Get back unicode instead of bytestrings.
106113
107-
The connection pool is a `psycopg2.pool.ThreadedConnectionPool
108-
<http://initd.org/psycopg/docs/pool.html#psycopg2.pool.ThreadedConnectionPool>`_.
109114
110115
"""
111116

@@ -119,19 +124,19 @@ def __init__(self, url, minconn=1, maxconn=10):
119124
)
120125

121126
def execute(self, *a, **kw):
122-
"""Execute the query and discard the results.
127+
"""Execute the query and discard any results.
123128
"""
124129
with self.get_cursor(*a, **kw):
125130
pass
126131

127132
def fetchone(self, *a, **kw):
128-
"""Execute the query and return a single result or None.
133+
"""Execute the query and return a single result (``dict`` or ``None``).
129134
"""
130135
with self.get_cursor(*a, **kw) as cursor:
131136
return cursor.fetchone()
132137

133138
def fetchall(self, *a, **kw):
134-
"""Execute the query and yield the results.
139+
"""Execute the query and yield the results (``dict``).
135140
"""
136141
with self.get_cursor(*a, **kw) as cursor:
137142
for row in cursor:

tests.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,28 @@
99
DATABASE_URL = os.environ['DATABASE_URL']
1010

1111

12-
class Test(TestCase):
12+
class WithSchema(TestCase):
1313

1414
def setUp(self):
1515
self.db = Postgres(DATABASE_URL)
16-
self.db.execute("DROP SCHEMA public CASCADE")
16+
self.db.execute("DROP SCHEMA IF EXISTS public CASCADE")
1717
self.db.execute("CREATE SCHEMA public")
1818

19+
def tearDown(self):
20+
self.db.execute("DROP SCHEMA IF EXISTS public CASCADE")
21+
22+
23+
class WithData(WithSchema):
24+
25+
def setUp(self):
26+
WithSchema.setUp(self)
27+
self.db.execute("CREATE TABLE foo (bar text)")
28+
self.db.execute("INSERT INTO foo VALUES ('baz')")
29+
self.db.execute("INSERT INTO foo VALUES ('buz')")
30+
31+
32+
class TestExecute(WithSchema):
33+
1934
def test_execute_executes(self):
2035
self.db.execute("CREATE TABLE foo (bar text)")
2136
actual = list(self.db.fetchall("SELECT tablename FROM pg_tables "
@@ -28,23 +43,25 @@ def test_execute_inserts(self):
2843
actual = len(list(self.db.fetchone("SELECT * FROM foo ORDER BY bar")))
2944
assert actual == 1
3045

46+
47+
class TestFetch(WithData):
48+
3149
def test_fetchone_fetches_the_first_one(self):
32-
self.db.execute("CREATE TABLE foo (bar text)")
33-
self.db.execute("INSERT INTO foo VALUES ('baz')")
34-
self.db.execute("INSERT INTO foo VALUES ('buz')")
3550
actual = self.db.fetchone("SELECT * FROM foo ORDER BY bar")
3651
assert actual == {"bar": "baz"}
3752

3853
def test_fetchone_returns_None_if_theres_none(self):
39-
self.db.execute("CREATE TABLE foo (bar text)")
40-
self.db.execute("INSERT INTO foo VALUES ('baz')")
41-
self.db.execute("INSERT INTO foo VALUES ('buz')")
4254
actual = self.db.fetchone("SELECT * FROM foo WHERE bar='blam'")
4355
assert actual is None
4456

4557
def test_fetchall_fetches_all(self):
46-
self.db.execute("CREATE TABLE foo (bar text)")
47-
self.db.execute("INSERT INTO foo VALUES ('baz')")
48-
self.db.execute("INSERT INTO foo VALUES ('buz')")
4958
actual = list(self.db.fetchall("SELECT * FROM foo ORDER BY bar"))
5059
assert actual == [{"bar": "baz"}, {"bar": "buz"}]
60+
61+
62+
class TestCursor(WithData):
63+
64+
def test_get_cursor_gets_a_cursor(self):
65+
with self.db.get_cursor("SELECT * FROM foo ORDER BY bar") as cursor:
66+
actual = cursor.fetchall()
67+
assert actual == [{"bar": "baz"}, {"bar": "buz"}]

0 commit comments

Comments
 (0)