From d317977205763a34bb035b73ea6915e6bc62fb43 Mon Sep 17 00:00:00 2001 From: Michel Albert Date: Sun, 30 Sep 2018 10:15:55 +0200 Subject: [PATCH] Implement __str__ for range types --- lib/_range.py | 13 +++++++++++ tests/test_types_extras.py | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/lib/_range.py b/lib/_range.py index fd15a76b..30cda679 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._lower, self._upper, self._bounds) == (None, None, 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 cda163b6..ca31c05f 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)