source: trunk/tests/test_classic_functions.py

Last change on this file was 997, checked in by cito, 3 months ago

Some more IDE hints

  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 40.1 KB
Line 
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4"""Test the classic PyGreSQL interface.
5
6Sub-tests for the module functions and constants.
7
8Contributed by Christoph Zwerschke.
9
10These tests do not need a database to test against.
11"""
12
13try:
14    import unittest2 as unittest  # for Python < 2.7
15except ImportError:
16    import unittest
17
18import json
19import re
20
21import pg  # the module under test
22
23from datetime import timedelta
24
25try:  # noinspection PyUnresolvedReferences
26    long
27except NameError:  # Python >= 3.0
28    long = int
29
30try:  # noinspection PyUnresolvedReferences
31    unicode
32except NameError:  # Python >= 3.0
33    unicode = str
34
35
36class TestHasConnect(unittest.TestCase):
37    """Test existence of basic pg module functions."""
38
39    def testhasPgError(self):
40        self.assertTrue(issubclass(pg.Error, Exception))
41
42    def testhasPgWarning(self):
43        self.assertTrue(issubclass(pg.Warning, Exception))
44
45    def testhasPgInterfaceError(self):
46        self.assertTrue(issubclass(pg.InterfaceError, pg.Error))
47
48    def testhasPgDatabaseError(self):
49        self.assertTrue(issubclass(pg.DatabaseError, pg.Error))
50
51    def testhasPgInternalError(self):
52        self.assertTrue(issubclass(pg.InternalError, pg.DatabaseError))
53
54    def testhasPgOperationalError(self):
55        self.assertTrue(issubclass(pg.OperationalError, pg.DatabaseError))
56
57    def testhasPgProgrammingError(self):
58        self.assertTrue(issubclass(pg.ProgrammingError, pg.DatabaseError))
59
60    def testhasPgIntegrityError(self):
61        self.assertTrue(issubclass(pg.IntegrityError, pg.DatabaseError))
62
63    def testhasPgDataError(self):
64        self.assertTrue(issubclass(pg.DataError, pg.DatabaseError))
65
66    def testhasPgNotSupportedError(self):
67        self.assertTrue(issubclass(pg.NotSupportedError, pg.DatabaseError))
68
69    def testhasPgInvalidResultError(self):
70        self.assertTrue(issubclass(pg.InvalidResultError, pg.DataError))
71
72    def testhasPgNoResultError(self):
73        self.assertTrue(issubclass(pg.NoResultError, pg.InvalidResultError))
74
75    def testhasPgMultipleResultsError(self):
76        self.assertTrue(
77            issubclass(pg.MultipleResultsError, pg.InvalidResultError))
78
79    def testhasConnect(self):
80        self.assertTrue(callable(pg.connect))
81
82    def testhasEscapeString(self):
83        self.assertTrue(callable(pg.escape_string))
84
85    def testhasEscapeBytea(self):
86        self.assertTrue(callable(pg.escape_bytea))
87
88    def testhasUnescapeBytea(self):
89        self.assertTrue(callable(pg.unescape_bytea))
90
91    def testDefHost(self):
92        d0 = pg.get_defhost()
93        d1 = 'pgtesthost'
94        pg.set_defhost(d1)
95        self.assertEqual(pg.get_defhost(), d1)
96        pg.set_defhost(d0)
97        self.assertEqual(pg.get_defhost(), d0)
98
99    def testDefPort(self):
100        d0 = pg.get_defport()
101        d1 = 1234
102        pg.set_defport(d1)
103        self.assertEqual(pg.get_defport(), d1)
104        if d0 is None:
105            d0 = -1
106        pg.set_defport(d0)
107        if d0 == -1:
108            d0 = None
109        self.assertEqual(pg.get_defport(), d0)
110
111    def testDefOpt(self):
112        d0 = pg.get_defopt()
113        d1 = '-h pgtesthost -p 1234'
114        pg.set_defopt(d1)
115        self.assertEqual(pg.get_defopt(), d1)
116        pg.set_defopt(d0)
117        self.assertEqual(pg.get_defopt(), d0)
118
119    def testDefBase(self):
120        d0 = pg.get_defbase()
121        d1 = 'pgtestdb'
122        pg.set_defbase(d1)
123        self.assertEqual(pg.get_defbase(), d1)
124        pg.set_defbase(d0)
125        self.assertEqual(pg.get_defbase(), d0)
126
127
128class TestParseArray(unittest.TestCase):
129    """Test the array parser."""
130
131    test_strings = [
132        ('', str, ValueError),
133        ('{}', None, []),
134        ('{}', str, []),
135        ('   {   }   ', None, []),
136        ('{', str, ValueError),
137        ('{{}', str, ValueError),
138        ('{}{', str, ValueError),
139        ('[]', str, ValueError),
140        ('()', str, ValueError),
141        ('{[]}', str, ['[]']),
142        ('{hello}', int, ValueError),
143        ('{42}', int, [42]),
144        ('{ 42 }', int, [42]),
145        ('{42', int, ValueError),
146        ('{ 42 ', int, ValueError),
147        ('{hello}', str, ['hello']),
148        ('{ hello }', str, ['hello']),
149        ('{hi}   ', str, ['hi']),
150        ('{hi}   ?', str, ValueError),
151        ('{null}', str, [None]),
152        (' { NULL } ', str, [None]),
153        ('   {   NULL   }   ', str, [None]),
154        (' { not null } ', str, ['not null']),
155        (' { not NULL } ', str, ['not NULL']),
156        (' {"null"} ', str, ['null']),
157        (' {"NULL"} ', str, ['NULL']),
158        ('{Hi!}', str, ['Hi!']),
159        ('{"Hi!"}', str, ['Hi!']),
160        ('{" Hi! "}', str, [' Hi! ']),
161        ('{a"}', str, ValueError),
162        ('{"b}', str, ValueError),
163        ('{a"b}', str, ValueError),
164        (r'{a\"b}', str, ['a"b']),
165        (r'{a\,b}', str, ['a,b']),
166        (r'{a\bc}', str, ['abc']),
167        (r'{"a\bc"}', str, ['abc']),
168        (r'{\a\b\c}', str, ['abc']),
169        (r'{"\a\b\c"}', str, ['abc']),
170        (r'{"a"b"}', str, ValueError),
171        (r'{"a""b"}', str, ValueError),
172        (r'{"a\"b"}', str, ['a"b']),
173        ('{"{}"}', str, ['{}']),
174        (r'{\{\}}', str, ['{}']),
175        ('{"{a,b,c}"}', str, ['{a,b,c}']),
176        ("{'abc'}", str, ["'abc'"]),
177        ('{"abc"}', str, ['abc']),
178        (r'{\"abc\"}', str, ['"abc"']),
179        (r"{\'abc\'}", str, ["'abc'"]),
180        (r"{abc,d,efg}", str, ['abc', 'd', 'efg']),
181        ('{Hello World!}', str, ['Hello World!']),
182        ('{Hello, World!}', str, ['Hello', 'World!']),
183        ('{Hello,\ World!}', str, ['Hello', ' World!']),
184        ('{Hello\, World!}', str, ['Hello, World!']),
185        ('{"Hello World!"}', str, ['Hello World!']),
186        ('{this, should, be, null}', str, ['this', 'should', 'be', None]),
187        ('{This, should, be, NULL}', str, ['This', 'should', 'be', None]),
188        ('{3, 2, 1, null}', int, [3, 2, 1, None]),
189        ('{3, 2, 1, NULL}', int, [3, 2, 1, None]),
190        ('{3,17,51}', int, [3, 17, 51]),
191        (' { 3 , 17 , 51 } ', int, [3, 17, 51]),
192        ('{3,17,51}', str, ['3', '17', '51']),
193        (' { 3 , 17 , 51 } ', str, ['3', '17', '51']),
194        ('{1,"2",abc,"def"}', str, ['1', '2', 'abc', 'def']),
195        ('{{}}', int, [[]]),
196        ('{{},{}}', int, [[], []]),
197        ('{ {} , {} , {} }', int, [[], [], []]),
198        ('{ {} , {} , {} , }', int, ValueError),
199        ('{{{1,2,3},{4,5,6}}}', int, [[[1, 2, 3], [4, 5, 6]]]),
200        ('{{1,2,3},{4,5,6},{7,8,9}}', int, [[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
201        ('{20000, 25000, 25000, 25000}', int, [20000, 25000, 25000, 25000]),
202        ('{{{17,18,19},{14,15,16},{11,12,13}},'
203         '{{27,28,29},{24,25,26},{21,22,23}},'
204         '{{37,38,39},{34,35,36},{31,32,33}}}', int,
205            [[[17, 18, 19], [14, 15, 16], [11, 12, 13]],
206             [[27, 28, 29], [24, 25, 26], [21, 22, 23]],
207             [[37, 38, 39], [34, 35, 36], [31, 32, 33]]]),
208        ('{{"breakfast", "consulting"}, {"meeting", "lunch"}}', str,
209            [['breakfast', 'consulting'], ['meeting', 'lunch']]),
210        ('[1:3]={1,2,3}', int, [1, 2, 3]),
211        ('[-1:1]={1,2,3}', int, [1, 2, 3]),
212        ('[-1:+1]={1,2,3}', int, [1, 2, 3]),
213        ('[-3:-1]={1,2,3}', int, [1, 2, 3]),
214        ('[+1:+3]={1,2,3}', int, [1, 2, 3]),
215        ('[0:2]={1,2,3}', int, [1, 2, 3]),
216        ('[7:9]={1,2,3}', int, [1, 2, 3]),
217        ('[]={1,2,3}', int, ValueError),
218        ('[1:]={1,2,3}', int, ValueError),
219        ('[:3]={1,2,3}', int, ValueError),
220        ('[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}',
221            int, [[[1, 2, 3], [4, 5, 6]]]),
222        ('  [1:1]  [-2:-1]  [3:5]  =  { { { 1 , 2 , 3 }, {4 , 5 , 6 } } }',
223            int, [[[1, 2, 3], [4, 5, 6]]]),
224        ('[1:1][3:5]={{1,2,3},{4,5,6}}', int, [[1, 2, 3], [4, 5, 6]]),
225        ('[3:5]={{1,2,3},{4,5,6}}', int, ValueError),
226        ('[1:1][-2:-1][3:5]={{1,2,3},{4,5,6}}', int, ValueError)]
227
228    def testParserParams(self):
229        f = pg.cast_array
230        self.assertRaises(TypeError, f)
231        self.assertRaises(TypeError, f, None)
232        self.assertRaises(TypeError, f, '{}', 1)
233        self.assertRaises(TypeError, f, '{}', b',',)
234        self.assertRaises(TypeError, f, '{}', None, None)
235        self.assertRaises(TypeError, f, '{}', None, 1)
236        self.assertRaises(TypeError, f, '{}', None, b'')
237        self.assertRaises(ValueError, f, '{}', None, b'\\')
238        self.assertRaises(ValueError, f, '{}', None, b'{')
239        self.assertRaises(ValueError, f, '{}', None, b'}')
240        self.assertRaises(TypeError, f, '{}', None, b',;')
241        self.assertEqual(f('{}'), [])
242        self.assertEqual(f('{}', None), [])
243        self.assertEqual(f('{}', None, b';'), [])
244        self.assertEqual(f('{}', str), [])
245        self.assertEqual(f('{}', str, b';'), [])
246
247    def testParserSimple(self):
248        r = pg.cast_array('{a,b,c}')
249        self.assertIsInstance(r, list)
250        self.assertEqual(len(r), 3)
251        self.assertEqual(r, ['a', 'b', 'c'])
252
253    def testParserNested(self):
254        f = pg.cast_array
255        r = f('{{a,b,c}}')
256        self.assertIsInstance(r, list)
257        self.assertEqual(len(r), 1)
258        r = r[0]
259        self.assertIsInstance(r, list)
260        self.assertEqual(len(r), 3)
261        self.assertEqual(r, ['a', 'b', 'c'])
262        self.assertRaises(ValueError, f, '{a,{b,c}}')
263        r = f('{{a,b},{c,d}}')
264        self.assertIsInstance(r, list)
265        self.assertEqual(len(r), 2)
266        r = r[1]
267        self.assertIsInstance(r, list)
268        self.assertEqual(len(r), 2)
269        self.assertEqual(r, ['c', 'd'])
270        r = f('{{a},{b},{c}}')
271        self.assertIsInstance(r, list)
272        self.assertEqual(len(r), 3)
273        r = r[1]
274        self.assertIsInstance(r, list)
275        self.assertEqual(len(r), 1)
276        self.assertEqual(r[0], 'b')
277        r = f('{{{{{{{abc}}}}}}}')
278        for i in range(7):
279            self.assertIsInstance(r, list)
280            self.assertEqual(len(r), 1)
281            r = r[0]
282        self.assertEqual(r, 'abc')
283
284    def testParserTooDeeplyNested(self):
285        f = pg.cast_array
286        for n in 3, 5, 9, 12, 16, 32, 64, 256:
287            r = '%sa,b,c%s' % ('{' * n, '}' * n)
288            if n > 16:  # hard coded maximum depth
289                self.assertRaises(ValueError, f, r)
290            else:
291                r = f(r)
292                for i in range(n - 1):
293                    self.assertIsInstance(r, list)
294                    self.assertEqual(len(r), 1)
295                    r = r[0]
296                self.assertEqual(len(r), 3)
297                self.assertEqual(r, ['a', 'b', 'c'])
298
299    def testParserCast(self):
300        f = pg.cast_array
301        self.assertEqual(f('{1}'), ['1'])
302        self.assertEqual(f('{1}', None), ['1'])
303        self.assertEqual(f('{1}', int), [1])
304        self.assertEqual(f('{1}', str), ['1'])
305        self.assertEqual(f('{a}'), ['a'])
306        self.assertEqual(f('{a}', None), ['a'])
307        self.assertRaises(ValueError, f, '{a}', int)
308        self.assertEqual(f('{a}', str), ['a'])
309        cast = lambda s: '%s is ok' % s
310        self.assertEqual(f('{a}', cast), ['a is ok'])
311
312    def testParserDelim(self):
313        f = pg.cast_array
314        self.assertEqual(f('{1,2}'), ['1', '2'])
315        self.assertEqual(f('{1,2}', delim=b','), ['1', '2'])
316        self.assertEqual(f('{1;2}'), ['1;2'])
317        self.assertEqual(f('{1;2}', delim=b';'), ['1', '2'])
318        self.assertEqual(f('{1,2}', delim=b';'), ['1,2'])
319
320    def testParserWithData(self):
321        f = pg.cast_array
322        for string, cast, expected in self.test_strings:
323            if expected is ValueError:
324                self.assertRaises(ValueError, f, string, cast)
325            else:
326                self.assertEqual(f(string, cast), expected)
327
328    def testParserWithoutCast(self):
329        f = pg.cast_array
330
331        for string, cast, expected in self.test_strings:
332            if cast is not str:
333                continue
334            if expected is ValueError:
335                self.assertRaises(ValueError, f, string)
336            else:
337                self.assertEqual(f(string), expected)
338
339    def testParserWithDifferentDelimiter(self):
340        f = pg.cast_array
341
342        def replace_comma(value):
343            if isinstance(value, str):
344                return value.replace(',', ';')
345            elif isinstance(value, list):
346                return [replace_comma(v) for v in value]
347            else:
348                return value
349
350        for string, cast, expected in self.test_strings:
351            string = replace_comma(string)
352            if expected is ValueError:
353                self.assertRaises(ValueError, f, string, cast)
354            else:
355                expected = replace_comma(expected)
356                self.assertEqual(f(string, cast, b';'), expected)
357
358
359class TestParseRecord(unittest.TestCase):
360    """Test the record parser."""
361
362    test_strings = [
363        ('', None, ValueError),
364        ('', str, ValueError),
365        ('(', None, ValueError),
366        ('(', str, ValueError),
367        ('()', None, (None,)),
368        ('()', str, (None,)),
369        ('()', int, (None,)),
370        ('(,)', str, (None, None)),
371        ('( , )', str, (' ', ' ')),
372        ('(")', None, ValueError),
373        ('("")', None, ('',)),
374        ('("")', str, ('',)),
375        ('("")', int, ValueError),
376        ('("" )', None, (' ',)),
377        ('("" )', str, (' ',)),
378        ('("" )', int, ValueError),
379        ('    ()    ', None, (None,)),
380        ('   (   )   ', None, ('   ',)),
381        ('(', str, ValueError),
382        ('(()', str, ('(',)),
383        ('(())', str, ValueError),
384        ('()(', str, ValueError),
385        ('()()', str, ValueError),
386        ('[]', str, ValueError),
387        ('{}', str, ValueError),
388        ('([])', str, ('[]',)),
389        ('(hello)', int, ValueError),
390        ('(42)', int, (42,)),
391        ('( 42 )', int, (42,)),
392        ('(  42)', int, (42,)),
393        ('(42)', str, ('42',)),
394        ('( 42 )', str, (' 42 ',)),
395        ('(  42)', str, ('  42',)),
396        ('(42', int, ValueError),
397        ('( 42 ', int, ValueError),
398        ('(hello)', str, ('hello',)),
399        ('( hello )', str, (' hello ',)),
400        ('(hello))', str, ValueError),
401        ('   (hello)   ', str, ('hello',)),
402        ('   (hello)   )', str, ValueError),
403        ('(hello)?', str, ValueError),
404        ('(null)', str, ('null',)),
405        ('(null)', int, ValueError),
406        (' ( NULL ) ', str, (' NULL ',)),
407        ('   (   NULL   )   ', str, ('   NULL   ',)),
408        (' ( null null ) ', str, (' null null ',)),
409        (' ("null") ', str, ('null',)),
410        (' ("NULL") ', str, ('NULL',)),
411        ('(Hi!)', str, ('Hi!',)),
412        ('("Hi!")', str, ('Hi!',)),
413        ("('Hi!')", str, ("'Hi!'",)),
414        ('(" Hi! ")', str, (' Hi! ',)),
415        ('("Hi!" )', str, ('Hi! ',)),
416        ('( "Hi!")', str, (' Hi!',)),
417        ('( "Hi!" )', str, (' Hi! ',)),
418        ('( ""Hi!"" )', str, (' Hi! ',)),
419        ('( """Hi!""" )', str, (' "Hi!" ',)),
420        ('(a")', str, ValueError),
421        ('("b)', str, ValueError),
422        ('("a" "b)', str, ValueError),
423        ('("a" "b")', str, ('a b',)),
424        ('( "a" "b" "c" )', str, (' a b c ',)),
425        ('(  "a"  "b"  "c"  )', str, ('  a  b  c  ',)),
426        ('(  "a,b"  "c,d"  )', str, ('  a,b  c,d  ',)),
427        ('( "(a,b,c)" d, e, "f,g")', str, (' (a,b,c) d', ' e', ' f,g')),
428        ('(a",b,c",d,"e,f")', str, ('a,b,c', 'd', 'e,f')),
429        ('( """a,b""", ""c,d"", "e,f", "g", ""h"", """i""")', str,
430            (' "a,b"', ' c', 'd', ' e,f', ' g', ' h', ' "i"')),
431        ('(a",b)",c"),(d,e)",f,g)', str, ('a,b)', 'c),(d,e)', 'f', 'g')),
432        ('(a"b)', str, ValueError),
433        (r'(a\"b)', str, ('a"b',)),
434        ('(a""b)', str, ('ab',)),
435        ('("a""b")', str, ('a"b',)),
436        (r'(a\,b)', str, ('a,b',)),
437        (r'(a\bc)', str, ('abc',)),
438        (r'("a\bc")', str, ('abc',)),
439        (r'(\a\b\c)', str, ('abc',)),
440        (r'("\a\b\c")', str, ('abc',)),
441        ('("()")', str, ('()',)),
442        (r'(\,)', str, (',',)),
443        (r'(\(\))', str, ('()',)),
444        (r'(\)\()', str, (')(',)),
445        ('("(a,b,c)")', str, ('(a,b,c)',)),
446        ("('abc')", str, ("'abc'",)),
447        ('("abc")', str, ('abc',)),
448        (r'(\"abc\")', str, ('"abc"',)),
449        (r"(\'abc\')", str, ("'abc'",)),
450        ('(Hello World!)', str, ('Hello World!',)),
451        ('(Hello, World!)', str, ('Hello', ' World!',)),
452        ('(Hello,\ World!)', str, ('Hello', ' World!',)),
453        ('(Hello\, World!)', str, ('Hello, World!',)),
454        ('("Hello World!")', str, ('Hello World!',)),
455        ("(this,shouldn't,be,null)", str, ('this', "shouldn't", 'be', 'null')),
456        ('(null,should,be,)', str, ('null', 'should', 'be', None)),
457        ('(abcABC0123!?+-*/=&%$\\\\\'\\"{[]}"""":;\\,,)', str,
458            ('abcABC0123!?+-*/=&%$\\\'"{[]}":;,', None)),
459        ('(3, 2, 1,)', int, (3, 2, 1, None)),
460        ('(3, 2, 1, )', int, ValueError),
461        ('(, 1, 2, 3)', int, (None, 1, 2, 3)),
462        ('( , 1, 2, 3)', int, ValueError),
463        ('(,1,,2,,3,)', int, (None, 1, None, 2, None, 3, None)),
464        ('(3,17,51)', int, (3, 17, 51)),
465        (' ( 3 , 17 , 51 ) ', int, (3, 17, 51)),
466        ('(3,17,51)', str, ('3', '17', '51')),
467        (' ( 3 , 17 , 51 ) ', str, (' 3 ', ' 17 ', ' 51 ')),
468        ('(1,"2",abc,"def")', str, ('1', '2', 'abc', 'def')),
469        ('(())', str, ValueError),
470        ('()))', str, ValueError),
471        ('()()', str, ValueError),
472        ('((()', str, ('((',)),
473        ('(())', int, ValueError),
474        ('((),())', str, ValueError),
475        ('("()","()")', str, ('()', '()')),
476        ('( " () , () , () " )', str, ('  () , () , ()  ',)),
477        ('(20000, 25000, 25000, 25000)', int, (20000, 25000, 25000, 25000)),
478        ('("breakfast","consulting","meeting","lunch")', str,
479            ('breakfast', 'consulting', 'meeting', 'lunch')),
480        ('("breakfast","consulting","meeting","lunch")',
481            (str, str, str), ValueError),
482        ('("breakfast","consulting","meeting","lunch")', (str, str, str, str),
483            ('breakfast', 'consulting', 'meeting', 'lunch')),
484        ('("breakfast","consulting","meeting","lunch")',
485            (str, str, str, str, str), ValueError),
486        ('("fuzzy dice",42,1.9375)', None, ('fuzzy dice', '42', '1.9375')),
487        ('("fuzzy dice",42,1.9375)', str, ('fuzzy dice', '42', '1.9375')),
488        ('("fuzzy dice",42,1.9375)', int, ValueError),
489        ('("fuzzy dice",42,1.9375)', (str, int, float),
490            ('fuzzy dice', 42, 1.9375)),
491        ('("fuzzy dice",42,1.9375)', (str, int), ValueError),
492        ('("fuzzy dice",42,1.9375)', (str, int, float, str), ValueError),
493        ('("fuzzy dice",42,)', (str, int, float), ('fuzzy dice', 42, None)),
494        ('("fuzzy dice",42,)', (str, int), ValueError),
495        ('("",42,)', (str, int, float), ('', 42, None)),
496        ('("fuzzy dice","",1.9375)', (str, int, float), ValueError),
497        ('(fuzzy dice,"42","1.9375")', (str, int, float),
498            ('fuzzy dice', 42, 1.9375))]
499
500    def testParserParams(self):
501        f = pg.cast_record
502        self.assertRaises(TypeError, f)
503        self.assertRaises(TypeError, f, None)
504        self.assertRaises(TypeError, f, '()', 1)
505        self.assertRaises(TypeError, f, '()', b',',)
506        self.assertRaises(TypeError, f, '()', None, None)
507        self.assertRaises(TypeError, f, '()', None, 1)
508        self.assertRaises(TypeError, f, '()', None, b'')
509        self.assertRaises(ValueError, f, '()', None, b'\\')
510        self.assertRaises(ValueError, f, '()', None, b'(')
511        self.assertRaises(ValueError, f, '()', None, b')')
512        self.assertRaises(TypeError, f, '{}', None, b',;')
513        self.assertEqual(f('()'), (None,))
514        self.assertEqual(f('()', None), (None,))
515        self.assertEqual(f('()', None, b';'), (None,))
516        self.assertEqual(f('()', str), (None,))
517        self.assertEqual(f('()', str, b';'), (None,))
518
519    def testParserSimple(self):
520        r = pg.cast_record('(a,b,c)')
521        self.assertIsInstance(r, tuple)
522        self.assertEqual(len(r), 3)
523        self.assertEqual(r, ('a', 'b', 'c'))
524
525    def testParserNested(self):
526        f = pg.cast_record
527        self.assertRaises(ValueError, f, '((a,b,c))')
528        self.assertRaises(ValueError, f, '((a,b),(c,d))')
529        self.assertRaises(ValueError, f, '((a),(b),(c))')
530        self.assertRaises(ValueError, f, '(((((((abc)))))))')
531
532    def testParserManyElements(self):
533        f = pg.cast_record
534        for n in 3, 5, 9, 12, 16, 32, 64, 256:
535            r = '(%s)' % ','.join(map(str, range(n)))
536            r = f(r, int)
537            self.assertEqual(r, tuple(range(n)))
538
539    def testParserCastUniform(self):
540        f = pg.cast_record
541        self.assertEqual(f('(1)'), ('1',))
542        self.assertEqual(f('(1)', None), ('1',))
543        self.assertEqual(f('(1)', int), (1,))
544        self.assertEqual(f('(1)', str), ('1',))
545        self.assertEqual(f('(a)'), ('a',))
546        self.assertEqual(f('(a)', None), ('a',))
547        self.assertRaises(ValueError, f, '(a)', int)
548        self.assertEqual(f('(a)', str), ('a',))
549        cast = lambda s: '%s is ok' % s
550        self.assertEqual(f('(a)', cast), ('a is ok',))
551
552    def testParserCastNonUniform(self):
553        f = pg.cast_record
554        self.assertEqual(f('(1)', []), ('1',))
555        self.assertEqual(f('(1)', [None]), ('1',))
556        self.assertEqual(f('(1)', [str]), ('1',))
557        self.assertEqual(f('(1)', [int]), (1,))
558        self.assertRaises(ValueError, f, '(1)', [None, None])
559        self.assertRaises(ValueError, f, '(1)', [str, str])
560        self.assertRaises(ValueError, f, '(1)', [int, int])
561        self.assertEqual(f('(a)', [None]), ('a',))
562        self.assertEqual(f('(a)', [str]), ('a',))
563        self.assertRaises(ValueError, f, '(a)', [int])
564        self.assertEqual(f('(1,a)', [int, str]), (1, 'a'))
565        self.assertRaises(ValueError, f, '(1,a)', [str, int])
566        self.assertEqual(f('(a,1)', [str, int]), ('a', 1))
567        self.assertRaises(ValueError, f, '(a,1)', [int, str])
568        self.assertEqual(f('(1,a,2,b,3,c)',
569            [int, str, int, str, int, str]), (1, 'a', 2, 'b', 3, 'c'))
570        self.assertEqual(f('(1,a,2,b,3,c)',
571            (int, str, int, str, int, str)), (1, 'a', 2, 'b', 3, 'c'))
572        cast1 = lambda s: '%s is ok' % s
573        self.assertEqual(f('(a)', [cast1]), ('a is ok',))
574        cast2 = lambda s: 'and %s is ok, too' % s
575        self.assertEqual(f('(a,b)', [cast1, cast2]),
576            ('a is ok', 'and b is ok, too'))
577        self.assertRaises(ValueError, f, '(a)', [cast1, cast2])
578        self.assertRaises(ValueError, f, '(a,b,c)', [cast1, cast2])
579        self.assertEqual(f('(1,2,3,4,5,6)',
580            [int, float, str, None, cast1, cast2]),
581            (1, 2.0, '3', '4', '5 is ok', 'and 6 is ok, too'))
582
583    def testParserDelim(self):
584        f = pg.cast_record
585        self.assertEqual(f('(1,2)'), ('1', '2'))
586        self.assertEqual(f('(1,2)', delim=b','), ('1', '2'))
587        self.assertEqual(f('(1;2)'), ('1;2',))
588        self.assertEqual(f('(1;2)', delim=b';'), ('1', '2'))
589        self.assertEqual(f('(1,2)', delim=b';'), ('1,2',))
590
591    def testParserWithData(self):
592        f = pg.cast_record
593        for string, cast, expected in self.test_strings:
594            if expected is ValueError:
595                self.assertRaises(ValueError, f, string, cast)
596            else:
597                self.assertEqual(f(string, cast), expected)
598
599    def testParserWithoutCast(self):
600        f = pg.cast_record
601
602        for string, cast, expected in self.test_strings:
603            if cast is not str:
604                continue
605            if expected is ValueError:
606                self.assertRaises(ValueError, f, string)
607            else:
608                self.assertEqual(f(string), expected)
609
610    def testParserWithDifferentDelimiter(self):
611        f = pg.cast_record
612
613        def replace_comma(value):
614            if isinstance(value, str):
615                return value.replace(';', '@').replace(
616                    ',', ';').replace('@', ',')
617            elif isinstance(value, tuple):
618                return tuple(replace_comma(v) for v in value)
619            else:
620                return value
621
622        for string, cast, expected in self.test_strings:
623            string = replace_comma(string)
624            if expected is ValueError:
625                self.assertRaises(ValueError, f, string, cast)
626            else:
627                expected = replace_comma(expected)
628                self.assertEqual(f(string, cast, b';'), expected)
629
630
631class TestParseHStore(unittest.TestCase):
632    """Test the hstore parser."""
633
634    test_strings = [
635        ('', {}),
636        ('=>', ValueError),
637        ('""=>', ValueError),
638        ('=>""', ValueError),
639        ('""=>""', {'': ''}),
640        ('NULL=>NULL', {'NULL': None}),
641        ('null=>null', {'null': None}),
642        ('NULL=>"NULL"', {'NULL': 'NULL'}),
643        ('null=>"null"', {'null': 'null'}),
644        ('k', ValueError),
645        ('k,', ValueError),
646        ('k=', ValueError),
647        ('k=>', ValueError),
648        ('k=>v', {'k': 'v'}),
649        ('k=>v,', ValueError),
650        (' k => v ', {'k': 'v'}),
651        ('   k   =>   v   ', {'k': 'v'}),
652        ('" k " => " v "', {' k ': ' v '}),
653        ('"k=>v', ValueError),
654        ('k=>"v', ValueError),
655        ('"1-a" => "anything at all"', {'1-a': 'anything at all'}),
656        ('k => v, foo => bar, baz => whatever,'
657                ' "1-a" => "anything at all"',
658            {'k': 'v', 'foo': 'bar', 'baz': 'whatever',
659            '1-a': 'anything at all'}),
660        ('"Hello, World!"=>"Hi!"', {'Hello, World!': 'Hi!'}),
661        ('"Hi!"=>"Hello, World!"', {'Hi!': 'Hello, World!'}),
662        ('"k=>v"=>k\=\>v', {'k=>v': 'k=>v'}),
663        ('k\=\>v=>"k=>v"', {'k=>v': 'k=>v'}),
664        ('a\\,b=>a,b=>a', {'a,b': 'a', 'b': 'a'})]
665
666    def testParser(self):
667        f = pg.cast_hstore
668
669        self.assertRaises(TypeError, f)
670        self.assertRaises(TypeError, f, None)
671        self.assertRaises(TypeError, f, 42)
672        self.assertRaises(TypeError, f, '', None)
673
674        for string, expected in self.test_strings:
675            if expected is ValueError:
676                self.assertRaises(ValueError, f, string)
677            else:
678                self.assertEqual(f(string), expected)
679
680
681class TestCastInterval(unittest.TestCase):
682    """Test the interval typecast function."""
683
684    intervals = [
685        ((0, 0, 0, 1, 0, 0, 0),
686            ('1:00:00', '01:00:00', '@ 1 hour', 'PT1H')),
687        ((0, 0, 0, -1, 0, 0, 0),
688            ('-1:00:00', '-01:00:00', '@ -1 hour', 'PT-1H')),
689        ((0, 0, 0, 1, 0, 0, 0),
690            ('0-0 0 1:00:00', '0 years 0 mons 0 days 01:00:00',
691            '@ 0 years 0 mons 0 days 1 hour', 'P0Y0M0DT1H')),
692        ((0, 0, 0, -1, 0, 0, 0),
693            ('-0-0 -1:00:00', '0 years 0 mons 0 days -01:00:00',
694            '@ 0 years 0 mons 0 days -1 hour', 'P0Y0M0DT-1H')),
695        ((0, 0, 1, 0, 0, 0, 0),
696            ('1 0:00:00', '1 day', '@ 1 day', 'P1D')),
697        ((0, 0, -1, 0, 0, 0, 0),
698            ('-1 0:00:00', '-1 day', '@ -1 day', 'P-1D')),
699        ((0, 1, 0, 0, 0, 0, 0),
700            ('0-1', '1 mon', '@ 1 mon', 'P1M')),
701        ((1, 0, 0, 0, 0, 0, 0),
702            ('1-0', '1 year', '@ 1 year', 'P1Y')),
703        ((0, 0, 0, 2, 0, 0, 0),
704            ('2:00:00', '02:00:00', '@ 2 hours', 'PT2H')),
705        ((0, 0, 2, 0, 0, 0, 0),
706            ('2 0:00:00', '2 days', '@ 2 days', 'P2D')),
707        ((0, 2, 0, 0, 0, 0, 0),
708            ('0-2', '2 mons', '@ 2 mons', 'P2M')),
709        ((2, 0, 0, 0, 0, 0, 0),
710            ('2-0', '2 years', '@ 2 years', 'P2Y')),
711        ((0, 0, 0, -3, 0, 0, 0),
712            ('-3:00:00', '-03:00:00', '@ 3 hours ago', 'PT-3H')),
713        ((0, 0, -3, 0, 0, 0, 0),
714            ('-3 0:00:00', '-3 days', '@ 3 days ago', 'P-3D')),
715        ((0, -3, 0, 0, 0, 0, 0),
716            ('-0-3', '-3 mons', '@ 3 mons ago', 'P-3M')),
717        ((-3, 0, 0, 0, 0, 0, 0),
718            ('-3-0', '-3 years', '@ 3 years ago', 'P-3Y')),
719        ((0, 0, 0, 0, 1, 0, 0),
720            ('0:01:00', '00:01:00', '@ 1 min', 'PT1M')),
721        ((0, 0, 0, 0, 0, 1, 0),
722            ('0:00:01', '00:00:01', '@ 1 sec', 'PT1S')),
723        ((0, 0, 0, 0, 0, 0, 1),
724            ('0:00:00.000001', '00:00:00.000001',
725             '@ 0.000001 secs', 'PT0.000001S')),
726        ((0, 0, 0, 0, 2, 0, 0),
727            ('0:02:00', '00:02:00', '@ 2 mins', 'PT2M')),
728        ((0, 0, 0, 0, 0, 2, 0),
729            ('0:00:02', '00:00:02', '@ 2 secs', 'PT2S')),
730        ((0, 0, 0, 0, 0, 0, 2),
731            ('0:00:00.000002', '00:00:00.000002',
732             '@ 0.000002 secs', 'PT0.000002S')),
733        ((0, 0, 0, 0, -3, 0, 0),
734            ('-0:03:00', '-00:03:00', '@ 3 mins ago', 'PT-3M')),
735        ((0, 0, 0, 0, 0, -3, 0),
736            ('-0:00:03', '-00:00:03', '@ 3 secs ago', 'PT-3S')),
737        ((0, 0, 0, 0, 0, 0, -3),
738            ('-0:00:00.000003', '-00:00:00.000003',
739             '@ 0.000003 secs ago', 'PT-0.000003S')),
740        ((1, 2, 0, 0, 0, 0, 0),
741            ('1-2', '1 year 2 mons', '@ 1 year 2 mons', 'P1Y2M')),
742        ((0, 0, 3, 4, 5, 6, 0),
743            ('3 4:05:06', '3 days 04:05:06',
744             '@ 3 days 4 hours 5 mins 6 secs', 'P3DT4H5M6S')),
745        ((1, 2, 3, 4, 5, 6, 0),
746            ('+1-2 +3 +4:05:06', '1 year 2 mons 3 days 04:05:06',
747             '@ 1 year 2 mons 3 days 4 hours 5 mins 6 secs',
748             'P1Y2M3DT4H5M6S')),
749        ((1, 2, 3, -4, -5, -6, 0),
750            ('+1-2 +3 -4:05:06', '1 year 2 mons 3 days -04:05:06',
751             '@ 1 year 2 mons 3 days -4 hours -5 mins -6 secs',
752             'P1Y2M3DT-4H-5M-6S')),
753        ((1, 2, 3, -4, 5, 6, 0),
754            ('+1-2 +3 -3:54:54', '1 year 2 mons 3 days -03:54:54',
755             '@ 1 year 2 mons 3 days -3 hours -54 mins -54 secs',
756             'P1Y2M3DT-3H-54M-54S')),
757        ((-1, -2, 3, -4, -5, -6, 0),
758            ('-1-2 +3 -4:05:06', '-1 years -2 mons +3 days -04:05:06',
759             '@ 1 year 2 mons -3 days 4 hours 5 mins 6 secs ago',
760             'P-1Y-2M3DT-4H-5M-6S')),
761        ((1, 2, -3, 4, 5, 6, 0),
762            ('+1-2 -3 +4:05:06', '1 year 2 mons -3 days +04:05:06',
763             '@ 1 year 2 mons -3 days 4 hours 5 mins 6 secs',
764             'P1Y2M-3DT4H5M6S')),
765        ((0, 0, 0, 1, 30, 0, 0),
766            ('1:30:00', '01:30:00', '@ 1 hour 30 mins', 'PT1H30M')),
767        ((0, 0, 0, 3, 15, 45, 123456),
768            ('3:15:45.123456', '03:15:45.123456',
769             '@ 3 hours 15 mins 45.123456 secs', 'PT3H15M45.123456S')),
770        ((0, 0, 0, 3, 15, -5, 123),
771            ('3:14:55.000123', '03:14:55.000123',
772             '@ 3 hours 14 mins 55.000123 secs', 'PT3H14M55.000123S')),
773        ((0, 0, 0, 3, -5, 15, -12345),
774            ('2:55:14.987655', '02:55:14.987655',
775             '@ 2 hours 55 mins 14.987655 secs', 'PT2H55M14.987655S')),
776        ((0, 0, 0, 2, -1, 0, 0),
777            ('1:59:00', '01:59:00', '@ 1 hour 59 mins', 'PT1H59M')),
778        ((0, 0, 0, -1, 2, 0, 0),
779            ('-0:58:00', '-00:58:00', '@ 58 mins ago', 'PT-58M')),
780        ((1, 11, 0, 0, 0, 0, 0),
781            ('1-11', '1 year 11 mons', '@ 1 year 11 mons', 'P1Y11M')),
782        ((0, -10, 0, 0, 0, 0, 0),
783            ('-0-10', '-10 mons', '@ 10 mons ago', 'P-10M')),
784        ((0, 0, 2, -1, 0, 0, 0),
785            ('+0-0 +2 -1:00:00', '2 days -01:00:00',
786             '@ 2 days -1 hours', 'P2DT-1H')),
787        ((0, 0, -1, 2, 0, 0, 0),
788            ('+0-0 -1 +2:00:00', '-1 days +02:00:00',
789             '@ 1 day -2 hours ago', 'P-1DT2H')),
790        ((0, 0, 1, 0, 0, 0, 1),
791            ('1 0:00:00.000001', '1 day 00:00:00.000001',
792             '@ 1 day 0.000001 secs', 'P1DT0.000001S')),
793        ((0, 0, 1, 0, 0, 1, 0),
794            ('1 0:00:01', '1 day 00:00:01', '@ 1 day 1 sec', 'P1DT1S')),
795        ((0, 0, 1, 0, 1, 0, 0),
796            ('1 0:01:00', '1 day 00:01:00', '@ 1 day 1 min', 'P1DT1M')),
797        ((0, 0, 0, 0, 1, 0, -1),
798            ('0:00:59.999999', '00:00:59.999999',
799             '@ 59.999999 secs', 'PT59.999999S')),
800        ((0, 0, 0, 0, -1, 0, 1),
801            ('-0:00:59.999999', '-00:00:59.999999',
802             '@ 59.999999 secs ago', 'PT-59.999999S')),
803        ((0, 0, 0, 0, -1, 1, 1),
804            ('-0:00:58.999999', '-00:00:58.999999',
805             '@ 58.999999 secs ago', 'PT-58.999999S')),
806        ((0, 0, 42, 0, 0, 0, 0),
807            ('42 0:00:00', '42 days', '@ 42 days', 'P42D')),
808        ((0, 0, -7, 0, 0, 0, 0),
809            ('-7 0:00:00', '-7 days', '@ 7 days ago', 'P-7D')),
810        ((1, 1, 1, 1, 1, 0, 0),
811            ('+1-1 +1 +1:01:00', '1 year 1 mon 1 day 01:01:00',
812             '@ 1 year 1 mon 1 day 1 hour 1 min', 'P1Y1M1DT1H1M')),
813        ((0, -11, -1, -1, 1, 0, 0),
814            ('-0-11 -1 -0:59:00', '-11 mons -1 days -00:59:00',
815             '@ 11 mons 1 day 59 mins ago', 'P-11M-1DT-59M')),
816        ((-1, -1, -1, -1, -1, 0, 0),
817            ('-1-1 -1 -1:01:00', '-1 years -1 mons -1 days -01:01:00',
818             '@ 1 year 1 mon 1 day 1 hour 1 min ago', 'P-1Y-1M-1DT-1H-1M')),
819        ((-1, 0, -3, 1, 0, 0, 0),
820            ('-1-0 -3 +1:00:00', '-1 years -3 days +01:00:00',
821             '@ 1 year 3 days -1 hours ago', 'P-1Y-3DT1H')),
822        ((1, 0, 0, 0, 0, 0, 1),
823            ('+1-0 +0 +0:00:00.000001', '1 year 00:00:00.000001',
824             '@ 1 year 0.000001 secs', 'P1YT0.000001S')),
825        ((1, 0, 0, 0, 0, 0, -1),
826            ('+1-0 +0 -0:00:00.000001', '1 year -00:00:00.000001',
827             '@ 1 year -0.000001 secs', 'P1YT-0.000001S')),
828        ((1, 2, 3, 4, 5, 6, 7),
829            ('+1-2 +3 +4:05:06.000007',
830             '1 year 2 mons 3 days 04:05:06.000007',
831             '@ 1 year 2 mons 3 days 4 hours 5 mins 6.000007 secs',
832             'P1Y2M3DT4H5M6.000007S')),
833        ((0, 10, 3, -4, 5, -6, 7),
834            ('+0-10 +3 -3:55:05.999993', '10 mons 3 days -03:55:05.999993',
835             '@ 10 mons 3 days -3 hours -55 mins -5.999993 secs',
836             'P10M3DT-3H-55M-5.999993S')),
837        ((0, -10, -3, 4, -5, 6, -7),
838            ('-0-10 -3 +3:55:05.999993',
839             '-10 mons -3 days +03:55:05.999993',
840             '@ 10 mons 3 days -3 hours -55 mins -5.999993 secs ago',
841             'P-10M-3DT3H55M5.999993S'))]
842
843    def testCastInterval(self):
844        for result, values in self.intervals:
845            f = pg.cast_interval
846            years, mons, days, hours, mins, secs, usecs = result
847            days += 365 * years + 30 * mons
848            interval = timedelta(days=days, hours=hours, minutes=mins,
849                seconds=secs, microseconds=usecs)
850            for value in values:
851                self.assertEqual(f(value), interval)
852
853
854class TestEscapeFunctions(unittest.TestCase):
855    """Test pg escape and unescape functions.
856
857    The libpq interface memorizes some parameters of the last opened
858    connection that influence the result of these functions.
859    Therefore we cannot do rigid tests of these functions here.
860    We leave this for the test module that runs with a database.
861
862    """
863
864    def testEscapeString(self):
865        f = pg.escape_string
866        r = f(b'plain')
867        self.assertIsInstance(r, bytes)
868        self.assertEqual(r, b'plain')
869        r = f(u'plain')
870        self.assertIsInstance(r, unicode)
871        self.assertEqual(r, u'plain')
872        r = f("that's cheese")
873        self.assertIsInstance(r, str)
874        self.assertEqual(r, "that''s cheese")
875
876    def testEscapeBytea(self):
877        f = pg.escape_bytea
878        r = f(b'plain')
879        self.assertIsInstance(r, bytes)
880        self.assertEqual(r, b'plain')
881        r = f(u'plain')
882        self.assertIsInstance(r, unicode)
883        self.assertEqual(r, u'plain')
884        r = f("that's cheese")
885        self.assertIsInstance(r, str)
886        self.assertEqual(r, "that''s cheese")
887
888    def testUnescapeBytea(self):
889        f = pg.unescape_bytea
890        r = f(b'plain')
891        self.assertIsInstance(r, bytes)
892        self.assertEqual(r, b'plain')
893        r = f(u'plain')
894        self.assertIsInstance(r, bytes)
895        self.assertEqual(r, b'plain')
896        r = f(b"das is' k\\303\\244se")
897        self.assertIsInstance(r, bytes)
898        self.assertEqual(r, u"das is' kÀse".encode('utf-8'))
899        r = f(u"das is' k\\303\\244se")
900        self.assertIsInstance(r, bytes)
901        self.assertEqual(r, u"das is' kÀse".encode('utf-8'))
902        r = f(b'O\\000ps\\377!')
903        self.assertEqual(r, b'O\x00ps\xff!')
904        r = f(u'O\\000ps\\377!')
905        self.assertEqual(r, b'O\x00ps\xff!')
906
907
908class TestConfigFunctions(unittest.TestCase):
909    """Test the functions for changing default settings.
910
911    The effect of most of these cannot be tested here, because that
912    needs a database connection.  So we merely test their existence here.
913
914    """
915
916    def testGetDatestyle(self):
917        self.assertIsNone(pg.get_datestyle())
918
919    def testGetDatestyle(self):
920        datestyle = pg.get_datestyle()
921        try:
922            pg.set_datestyle('ISO, YMD')
923            self.assertEqual(pg.get_datestyle(), 'ISO, YMD')
924            pg.set_datestyle('Postgres, MDY')
925            self.assertEqual(pg.get_datestyle(), 'Postgres, MDY')
926            pg.set_datestyle('Postgres, DMY')
927            self.assertEqual(pg.get_datestyle(), 'Postgres, DMY')
928            pg.set_datestyle('SQL, MDY')
929            self.assertEqual(pg.get_datestyle(), 'SQL, MDY')
930            pg.set_datestyle('SQL, DMY')
931            self.assertEqual(pg.get_datestyle(), 'SQL, DMY')
932            pg.set_datestyle('German, DMY')
933            self.assertEqual(pg.get_datestyle(), 'German, DMY')
934            pg.set_datestyle(None)
935            self.assertIsNone(pg.get_datestyle())
936        finally:
937            pg.set_datestyle(datestyle)
938
939    def testGetDecimalPoint(self):
940        r = pg.get_decimal_point()
941        self.assertIsInstance(r, str)
942        self.assertEqual(r, '.')
943
944    def testSetDecimalPoint(self):
945        point = pg.get_decimal_point()
946        try:
947            pg.set_decimal_point('*')
948            r = pg.get_decimal_point()
949            self.assertIsInstance(r, str)
950            self.assertEqual(r, '*')
951        finally:
952            pg.set_decimal_point(point)
953        r = pg.get_decimal_point()
954        self.assertIsInstance(r, str)
955        self.assertEqual(r, point)
956
957    def testGetDecimal(self):
958        r = pg.get_decimal()
959        self.assertIs(r, pg.Decimal)
960
961    def testSetDecimal(self):
962        decimal_class = pg.Decimal
963        try:
964            pg.set_decimal(int)
965            r = pg.get_decimal()
966            self.assertIs(r, int)
967        finally:
968            pg.set_decimal(decimal_class)
969        r = pg.get_decimal()
970        self.assertIs(r, decimal_class)
971
972    def testGetBool(self):
973        r = pg.get_bool()
974        self.assertIsInstance(r, bool)
975        self.assertIs(r, True)
976
977    def testSetBool(self):
978        use_bool = pg.get_bool()
979        try:
980            pg.set_bool(False)
981            r = pg.get_bool()
982            pg.set_bool(use_bool)
983            self.assertIsInstance(r, bool)
984            self.assertIs(r, False)
985            pg.set_bool(True)
986            r = pg.get_bool()
987            self.assertIsInstance(r, bool)
988            self.assertIs(r, True)
989        finally:
990            pg.set_bool(use_bool)
991        r = pg.get_bool()
992        self.assertIsInstance(r, bool)
993        self.assertIs(r, use_bool)
994
995    def testGetByteaEscaped(self):
996        r = pg.get_bytea_escaped()
997        self.assertIsInstance(r, bool)
998        self.assertIs(r, False)
999
1000    def testSetByteaEscaped(self):
1001        bytea_escaped = pg.get_bytea_escaped()
1002        try:
1003            pg.set_bytea_escaped(True)
1004            r = pg.get_bytea_escaped()
1005            pg.set_bytea_escaped(bytea_escaped)
1006            self.assertIsInstance(r, bool)
1007            self.assertIs(r, True)
1008            pg.set_bytea_escaped(False)
1009            r = pg.get_bytea_escaped()
1010            self.assertIsInstance(r, bool)
1011            self.assertIs(r, False)
1012        finally:
1013            pg.set_bytea_escaped(bytea_escaped)
1014        r = pg.get_bytea_escaped()
1015        self.assertIsInstance(r, bool)
1016        self.assertIs(r, bytea_escaped)
1017
1018    def testGetJsondecode(self):
1019        r = pg.get_jsondecode()
1020        self.assertTrue(callable(r))
1021        self.assertIs(r, json.loads)
1022
1023    def testSetJsondecode(self):
1024        jsondecode = pg.get_jsondecode()
1025        try:
1026            pg.set_jsondecode(None)
1027            r = pg.get_jsondecode()
1028            self.assertIsNone(r)
1029            pg.set_jsondecode(str)
1030            r = pg.get_jsondecode()
1031            self.assertIs(r, str)
1032            self.assertRaises(TypeError, pg.set_jsondecode, 'invalid')
1033        finally:
1034            pg.set_jsondecode(jsondecode)
1035        r = pg.get_jsondecode()
1036        self.assertIs(r, jsondecode)
1037
1038
1039class TestModuleConstants(unittest.TestCase):
1040    """Test the existence of the documented module constants."""
1041
1042    def testVersion(self):
1043        v = pg.version
1044        self.assertIsInstance(v, str)
1045        # make sure the version conforms to PEP440
1046        re_version = r"""^
1047            (\d[\.\d]*(?<= \d))
1048            ((?:[abc]|rc)\d+)?
1049            (?:(\.post\d+))?
1050            (?:(\.dev\d+))?
1051            (?:(\+(?![.])[a-zA-Z0-9\.]*[a-zA-Z0-9]))?
1052            $"""
1053        match = re.match(re_version, v, re.X)
1054        self.assertIsNotNone(match)
1055        self.assertEqual(pg.__version__, v)
1056
1057
1058if __name__ == '__main__':
1059    unittest.main()
Note: See TracBrowser for help on using the repository browser.