source: trunk/tests/test_classic_functions.py @ 817

Last change on this file since 817 was 817, checked in by cito, 4 years ago

Support the hstore data type

Added adaptation and typecasting of the hstore type as Python dictionaries.
For the typecasting, a fast parser has been added to the C extension.

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