summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>2018-10-04 12:24:27 +0100
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>2018-10-04 12:24:27 +0100
commit695c757dc3ea352e9f92e0ebc8d9d88d1a1ce97d (patch)
tree4ccbec296d24e34381bf6acffaaa10f49598023b
parent6b6b1a6e8d5d831fe17b168b4bf33d2c22bb2cc0 (diff)
parent81d6f7a7caabf7662f3acaedf91b77b913952762 (diff)
downloadpsycopg2-695c757dc3ea352e9f92e0ebc8d9d88d1a1ce97d.tar.gz
Merge branch 'stringification-of-ranges'
-rw-r--r--NEWS2
-rw-r--r--lib/_range.py13
-rwxr-xr-xtests/test_types_extras.py46
3 files changed, 61 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index 6602d93..206265a 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ What's new in psycopg 2.8
New features:
- Added `~psycopg2.extensions.encrypt_password()` function (:ticket:`#576`).
+- `!str()` on `~psycopg2.extras.Range` produces a human-readable representation
+ (:ticket:`#773`).
- `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
maintain columns order (:ticket:`#177`).
diff --git a/lib/_range.py b/lib/_range.py
index fd15a76..e72c878 100644
--- a/lib/_range.py
+++ b/lib/_range.py
@@ -62,6 +62,19 @@ class Range(object):
return "%s(%r, %r, %r)" % (self.__class__.__name__,
self._lower, self._upper, self._bounds)
+ def __str__(self):
+ if self._bounds is None:
+ return 'empty'
+
+ items = [
+ self._bounds[0],
+ str(self._lower),
+ ', ',
+ str(self._upper),
+ self._bounds[1]
+ ]
+ return ''.join(items)
+
@property
def lower(self):
"""The lower bound of the range. `!None` if empty or unbound."""
diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py
index cda163b..ca31c05 100755
--- a/tests/test_types_extras.py
+++ b/tests/test_types_extras.py
@@ -1386,6 +1386,52 @@ class RangeTestCase(unittest.TestCase):
r = Range(0, 4)
self.assertEqual(loads(dumps(r)), r)
+ def test_str(self):
+ '''
+ Range types should have a short and readable ``str`` implementation.
+
+ Using ``repr`` for all string conversions can be very unreadable for
+ longer types like ``DateTimeTZRange``.
+ '''
+ from psycopg2.extras import Range
+
+ # Using the "u" prefix to make sure we have the proper return types in
+ # Python2
+ expected = [
+ u'(0, 4)',
+ u'[0, 4]',
+ u'(0, 4]',
+ u'[0, 4)',
+ u'empty',
+ ]
+ results = []
+
+ converter = unicode if sys.version_info < (3, 0) else str
+
+ for bounds in ('()', '[]', '(]', '[)'):
+ r = Range(0, 4, bounds=bounds)
+ results.append(converter(r))
+
+ r = Range(empty=True)
+ results.append(converter(r))
+ self.assertEqual(results, expected)
+
+ def test_str_datetime(self):
+ '''
+ Date-Time ranges should return a human-readable string as well on
+ string conversion.
+ '''
+ from psycopg2.extras import DateTimeTZRange
+ from datetime import datetime
+ from psycopg2.tz import FixedOffsetTimezone
+ converter = unicode if sys.version_info < (3, 0) else str
+ tz = FixedOffsetTimezone(-5*60, "EST")
+ r = DateTimeTZRange(datetime(2010, 1, 1, tzinfo=tz),
+ datetime(2011, 1, 1, tzinfo=tz))
+ expected = u'[2010-01-01 00:00:00-05:00, 2011-01-01 00:00:00-05:00)'
+ result = converter(r)
+ self.assertEqual(result, expected)
+
def skip_if_no_range(f):
@wraps(f)