1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2009 Christopher Lenz
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution.
8
9"""Thin abstraction layer over the different available modules for decoding
10and encoding JSON data.
11
12This module currently supports the following JSON modules:
13 - ``simplejson``: http://code.google.com/p/simplejson/
14 - ``cjson``: http://pypi.python.org/pypi/python-cjson
15 - ``json``: This is the version of ``simplejson`` that is bundled with the
16   Python standard library since version 2.6
17   (see http://docs.python.org/library/json.html)
18
19The default behavior is to use ``simplejson`` if installed, and otherwise
20fallback to the standard library module. To explicitly tell CouchDB-Python
21which module to use, invoke the `use()` function with the module name::
22
23    from couchdb import json
24    json.use('cjson')
25
26In addition to choosing one of the above modules, you can also configure
27CouchDB-Python to use custom decoding and encoding functions::
28
29    from couchdb import json
30    json.use(decode=my_decode, encode=my_encode)
31
32"""
33
34__all__ = ['decode', 'encode', 'use']
35
36_initialized = False
37_using = None
38_decode = None
39_encode = None
40
41
42def decode(string):
43    """Decode the given JSON string.
44
45    :param string: the JSON string to decode
46    :type string: basestring
47    :return: the corresponding Python data structure
48    :rtype: object
49    """
50    if not _initialized:
51        _initialize()
52    return _decode(string)
53
54
55def encode(obj):
56    """Encode the given object as a JSON string.
57
58    :param obj: the Python data structure to encode
59    :type obj: object
60    :return: the corresponding JSON string
61    :rtype: basestring
62    """
63    if not _initialized:
64        _initialize()
65    return _encode(obj)
66
67
68def use(module=None, decode=None, encode=None):
69    """Set the JSON library that should be used, either by specifying a known
70    module name, or by providing a decode and encode function.
71
72    The modules "simplejson", "cjson", and "json" are currently supported for
73    the ``module`` parameter.
74
75    If provided, the ``decode`` parameter must be a callable that accepts a
76    JSON string and returns a corresponding Python data structure. The
77    ``encode`` callable must accept a Python data structure and return the
78    corresponding JSON string. Exceptions raised by decoding and encoding
79    should be propagated up unaltered.
80
81    :param module: the name of the JSON library module to use, or the module
82                   object itself
83    :type module: str or module
84    :param decode: a function for decoding JSON strings
85    :type decode: callable
86    :param encode: a function for encoding objects as JSON strings
87    :type encode: callable
88    """
89    global _decode, _encode, _initialized, _using
90    if module is not None:
91        if not isinstance(module, basestring):
92            module = module.__name__
93        if module not in ('cjson', 'json', 'simplejson'):
94            raise ValueError('Unsupported JSON module %s' % module)
95        _using = module
96        _initialized = False
97    else:
98        assert decode is not None and encode is not None
99        _using = 'custom'
100        _decode = decode
101        _encode = encode
102        _initialized = True
103
104
105def _initialize():
106    global _initialized
107
108    def _init_simplejson():
109        global _decode, _encode
110        import simplejson
111        _decode = lambda string, loads=simplejson.loads: loads(string)
112        _encode = lambda obj, dumps=simplejson.dumps: \
113            dumps(obj, allow_nan=False, ensure_ascii=False)
114
115    def _init_cjson():
116        global _decode, _encode
117        import cjson
118        _decode = lambda string, decode=cjson.decode: decode(string)
119        _encode = lambda obj, encode=cjson.encode: encode(obj)
120
121    def _init_stdlib():
122        global _decode, _encode
123        json = __import__('json', {}, {})
124        _decode = lambda string, loads=json.loads: loads(string)
125        _encode = lambda obj, dumps=json.dumps: \
126            dumps(obj, allow_nan=False, ensure_ascii=False)
127
128    if _using == 'simplejson':
129        _init_simplejson()
130    elif _using == 'cjson':
131        _init_cjson()
132    elif _using == 'json':
133        _init_stdlib()
134    elif _using != 'custom':
135        try:
136            _init_simplejson()
137        except ImportError:
138            _init_stdlib()
139    _initialized = True
140