@@ -194,14 +194,25 @@ def url_to_dsn(url):
194
194
# Exceptions
195
195
# ==========
196
196
197
- class NotOne (Exception ):
198
- def __init__ (self , rowcount ):
199
- self .rowcount = rowcount
197
+ class OutOfBounds (Exception ):
198
+
199
+ def __init__ (self , n , lo , hi ):
200
+ self .n = n
201
+ self .lo = lo
202
+ self .hi = hi
203
+
200
204
def __str__ (self ):
201
- return "Got {0} rows instead of 1." .format (self .rowcount )
205
+ msg = "Got {n} rows; expecting "
206
+ if self .lo == self .hi :
207
+ msg += "exactly {lo}."
208
+ elif self .hi - self .lo == 1 :
209
+ msg += "{lo} or {hi}."
210
+ else :
211
+ msg += "between {lo} and {hi} (inclusive)."
212
+ return msg .format (** self .__dict__ )
202
213
203
- class TooFew (NotOne ): pass
204
- class TooMany (NotOne ): pass
214
+ class TooFew (OutOfBounds ): pass
215
+ class TooMany (OutOfBounds ): pass
205
216
206
217
207
218
# The Main Event
@@ -335,7 +346,7 @@ def one(self, sql, parameters=None, strict=None):
335
346
By default, :py:attr:`strict` ends up evaluating to :py:class:`True`,
336
347
in which case we raise :py:exc:`postgres.TooFew` or
337
348
:py:exc:`postgres.TooMany` if the number of rows returned isn't exactly
338
- one (both are subclasses of :py:exc:`postgres.NotOne `). You can
349
+ one (both are subclasses of :py:exc:`postgres.OutOfBounds `). You can
339
350
override this behavior per-call with the :py:attr:`strict` argument
340
351
here, or globally by passing :py:attr:`strict_one` to the
341
352
:py:class:`~postgres.Postgres` constructor. If you use both, the
@@ -357,16 +368,14 @@ def one(self, sql, parameters=None, strict=None):
357
368
else :
358
369
strict = self .strict_one # user default
359
370
360
- with self .get_cursor () as cursor :
361
- cursor .execute (sql , parameters )
362
-
363
- if strict :
364
- if cursor .rowcount < 1 :
365
- raise TooFew (cursor .rowcount )
366
- elif cursor .rowcount > 1 :
367
- raise TooMany (cursor .rowcount )
371
+ if strict :
372
+ out = self ._some (sql , parameters , 1 , 1 )
373
+ else :
374
+ with self .get_cursor () as cursor :
375
+ cursor .execute (sql , parameters )
376
+ out = cursor .fetchone ()
377
+ return out
368
378
369
- return cursor .fetchone ()
370
379
371
380
def one_or_zero (self , sql , parameters = None ):
372
381
"""Execute a query and return a single result or :py:class:`None`.
@@ -387,15 +396,24 @@ def one_or_zero(self, sql, parameters=None):
387
396
No blam yet.
388
397
389
398
"""
390
- with self .get_cursor () as cursor :
391
- cursor .execute (sql , parameters )
399
+ return self ._some (sql , parameters , 0 , 1 )
400
+
401
+
402
+ def _some (self , sql , parameters = None , lo = 0 , hi = 1 ):
403
+
404
+ # This is undocumented (and largely untested) because I think it's a
405
+ # rare case where this is wanted directly. It's here to make one and
406
+ # one_or_zero DRY. Help yourself to it now that you've found it. :^)
407
+
408
+ with self .get_cursor () as txn :
409
+ txn .execute (sql , parameters )
392
410
393
- if cursor .rowcount < 0 :
394
- raise TooFew (cursor .rowcount )
395
- elif cursor .rowcount > 1 :
396
- raise TooMany (cursor .rowcount )
411
+ if txn .rowcount < lo :
412
+ raise TooFew (txn .rowcount , lo , hi )
413
+ elif txn .rowcount > hi :
414
+ raise TooMany (txn .rowcount , lo , hi )
397
415
398
- return cursor .fetchone ()
416
+ return txn .fetchone ()
399
417
400
418
401
419
def get_cursor (self , * a , ** kw ):
0 commit comments