From c9a63a8524fe9e4d6e35465b4c0a70ca4ae83675 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 17 Mar 2013 12:11:10 -0500 Subject: [PATCH] fixed issue 1393, cast_keys in dict for python 2.5 support, thanks Alan and Niphlod --- VERSION | 2 +- gluon/dal.py | 34 +++++++++++++++++++++++------ gluon/serializers.py | 47 +++++++++++++++++++++++++++++++++++++++-- gluon/tests/test_dal.py | 8 ++++++- 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index fe9ebcc3..32eef661 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.4-stable+timestamp.2013.03.16.20.52.38 +Version 2.4.4-stable+timestamp.2013.03.17.12.10.29 diff --git a/gluon/dal.py b/gluon/dal.py index 18301663..eea64173 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7054,15 +7054,13 @@ class DAL(object): Example:: db = DAL('sqlite://test.db') + + or + + db = DAL({"uri": ..., "items": ...}) # experimental + db.define_table('tablename', Field('fieldname1'), Field('fieldname2')) - - (experimental) - you can pass a dict object as uri with the uri string - and table/field definitions. For an example of valid data check - the output of: - - >>> db.as_dict(flat=True, sanitize=False) """ def __new__(cls, uri='sqlite://dummy.db', *args, **kwargs): @@ -7183,6 +7181,28 @@ class DAL(object): :uri: string that contains information for connecting to a database. (default: 'sqlite://dummy.db') + + experimental: you can specify a dictionary as uri + parameter i.e. with + db = DAL({"uri": "sqlite://storage.sqlite", + "items": {...}, ...}) + + for an example of dict input you can check the output + of the scaffolding db model with + + db.as_dict() + + Note that for compatibility with Python older than + version 2.6.5 you should cast your dict input keys + to str due to a syntax limitation on kwarg names. + for proper DAL dictionary input you can use one of: + + obj = serializers.cast_keys(dict, [encoding="utf-8"]) + + or else (for parsing json input) + + obj = serializers.loads_json(data, unicode_keys=False) + :pool_size: How many open connections to make to the database object. :folder: where .table files will be created. automatically set within web2py diff --git a/gluon/serializers.py b/gluon/serializers.py index c4268599..388c8df1 100644 --- a/gluon/serializers.py +++ b/gluon/serializers.py @@ -25,9 +25,52 @@ try: except ImportError: have_yaml = False -def loads_json(o): +def cast_keys(o, cast=str, encoding="utf-8"): + """ Builds a new object with type keys + + Arguments: + o is the object input + cast (defaults to str) is an object type or function + which supports conversion such as: + + >>> converted = cast(o) + + encoding (defaults to utf-8) is the encoding for unicode + keys. This is not used for custom cast functions + + Use this funcion if you are in Python < 2.6.5 + This avoids syntax errors when unpacking dictionary arguments. + """ + + if isinstance(o, (dict, Storage)): + if isinstance(o, dict): + newobj = dict() + else: + newobj = Storage() + + for k, v in o.items(): + if (cast == str) and isinstance(k, unicode): + key = k.encode(encoding) + else: + key = cast(k) + if isinstance(v, (dict, Storage)): + value = cast_keys(v, cast=cast, encoding=encoding) + else: + value = v + newobj[key] = value + else: + raise TypeError("Cannot cast keys: %s is not supported" % \ + type(o)) + return newobj + +def loads_json(o, unicode_keys=True, **kwargs): # deserialize a json string - return json_parser.loads(o) + result = json_parser.loads(o, **kwargs) + if not unicode_keys: + # filter non-str keys in dictionary objects + result = cast_keys(result, + encoding=kwargs.get("encoding", "utf-8")) + return result def custom_json(o): if hasattr(o, 'custom_json') and callable(o.custom_json): diff --git a/gluon/tests/test_dal.py b/gluon/tests/test_dal.py index dbdf57c0..c2e5ce65 100644 --- a/gluon/tests/test_dal.py +++ b/gluon/tests/test_dal.py @@ -7,6 +7,7 @@ import sys import os import glob + if os.path.isdir('gluon'): sys.path.append(os.path.realpath('gluon')) else: @@ -659,7 +660,12 @@ class TestDALDictImportExport(unittest.TestCase): import serializers dbjson = db.as_json(sanitize=False) assert isinstance(dbjson, basestring) and len(dbjson) > 0 - db3 = DAL(serializers.loads_json(dbjson)) + + unicode_keys = True + if sys.version < "2.6.5": + unicode_keys = False + db3 = DAL(serializers.loads_json(dbjson, + unicode_keys=unicode_keys)) assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\ db3.person.uuid.type == db.person.uuid.type db3.person.drop()