parse_hstore converted in class method.

This commit is contained in:
Daniele Varrazzo 2010-09-26 21:59:54 +01:00
parent 5844e989c4
commit fef9727cce
2 changed files with 48 additions and 45 deletions

View File

@ -35,7 +35,7 @@ try:
except: except:
logging = None logging = None
from psycopg2 import DATETIME, DataError, InterfaceError import psycopg2
from psycopg2 import extensions as _ext from psycopg2 import extensions as _ext
from psycopg2.extensions import cursor as _cursor from psycopg2.extensions import cursor as _cursor
from psycopg2.extensions import connection as _connection from psycopg2.extensions import connection as _connection
@ -515,55 +515,57 @@ class HstoreAdapter(object):
v.prepare(self.conn) v.prepare(self.conn)
return "hstore(%s, %s)" % (k.getquoted(), v.getquoted()) return "hstore(%s, %s)" % (k.getquoted(), v.getquoted())
_re_hstore = regex.compile(r""" _re_hstore = regex.compile(r"""
# hstore key: # hstore key:
"( # catch in quotes "( # catch in quotes
(?: # many of (?: # many of
[^"] # either not a quote [^"] # either not a quote
# or a quote escaped, i.e. preceded by an odd number of backslashes # or a quote escaped, i.e. preceded by an odd number of backslashes
| [^\\] (?:\\\\)* \\" | [^\\] (?:\\\\)* \\"
)* )*
)" )"
\s*=>\s* # hstore value \s*=>\s* # hstore value
(?: (?:
NULL # the value can be null - not catched NULL # the value can be null - not catched
# or the same quoted string of the key # or the same quoted string of the key
| "((?:[^"] | [^\\] (?:\\\\)* \\" )*)" | "((?:[^"] | [^\\] (?:\\\\)* \\" )*)"
) )
(?:\s*,\s*|$) # pairs separated by comma or end of string. (?:\s*,\s*|$) # pairs separated by comma or end of string.
""", regex.VERBOSE) """, regex.VERBOSE)
def parse_hstore(s, cur): def parse(self, s, cur):
"""Parse an hstore representation in a Python string. """Parse an hstore representation in a Python string.
The hstore is represented as something like:: The hstore is represented as something like::
"a"=>"1", "b"=>"2" "a"=>"1", "b"=>"2"
with backslash-escaped strings. with backslash-escaped strings.
""" """
if s is None: if s is None:
return None return None
rv = {} rv = {}
start = 0 start = 0
for m in _re_hstore.finditer(s): for m in self._re_hstore.finditer(s):
if m is None or m.start() != start: if m is None or m.start() != start:
raise InterfaceError( raise psycopg2.InterfaceError(
"error parsing hstore pair at char %d" % start) "error parsing hstore pair at char %d" % start)
k = m.group(1).decode("string_escape") k = m.group(1).decode("string_escape")
v = m.group(2) v = m.group(2)
if v is not None: if v is not None:
v = v.decode("string_escape") v = v.decode("string_escape")
rv[k] = v rv[k] = v
start = m.end() start = m.end()
if start < len(s): if start < len(s):
raise InterfaceError( raise psycopg2.InterfaceError(
"error parsing hstore: unparsed data after char %d" % start) "error parsing hstore: unparsed data after char %d" % start)
return rv return rv
parse = classmethod(parse)
__all__ = filter(lambda k: not k.startswith('_'), locals().keys()) __all__ = filter(lambda k: not k.startswith('_'), locals().keys())

View File

@ -158,10 +158,10 @@ class HstoreTestCase(unittest.TestCase):
self.assertEqual(ii[3], ("E'd'", "E'%s'" % encc)) self.assertEqual(ii[3], ("E'd'", "E'%s'" % encc))
def test_parse(self): def test_parse(self):
from psycopg2.extras import parse_hstore from psycopg2.extras import HstoreAdapter
def ok(s, d): def ok(s, d):
self.assertEqual(parse_hstore(s, None), d) self.assertEqual(HstoreAdapter.parse(s, None), d)
ok(None, None) ok(None, None)
ok('', {}) ok('', {})
@ -176,7 +176,8 @@ class HstoreTestCase(unittest.TestCase):
ok(r'"a\\\\\""=>"1"', {r'a\\"': '1'}) ok(r'"a\\\\\""=>"1"', {r'a\\"': '1'})
def ko(s): def ko(s):
self.assertRaises(psycopg2.InterfaceError, parse_hstore, s, None) self.assertRaises(psycopg2.InterfaceError,
HstoreAdapter.parse, s, None)
ko('a') ko('a')
ko('"a"') ko('"a"')