Skip to content

Commit 526400f

Browse files
committed
Remove one as well as the rows vestige
The one API was problematic. You could accidentally send DDL to it and get -1 for rowcount instead of 0, and then what? Better to scrap it in favor of one_or_zero, which you're less likely to confuse with run anyway.
1 parent c8475c2 commit 526400f

File tree

2 files changed

+17
-162
lines changed

2 files changed

+17
-162
lines changed

postgres.py

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -322,21 +322,15 @@ def all(self, sql, parameters=None):
322322
cursor.execute(sql, parameters)
323323
return cursor.fetchall()
324324

325-
def rows(self, *a, **kw):
326325

327-
# This is for backwards compatibility, see #16. It is stubbed instead
328-
# of aliased to avoid showing up in our docs via sphinx autodoc.
329-
330-
return self.all(*a, **kw)
331-
332-
333-
def one_or_zero(self, sql, parameters=None):
334-
"""Execute a query and return a single result or :py:class:`None`.
326+
def one_or_zero(self, sql, parameters=None, zero=None):
327+
"""Execute a query and return a single result or a default value.
335328
336329
:param unicode sql: the SQL statement to execute
337330
:param parameters: the bind parameters for the SQL statement
338331
:type parameters: dict or tuple
339-
:returns: a single row or :py:const:`None`
332+
:returns: a single row or the value of the :py:attr:`zero` argument
333+
:param zero: the value to return if zero results are found
340334
:raises: :py:exc:`~postgres.TooFew` or :py:exc:`~postgres.TooMany`
341335
342336
Use this for the common case where there should only be one record, but
@@ -349,64 +343,18 @@ def one_or_zero(self, sql, parameters=None):
349343
No blam yet.
350344
351345
"""
352-
return self._some(sql, parameters, 0, 1)
353-
354-
355-
def one(self, sql, parameters=None, strict=None):
356-
357-
# I'm half-considering dropping this. Now that one_or_zero exists, this
358-
# is really only useful for what should really be called db.first, and
359-
# in that case why aren't you using a LIMIT 1?
360-
361-
"""Execute a query and return a single result.
362-
363-
:param unicode sql: the SQL statement to execute
364-
:param parameters: the bind parameters for the SQL statement
365-
:type parameters: dict or tuple
366-
:param strict: whether to raise when there isn't exactly one result
367-
:type strict: :py:class:`bool`
368-
:returns: a single row or :py:const:`None`
369-
:raises: :py:exc:`~postgres.TooFew` or :py:exc:`~postgres.TooMany`
370-
371-
By default, :py:attr:`strict` ends up evaluating to :py:class:`True`,
372-
in which case we raise :py:exc:`postgres.TooFew` or
373-
:py:exc:`postgres.TooMany` if the number of rows returned isn't exactly
374-
one (both are subclasses of :py:exc:`postgres.OutOfBounds`). You can
375-
override this behavior per-call with the :py:attr:`strict` argument
376-
here, or globally by passing :py:attr:`strict_one` to the
377-
:py:class:`~postgres.Postgres` constructor. If you use both, the
378-
:py:attr:`strict` argument here wins. If you pass :py:class:`False`
379-
for :py:attr:`strict`, then we return :py:class:`None` if there are no
380-
results, and the first if there is more than one.
381-
382-
>>> row = db.one("SELECT * FROM foo WHERE bar='baz'")
383-
>>> print(row["bar"])
384-
baz
385-
386-
"""
387-
if strict not in (True, False, None):
388-
raise ValueError("strict must be True, False, or None.")
389-
390-
if strict is None:
391-
if self.strict_one is None:
392-
strict = True # library default
393-
else:
394-
strict = self.strict_one # user default
395-
396-
if strict:
397-
out = self._some(sql, parameters, 1, 1)
398-
else:
399-
with self.get_cursor() as cursor:
400-
cursor.execute(sql, parameters)
401-
out = cursor.fetchone()
346+
out = self._some(sql, parameters, 0, 1)
347+
if out is None:
348+
out = zero
402349
return out
403350

404351

405352
def _some(self, sql, parameters=None, lo=0, hi=1):
406353

407354
# This is undocumented (and largely untested) because I think it's a
408-
# rare case where this is wanted directly. It's here to make one and
409-
# one_or_zero DRY. Help yourself to it now that you've found it. :^)
355+
# rare case where this is wanted directly. It was added to make one and
356+
# one_or_zero DRY when we had one. Help yourself to it now that you've
357+
# found it. :^)
410358

411359
with self.get_transaction() as txn:
412360
txn.execute(sql, parameters)

tests.py

Lines changed: 7 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -109,106 +109,6 @@ def test_TooMany_message_is_helpful_for_a_range(self):
109109
"Got 4 rows; expecting between 1 and 3 (inclusive)."
110110

111111

112-
class TestOneRollsBack(WithData):
113-
114-
def test_one_rollsback_on_error(self):
115-
try:
116-
self.db.one("UPDATE foo SET bar='bum' RETURNING *", strict=True)
117-
except TooMany:
118-
pass
119-
actual = self.db.all("SELECT * FROM foo WHERE bar='bum'")
120-
assert actual == []
121-
122-
123-
class TestOne(WithData):
124-
125-
def test_with_strict_True_one_raises_TooFew(self):
126-
self.assertRaises( TooFew
127-
, self.db.one
128-
, "SELECT * FROM foo WHERE bar='blah'"
129-
, strict=True
130-
)
131-
132-
def test_with_strict_True_one_fetches_the_one(self):
133-
actual = self.db.one("SELECT * FROM foo WHERE bar='baz'", strict=True)
134-
assert actual == {"bar": "baz"}
135-
136-
def test_with_strict_True_one_raises_TooMany(self):
137-
self.assertRaises( TooMany
138-
, self.db.one
139-
, "SELECT * FROM foo"
140-
, strict=True
141-
)
142-
143-
144-
def test_with_strict_False_one_returns_None_if_theres_none(self):
145-
actual = self.db.one("SELECT * FROM foo WHERE bar='nun'", strict=False)
146-
assert actual is None
147-
148-
def test_with_strict_False_one_fetches_the_first_one(self):
149-
actual = self.db.one("SELECT * FROM foo ORDER BY bar", strict=False)
150-
assert actual == {"bar": "baz"}
151-
152-
153-
class TestOne_StrictOneNone(TestOne):
154-
155-
def setUp(self):
156-
WithData.setUp(self)
157-
self.db.strict_one = None
158-
159-
def test_one_raises_TooFew(self):
160-
self.assertRaises( TooFew
161-
, self.db.one
162-
, "SELECT * FROM foo WHERE bar='nun'"
163-
)
164-
165-
def test_one_returns_one(self):
166-
actual = self.db.one("SELECT * FROM foo WHERE bar='baz'")
167-
assert actual == {"bar": "baz"}
168-
169-
def test_one_raises_TooMany(self):
170-
self.assertRaises(TooMany, self.db.one, "SELECT * FROM foo")
171-
172-
173-
class TestOne_StrictOneFalse(TestOne):
174-
175-
def setUp(self):
176-
WithData.setUp(self)
177-
self.db.strict_one = False
178-
179-
def test_one_returns_None(self):
180-
actual = self.db.one("SELECT * FROM foo WHERE bar='nun'")
181-
assert actual is None
182-
183-
def test_one_returns_one(self):
184-
actual = self.db.one("SELECT * FROM foo WHERE bar='baz'")
185-
assert actual == {"bar": "baz"}
186-
187-
def test_one_returns_first_one(self):
188-
actual = self.db.one("SELECT * FROM foo ORDER BY bar")
189-
assert actual == {"bar": "baz"}
190-
191-
192-
class TestOne_StrictOneTrue(TestOne):
193-
194-
def setUp(self):
195-
WithData.setUp(self)
196-
self.db.strict_one = True
197-
198-
def test_one_raises_TooFew(self):
199-
self.assertRaises( TooFew
200-
, self.db.one
201-
, "SELECT * FROM foo WHERE bar='nun'"
202-
)
203-
204-
def test_one_returns_one(self):
205-
actual = self.db.one("SELECT * FROM foo WHERE bar='baz'")
206-
assert actual == {"bar": "baz"}
207-
208-
def test_one_raises_TooMany(self):
209-
self.assertRaises(TooMany, self.db.one, "SELECT * FROM foo")
210-
211-
212112
# db.one_or_zero
213113
# ==============
214114

@@ -234,6 +134,13 @@ def test_one_or_zero_returns_None(self):
234134
actual = self.db.one_or_zero("SELECT * FROM foo WHERE bar='blam'")
235135
assert actual is None
236136

137+
def test_one_or_zero_returns_whatever(self):
138+
class WHEEEE: pass
139+
actual = self.db.one_or_zero( "SELECT * FROM foo WHERE bar='blam'"
140+
, zero=WHEEEE
141+
)
142+
assert actual is WHEEEE
143+
237144
def test_one_or_zero_returns_one(self):
238145
actual = self.db.one_or_zero("SELECT * FROM foo WHERE bar='baz'")
239146
assert actual == {"bar": "baz"}

0 commit comments

Comments
 (0)