jsonpickle-0.6.1/0000775000501500000240000000000012206357645014076 5ustar davidstaff00000000000000jsonpickle-0.6.1/COPYING0000664000501500000240000000273412204631007015120 0ustar davidstaff00000000000000Copyright (C) 2008 John Paulett (john -at- paulett.org) Copyright (C) 2009, 2011, 2013 David Aguilar (davvid -at- gmail.com) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jsonpickle-0.6.1/docs/0000775000501500000240000000000012206357645015026 5ustar davidstaff00000000000000jsonpickle-0.6.1/docs/source/0000775000501500000240000000000012206357645016326 5ustar davidstaff00000000000000jsonpickle-0.6.1/docs/source/api.rst0000664000501500000240000000744312205343137017627 0ustar davidstaff00000000000000.. _jsonpickle-api: ============== jsonpickle API ============== .. testsetup:: * import jsonpickle import jsonpickle.pickler import jsonpickle.unpickler import jsonpickle.handlers import jsonpickle.util .. contents:: :mod:`jsonpickle` -- High Level API =================================== .. autofunction:: jsonpickle.encode .. autofunction:: jsonpickle.decode Choosing and Loading Backends ----------------------------- jsonpickle allows the user to specify what JSON backend to use when encoding and decoding. By default, jsonpickle will try to use, in the following order: `simplejson `_, :mod:`json`, and `demjson `_. The prefered backend can be set via :func:`jsonpickle.set_preferred_backend`. Additional JSON backends can be used via :func:`jsonpickle.load_backend`. For example, users of `Django `_ can use the version of simplejson that is bundled in Django:: jsonpickle.load_backend('django.util.simplejson', 'dumps', 'loads', ValueError)) jsonpickle.set_preferred_backend('django.util.simplejson') Supported backends: * :mod:`json` in Python 2.6+ * `simplejson `_ * `demjson `_ Experimental backends: * `jsonlib `_ * yajl via `py-yajl `_ * `ujson `_ .. autofunction:: jsonpickle.set_preferred_backend .. autofunction:: jsonpickle.load_backend .. autofunction:: jsonpickle.remove_backend .. autofunction:: jsonpickle.set_encoder_options Customizing JSON output ----------------------- jsonpickle supports the standard :mod:`pickle` `__getstate__` and `__setstate__` protocol for representating object instances. .. method:: object.__getstate__() Classes can further influence how their instances are pickled; if the class defines the method :meth:`__getstate__`, it is called and the return state is pickled as the contents for the instance, instead of the contents of the instance's dictionary. If there is no :meth:`__getstate__` method, the instance's :attr:`__dict__` is pickled. .. method:: object.__setstate__(state) Upon unpickling, if the class also defines the method :meth:`__setstate__`, it is called with the unpickled state. If there is no :meth:`__setstate__` method, the pickled state must be a dictionary and its items are assigned to the new instance's dictionary. If a class defines both :meth:`__getstate__` and :meth:`__setstate__`, the state object needn't be a dictionary and these methods can do what they want. :mod:`jsonpickle.handlers` -- Custom Serialization Handlers ----------------------------------------------------------- The `jsonpickle.handlers` module allows plugging in custom serialization handlers at run-time. This feature is useful when jsonpickle is unable to serialize objects that are not under your direct control. .. automodule:: jsonpickle.handlers :members: :undoc-members: Low Level API ============= Typically this low level functionality is not needed by clients. :mod:`jsonpickle.pickler` -- Python to JSON ------------------------------------------- .. automodule:: jsonpickle.pickler :members: :undoc-members: :mod:`jsonpickle.unpickler` -- JSON to Python --------------------------------------------- .. automodule:: jsonpickle.unpickler :members: :undoc-members: :mod:`jsonpickle.backend` -- JSON Backend Management ---------------------------------------------------- .. automodule:: jsonpickle.backend :members: :mod:`jsonpickle.util` -- Helper functions ------------------------------------------ .. automodule:: jsonpickle.util :members: :undoc-members: jsonpickle-0.6.1/docs/source/changelog.rst0000664000501500000240000001130612206357421021000 0ustar davidstaff00000000000000Change Log ========== Version 0.6.1 - August 25, 2013 ------------------------------- * Python 3.2 support, and additional fixes for Python 3. Version 0.6.0 - August 24, 2013 ------------------------------- * Python 3 support! * :class:`time.struct_time` is now serialized using the built-in :class:`jsonpickle.handlers.SimpleReduceHandler`. Version 0.5.0 - August 22, 2013 ------------------------------- * Non-string dictionary keys (e.g. ints, objects) are now supported by passing `keys=True` to :func:`jsonpickle.encode` and :func:`jsonpickle.decode`. * We now support namedtuple, deque, and defaultdict. * Datetimes with timezones are now fully supported. * Better support for complicated structures e.g. datetime inside dicts. * jsonpickle added support for references and cyclical data structures in 0.4.0. This can be disabled by passing `make_refs=False` to :func:`jsonpickle.encode`. Version 0.4.0 - June 21, 2011 ----------------------------- * Switch build from setuptools to distutils * Consistent dictionary key ordering * Fix areas with improper support for unpicklable=False * Added support for cyclical data structures (`#16 `_). * Experimental support for `jsonlib `_ and `py-yajl `_ backends. * New contributers David K. Hess and Alec Thomas .. warning:: To support cyclical data structures (`#16 `_), the storage format has been modified. Efforts have been made to ensure backwards-compatibility. jsonpickle 0.4.0 can read data encoded by jsonpickle 0.3.1, but earlier versions of jsonpickle may be unable to read data encoded by jsonpickle 0.4.0. Version 0.3.1 - December 12, 2009 --------------------------------- * Include tests and docs directories in sdist for distribution packages. Version 0.3.0 - December 11, 2009 --------------------------------- * Officially migrated to git from subversion. Project home now at ``_. Thanks to Michael Jone's `sphinx-to-github `_. * Fortified jsonpickle against common error conditions. * Added support for: * List and set subclasses. * Objects with module references. * Newstyle classes with `__slots__`. * Objects implementing `__setstate__()` and `__getstate__()` (follows the :mod:`pickle` protocol). * Improved support for Zope objects via pre-fetch. * Support for user-defined serialization handlers via the jsonpickle.handlers registry. * Removed cjson support per John Millikin's recommendation. * General improvements to style, including :pep:`257` compliance and refactored project layout. * Steps towards Python 2.3 and Python 3 support. * New contributors Dan Buch and Ian Schenck. * Thanks also to Kieran Darcy, Eoghan Murray, and Antonin Hildebrand for their assistance! Version 0.2.0 - January 10, 2009 -------------------------------- * Support for all major Python JSON backends (including json in Python 2.6, simplejson, cjson, and demjson) * Handle several datetime objects using the repr() of the objects (Thanks to Antonin Hildebrand). * Sphinx documentation * Added support for recursive data structures * Unicode dict-keys support * Support for Google App Engine and Django * Tons of additional testing and bug reports (Antonin Hildebrand, Sorin, Roberto Saccon, Faber Fedor, `FirePython `_, and `Joose `_) Version 0.1.0 - August 21, 2008 ------------------------------- * Added long as basic primitive (thanks Adam Fisk) * Prefer python-cjson to simplejson, if available * Major API change, use python-cjson's decode/encode instead of simplejson's load/loads/dump/dumps * Added benchmark.py to compare simplejson and python-cjson Version 0.0.5 - July 21, 2008 ----------------------------- * Changed prefix of special fields to conform with CouchDB requirements (Thanks Dean Landolt). Break backwards compatibility. * Moved to Google Code subversion * Fixed unit test imports Version 0.0.3 ------------- * Convert back to setup.py from pavement.py (issue found by spidaman) Version 0.0.2 ------------- * Handle feedparser's FeedParserDict * Converted project to Paver * Restructured directories * Increase test coverage Version 0.0.1 ------------- Initial release jsonpickle-0.6.1/docs/source/conf.py0000664000501500000240000001602412205344652017621 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # jsonpickle documentation build configuration file, created by # sphinx-quickstart on Fri Oct 16 23:44:35 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath(os.path.join('..', '..'))) import jsonpickle sys.path.insert(1, os.path.abspath(os.path.join('..', '..', 'thirdparty', 'sphinx-to-github'))) try: import sphinxtogithub sphinxtogithub # import side-effects except ImportError, e: raise ImportError('Could not import sphinxtogithub\n' 'Is the git submodule populated at thirdparty/sphinx-to-github?\n' 'At the project root run:\n' '\tgit submodule init\n' '\tgit submodule update') # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinxtogithub'] sphinx_to_github = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'jsonpickle' copyright = u'2008-2011, John Paulett; 2009-2013, David Aguilar' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = jsonpickle.__version__ # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = filter(os.path.exists, ['_static']) # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'jsonpickledoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'jsonpickle.tex', u'jsonpickle Documentation', u'John Paulett', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} jsonpickle-0.6.1/docs/source/contrib.rst0000664000501500000240000000431212204557545020517 0ustar davidstaff00000000000000========================== Contributing to jsonpickle ========================== We welcome contributions from everyone. Please fork jsonpickle on `github `_. Get the Code ============ .. _jsonpickle-contrib-checkout: :: git clone git://github.com/jsonpickle/jsonpickle.git Run the Test Suite ================== Before code is pulled into the master jsonpickle branch, all tests should pass. If you are contributing an addition or a change in behavior, we ask that you document the change in the form of test cases. The jsonpickle test suite uses several JSON encoding libraries as well as several libraries for sample objects. To simplify the process of setting up these libraries we recommend creating a virtualenv_ and using a pip_ requirements file to install the dependencies. In the base jsonpickle directory:: # create a virtualenv that is completely isolated from the # site-wide python install virtualenv --no-site-packages env # activate the virtualenv source env/bin/activate # use pip to install the dependencies listed in the requirements file pip install --upgrade -r tests/test-req.txt To run the suite, simply invoke :file:`tests/runtests.py`:: $ tests/runtests.py test_None (util_tests.IsPrimitiveTestCase) ... ok test_bool (util_tests.IsPrimitiveTestCase) ... ok test_dict (util_tests.IsPrimitiveTestCase) ... ok test_float (util_tests.IsPrimitiveTestCase) ... ok ... .. _virtualenv: http://pypi.python.org/pypi/virtualenv .. _pip: http://pypi.python.org/pypi/pip Generate Documentation ====================== You do not need to generate the documentation_ when contributing, though, if you are interested, you can generate the docs yourself. The following requires Sphinx_ (present if you follow the virtualenv instructions above):: # pull down the sphinx-to-github project git submodule init git submodule update cd docs make html If you wish to browse the documentation, use Python's :mod:`SimpleHTTPServer` to host them at http://localhost:8000:: cd build/html python -m SimpleHTTPServer .. _documentation: http://jsonpickle.github.com .. _Sphinx: http://sphinx.pocoo.org jsonpickle-0.6.1/docs/source/index.rst0000664000501500000240000000471612205344056020166 0ustar davidstaff00000000000000======================== jsonpickle Documentation ======================== `jsonpickle `_ is a Python library for serialization and deserialization of complex Python objects to and from JSON. The standard Python libraries for encoding Python into JSON, such as the stdlib's json, simplejson, and demjson, can only handle Python primitives that have a direct JSON equivalent (e.g. dicts, lists, strings, ints, etc.). jsonpickle builds on top of these libraries and allows more complex data structures to be serialized to JSON. jsonpickle is highly configurable and extendable--allowing the user to choose the JSON backend and add additional backends. .. contents:: jsonpickle Usage ================ .. automodule:: jsonpickle Download & Install ================== The easiest way to get jsonpickle is via PyPi_ with pip_:: $ pip install -U jsonpickle For Python 2.6+, jsonpickle has no required dependencies (it uses the standard library's :mod:`json` module by default). For Python 2.5 or earlier, you must install a supported JSON backend (including simplejson or demjson). For example:: $ pip install simplejson You can also download or :ref:`checkout ` the latest code and install from source:: $ python setup.py install .. _PyPi: http://pypi.python.org/pypi/jsonpickle .. _pip: http://pypi.python.org/pypi/pip .. _download: http://pypi.python.org/pypi/jsonpickle API Reference ============= .. toctree:: :maxdepth: 3 api Contributing ============ .. toctree:: :maxdepth: 3 contrib Contact ======= Please join our `mailing list `_. You can send email to *jsonpickle@googlegroups.com*. Check http://github.com/jsonpickle/jsonpickle for project updates. Authors ======= * John Paulett - john -at- paulett.org - http://github.com/johnpaulett * David Aguilar - davvid -at- gmail.com - http://github.com/davvid * Dan Buch - http://github.com/meatballhat * Ian Schenck - http://github.com/ianschenck * David K. Hess - http://github.com/davidkhess * Alec Thomas - http://github.com/alecthomas * jaraco - https://github.com/jaraco Change Log ========== .. toctree:: :maxdepth: 2 changelog License ======= jsonpickle is provided under a `New BSD license `_, Copyright (C) 2008-2011 John Paulett (john -at- paulett.org) Copyright (C) 2009-2013 David Aguilar (davvid -at- gmail.com) jsonpickle-0.6.1/jsonpickle/0000775000501500000240000000000012206357645016237 5ustar davidstaff00000000000000jsonpickle-0.6.1/jsonpickle/__init__.py0000664000501500000240000001101312206075535020337 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # Copyright (C) 2009, 2011, 2013 David Aguilar (davvid -at- gmail.com) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. """Python library for serializing any arbitrary object graph into JSON. jsonpickle can take almost any Python object and turn the object into JSON. Additionally, it can reconstitute the object back into Python. The object must be accessible globally via a module and must inherit from object (AKA new-style classes). Create an object. >>> from jsonpickle._samples import Thing >>> obj = Thing('A String') >>> obj.name 'A String' Use jsonpickle to transform the object into a JSON string. >>> import jsonpickle >>> pickled = jsonpickle.encode(obj) Use jsonpickle to recreate a Python object from a JSON string >>> unpickled = jsonpickle.decode(pickled) >>> print(unpickled.name) A String .. warning:: Loading a JSON string from an untrusted source represents a potential security vulnerability. jsonpickle makes no attempt to sanitize the input. The new object has the same type and data, but essentially is now a copy of the original. >>> obj is unpickled False >>> obj.name == unpickled.name True >>> type(obj) == type(unpickled) True If you will never need to load (regenerate the Python class from JSON), you can pass in the keyword unpicklable=False to prevent extra information from being added to JSON. >>> oneway = jsonpickle.encode(obj, unpicklable=False) >>> result = jsonpickle.decode(oneway) >>> print(result['name']) A String >>> print(result['child']) None """ from jsonpickle import pickler from jsonpickle import unpickler from jsonpickle.backend import JSONBackend from jsonpickle.version import VERSION # ensure built-in handlers are loaded __import__('jsonpickle.handlers') __all__ = ('encode', 'decode') __version__ = VERSION json = JSONBackend() # Export specific JSONPluginMgr methods into the jsonpickle namespace set_preferred_backend = json.set_preferred_backend set_encoder_options = json.set_encoder_options load_backend = json.load_backend remove_backend = json.remove_backend def encode(value, unpicklable=True, make_refs=True, keys=False, max_depth=None, backend=None): """ Return a JSON formatted representation of value, a Python object. The keyword argument 'unpicklable' defaults to True. If set to False, the output will not contain the information necessary to turn the JSON data back into Python objects. The keyword argument 'max_depth' defaults to None. If set to a non-negative integer then jsonpickle will not recurse deeper than 'max_depth' steps into the object. Anything deeper than 'max_depth' is represented using a Python repr() of the object. The keyword argument 'make_refs' defaults to True. If set to False jsonpickle's referencing support is disabled. Objects that are id()-identical won't be preserved across encode()/decode(), but the resulting JSON stream will be conceptually simpler. jsonpickle detects cyclical objects and will break the cycle by calling repr() instead of recursing when make_refs is set False. The keyword argument 'keys' defaults to False. If set to True then jsonpickle will encode non-string dictionary keys instead of coercing them into strings via `repr()`. >>> encode('my string') '"my string"' >>> encode(36) '36' >>> encode({'foo': True}) '{"foo": true}' >>> encode({'foo': True}, max_depth=0) '"{\\'foo\\': True}"' >>> encode({'foo': True}, max_depth=1) '{"foo": "True"}' """ if backend is None: backend = json return pickler.encode(value, backend=backend, unpicklable=unpicklable, make_refs=make_refs, keys=keys, max_depth=max_depth) def decode(string, backend=None, keys=False): """ Convert a JSON string into a Python object. The keyword argument 'keys' defaults to False. If set to True then jsonpickle will decode non-string dictionary keys into python objects via the jsonpickle protocol. >>> str(decode('"my string"')) 'my string' >>> decode('36') 36 """ if backend is None: backend = json return unpickler.decode(string, backend=backend, keys=keys) jsonpickle-0.6.1/jsonpickle/_samples.py0000664000501500000240000000752512206075535020420 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import collections import datetime import jsonpickle from jsonpickle.compat import set class Thing(object): def __init__(self, name): self.name = name self.child = None def __repr__(self): return 'Thing("%s")' % self.name class ThingWithSlots(object): __slots__ = ('a', 'b') def __init__(self, a, b): self.a = a self.b = b class ThingWithProps(object): def __init__(self, name='', dogs='reliable', monkies='tricksy'): self.name = name self._critters = (('dogs', dogs), ('monkies', monkies)) def _get_identity(self): keys = [self.dogs, self.monkies, self.name] return hash('-'.join([str(key) for key in keys])) identity = property(_get_identity) def _get_dogs(self): return self._critters[0][1] dogs = property(_get_dogs) def _get_monkies(self): return self._critters[1][1] monkies = property(_get_monkies) def __getstate__(self): out = dict( __identity__=self.identity, nom=self.name, dogs=self.dogs, monkies=self.monkies, ) return out def __setstate__(self, state_dict): self._critters = (('dogs', state_dict.get('dogs')), ('monkies', state_dict.get('monkies'))) self.name = state_dict.get('nom', '') ident = state_dict.get('__identity__') if ident != self.identity: raise ValueError('expanded object does not match originial state!') def __eq__(self, other): return self.identity == other.identity class DictSubclass(dict): name = 'Test' class ListSubclass(list): pass class ListSubclassWithInit(list): def __init__(self, attr): self.attr = attr super(ListSubclassWithInit, self).__init__() class SetSubclass(set): pass NamedTuple = collections.namedtuple('NamedTuple', 'a, b, c') class BrokenReprThing(Thing): def __repr__(self): raise Exception('%s has a broken repr' % self.name) def __str__(self): return '' % self.name class Node(object): def __init__(self, name): self._name = name self._children = [] self._parent = None def add_child(self, child, index=-1): if index == -1: index = len(self._children) self._children.insert(index, child) child._parent = self class Document(Node): def __init__(self, name): Node.__init__(self, name) def __str__(self): ret_str ='Document "%s"\n' %self._name for c in self._children: ret_str += repr(c) return ret_str def __repr__(self): return self.__str__() class Section(Node): def __init__(self, name): Node.__init__(self, name) def __str__(self): ret_str = 'Section "%s", parent: "%s"\n' % (self._name, self._parent._name) for c in self._children: ret_str += repr(c) return ret_str def __repr__(self): return self.__str__() class Question(Node): def __init__(self, name): Node.__init__(self, name) def __str__(self): return 'Question "%s", parent: "%s"\n' % (self._name, self._parent._name) def __repr__(self): return self.__str__() class ObjWithDate(object): def __init__(self): ts = datetime.datetime.now() self.data = dict(a='a', ts=ts) self.data_ref = dict(b='b', ts=ts) class ObjWithJsonPickleRepr(object): def __init__(self): self.data = {'a': self} def __repr__(self): return jsonpickle.encode(self) class OldStyleClass: pass jsonpickle-0.6.1/jsonpickle/backend.py0000664000501500000240000001626612206352323020177 0ustar davidstaff00000000000000from jsonpickle.compat import PY32 class JSONBackend(object): """Manages encoding and decoding using various backends. It tries these modules in this order: simplejson, json, demjson simplejson is a fast and popular backend and is tried first. json comes with python2.6 and is tried second. demjson is the most permissive backend and is tried last. """ def __init__(self): ## The names of backends that have been successfully imported self._backend_names = [] ## A dictionary mapping backend names to encode/decode functions self._encoders = {} self._decoders = {} ## Options to pass to specific encoders json_opts = ((), {'sort_keys': True}) self._encoder_options = { 'json': json_opts, 'simplejson': json_opts, 'django.util.simplejson': json_opts, } ## The exception class that is thrown when a decoding error occurs self._decoder_exceptions = {} ## Whether we've loaded any backends successfully self._verified = False if not PY32: self.load_backend('simplejson', 'dumps', 'loads', ValueError) self.load_backend('json', 'dumps', 'loads', ValueError) self.load_backend('demjson', 'encode', 'decode', 'JSONDecodeError') self.load_backend('jsonlib', 'write', 'read', 'ReadError') self.load_backend('yajl', 'dumps', 'loads', ValueError) self.load_backend('ujson', 'dumps', 'loads', ValueError) def _verify(self): """Ensures that we've loaded at least one JSON backend.""" if self._verified: return raise AssertionError('jsonpickle requires at least one of the ' 'following:\n' ' python2.6, simplejson, or demjson') def load_backend(self, name, encode_name, decode_name, decode_exc): """ Load a JSON backend by name. This method loads a backend and sets up references to that backend's encode/decode functions and exception classes. :param encode_name: is the name of the backend's encode method. The method should take an object and return a string. :param decode_name: names the backend's method for the reverse operation -- returning a Python object from a string. :param decode_exc: can be either the name of the exception class used to denote decoding errors, or it can be a direct reference to the appropriate exception class itself. If it is a name, then the assumption is that an exception class of that name can be found in the backend module's namespace. """ try: ## Load the JSON backend mod = __import__(name) except ImportError: return try: ## Handle submodules, e.g. django.utils.simplejson components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) except AttributeError: return try: ## Setup the backend's encode/decode methods self._encoders[name] = getattr(mod, encode_name) self._decoders[name] = getattr(mod, decode_name) except AttributeError: self.remove_backend(name) return try: if type(decode_exc) is str: ## This backend's decoder exception is part of the backend self._decoder_exceptions[name] = getattr(mod, decode_exc) else: ## simplejson uses the ValueError exception self._decoder_exceptions[name] = decode_exc except AttributeError: self.remove_backend(name) return ## Setup the default args and kwargs for this encoder self._encoder_options[name] = ([], {}) ## Add this backend to the list of candidate backends self._backend_names.append(name) ## Indicate that we successfully loaded a JSON backend self._verified = True def remove_backend(self, name): """Remove all entries for a particular backend.""" self._encoders.pop(name, None) self._decoders.pop(name, None) self._decoder_exceptions.pop(name, None) self._encoder_options.pop(name, None) if name in self._backend_names: self._backend_names.remove(name) self._verified = bool(self._backend_names) def encode(self, obj): """ Attempt to encode an object into JSON. This tries the loaded backends in order and passes along the last exception if no backend is able to encode the object. """ self._verify() for idx, name in enumerate(self._backend_names): try: optargs, optkwargs = self._encoder_options[name] encoder_kwargs = optkwargs.copy() encoder_args = (obj,) + tuple(optargs) return self._encoders[name](*encoder_args, **encoder_kwargs) except Exception: if idx == len(self._backend_names) - 1: raise def decode(self, string): """ Attempt to decode an object from a JSON string. This tries the loaded backends in order and passes along the last exception if no backends are able to decode the string. """ self._verify() for idx, name in enumerate(self._backend_names): try: return self._decoders[name](string) except self._decoder_exceptions[name] as e: if idx == len(self._backend_names) - 1: raise e else: pass # and try a more forgiving encoder, e.g. demjson def set_preferred_backend(self, name): """ Set the preferred json backend. If a preferred backend is set then jsonpickle tries to use it before any other backend. For example:: set_preferred_backend('simplejson') If the backend is not one of the built-in jsonpickle backends (json/simplejson, or demjson) then you must load the backend prior to calling set_preferred_backend. AssertionError is raised if the backend has not been loaded. """ if name in self._backend_names: self._backend_names.remove(name) self._backend_names.insert(0, name) else: errmsg = 'The "%s" backend has not been loaded.' % name raise AssertionError(errmsg) def set_encoder_options(self, name, *args, **kwargs): """ Associate encoder-specific options with an encoder. After calling set_encoder_options, any calls to jsonpickle's encode method will pass the supplied args and kwargs along to the appropriate backend's encode method. For example:: set_encoder_options('simplejson', sort_keys=True, indent=4) set_encoder_options('demjson', compactly=False) See the appropriate encoder's documentation for details about the supported arguments and keyword arguments. """ self._encoder_options[name] = (args, kwargs) jsonpickle-0.6.1/jsonpickle/compat.py0000664000501500000240000000071112206353231020056 0ustar davidstaff00000000000000import sys # True if we are running on Python 3. PY3 = sys.version_info[0] == 3 PY32 = PY3 and sys.version_info[1] == 2 PY2 = not PY3 try: bytes = bytes except NameError: bytes = str try: set = set except NameError: from sets import Set as set set = set try: unicode = unicode except NameError: unicode = str try: long = long except NameError: long = int try: unichr = unichr except NameError: unichr = chr jsonpickle-0.6.1/jsonpickle/handlers.py0000664000501500000240000000772512206356002020406 0ustar davidstaff00000000000000""" Custom handlers may be created to handle other objects. Each custom handler must derive from :class:`jsonpickle.handlers.BaseHandler` and implement ``flatten`` and ``restore``. A handler can be bound to other types by calling :func:`jsonpickle.handlers.register`. :class:`jsonpickle.customhandlers.SimpleReduceHandler` is suitable for handling objects that implement the reduce protocol:: from jsonpickle import handlers class MyCustomObject(handlers.BaseHandler): ... def __reduce__(self): return MyCustomObject, self._get_args() handlers.register(MyCustomObject, handlers.SimpleReduceHandler) """ import sys import datetime import time import collections from jsonpickle import util from jsonpickle.compat import unicode from jsonpickle.compat import bytes from jsonpickle.compat import PY3 class Registry(object): def __init__(self): self._handlers = {} def register(self, cls, handler): """Register the a custom handler for a class :param cls: The custom object class to handle :param handler: The custom handler class """ self._handlers[cls] = handler def get(self, cls): return self._handlers.get(cls) registry = Registry() register = registry.register get = registry.get class BaseHandler(object): def __init__(self, context): """ Initialize a new handler to handle a registered type. :Parameters: - `context`: reference to pickler/unpickler """ self.context = context def flatten(self, obj, data): """Flatten `obj` into a json-friendly form and write result in `data`""" raise NotImplementedError('You must implement flatten() in %s' % self.__class__) def restore(self, obj): """Restore the json-friendly `obj` to the registered type""" raise NotImplementedError('You must implement restore() in %s' % self.__class__) class DatetimeHandler(BaseHandler): """Custom handler for datetime objects Datetime objects use __reduce__, and they generate binary strings encoding the payload. This handler encodes that payload to reconstruct the object. """ def flatten(self, obj, data): pickler = self.context if not pickler.unpicklable: return unicode(obj) cls, args = obj.__reduce__() flatten = pickler.flatten payload = util.b64encode(args[0]) args = [payload] + [flatten(i, reset=False) for i in args[1:]] data['__reduce__'] = (flatten(cls, reset=False), args) return data def restore(self, obj): cls, args = obj['__reduce__'] unpickler = self.context restore = unpickler.restore cls = restore(cls, reset=False) value = util.b64decode(args[0]) params = (value,) + tuple([restore(i, reset=False) for i in args[1:]]) return cls.__new__(cls, *params) register(datetime.datetime, DatetimeHandler) register(datetime.date, DatetimeHandler) register(datetime.time, DatetimeHandler) class SimpleReduceHandler(BaseHandler): """ Follow the __reduce__ protocol to pickle an object. As long as the factory and its arguments are pickleable, this should pickle any object that implements the reduce protocol. """ def flatten(self, obj, data): pickler = self.context if not pickler.unpicklable: return unicode(obj) flatten = pickler.flatten data['__reduce__'] = [flatten(i, reset=False) for i in obj.__reduce__()] return data def restore(self, obj): unpickler = self.context restore = unpickler.restore factory, args = [restore(i, reset=False) for i in obj['__reduce__']] return factory(*args) register(time.struct_time, SimpleReduceHandler) register(datetime.timedelta, SimpleReduceHandler) if sys.version_info >= (2, 7): register(collections.OrderedDict, SimpleReduceHandler) jsonpickle-0.6.1/jsonpickle/pickler.py0000664000501500000240000002413112206103614020224 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # Copyright (C) 2009, 2011, 2013 David Aguilar (davvid -at- gmail.com) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import operator import jsonpickle.util as util import jsonpickle.tags as tags import jsonpickle.handlers as handlers from jsonpickle.backend import JSONBackend from jsonpickle.compat import unicode def encode(value, unpicklable=False, make_refs=True, keys=False, max_depth=None, reset=True, backend=None, context=None): backend = _make_backend(backend) if context is None: context = Pickler(unpicklable=unpicklable, make_refs=make_refs, keys=keys, backend=backend, max_depth=max_depth) return backend.encode(context.flatten(value, reset=reset)) def _make_backend(backend): if backend is None: return JSONBackend() else: return backend class Pickler(object): def __init__(self, unpicklable=True, make_refs=True, max_depth=None, backend=None, keys=False): self.unpicklable = unpicklable self.make_refs = make_refs self.backend = _make_backend(backend) self.keys = keys ## The current recursion depth self._depth = -1 ## The maximal recursion depth self._max_depth = max_depth ## Maps id(obj) to reference IDs self._objs = {} def reset(self): self._objs = {} self._depth = -1 def _push(self): """Steps down one level in the namespace. """ self._depth += 1 def _pop(self, value): """Step up one level in the namespace and return the value. If we're at the root, reset the pickler's state. """ self._depth -= 1 if self._depth == -1: self.reset() return value def _mkref(self, obj): objid = id(obj) if objid not in self._objs: new_id = len(self._objs) self._objs[objid] = new_id return True # Do not use references if not unpicklable. if not self.unpicklable or not self.make_refs: return True else: return False def _getref(self, obj): return {tags.ID: self._objs.get(id(obj))} def flatten(self, obj, reset=True): """Takes an object and returns a JSON-safe representation of it. Simply returns any of the basic builtin datatypes >>> p = Pickler() >>> p.flatten('hello world') 'hello world' >>> p.flatten(49) 49 >>> p.flatten(350.0) 350.0 >>> p.flatten(True) True >>> p.flatten(False) False >>> r = p.flatten(None) >>> r is None True >>> p.flatten(False) False >>> p.flatten([1, 2, 3, 4]) [1, 2, 3, 4] >>> p.flatten((1,2,))[tags.TUPLE] [1, 2] >>> p.flatten({'key': 'value'}) {'key': 'value'} """ if reset: self.reset() return self._flatten(obj) def _flatten(self, obj): self._push() max_reached = self._depth == self._max_depth if max_reached or (not self.make_refs and id(obj) in self._objs): # break the cycle flatten_func = repr else: flatten_func = self._get_flattener(obj) return self._pop(flatten_func(obj)) def _get_flattener(self, obj): if util.is_primitive(obj): return lambda obj: obj list_recurse = lambda obj: [self._flatten(v) for v in obj] if util.is_list(obj): if self._mkref(obj): return list_recurse else: self._push() return self._getref # We handle tuples and sets by encoding them in a "(tuple|set)dict" if util.is_tuple(obj): if not self.unpicklable: return list_recurse return lambda obj: {tags.TUPLE: [self._flatten(v) for v in obj]} if util.is_set(obj): if not self.unpicklable: return list_recurse return lambda obj: {tags.SET: [self._flatten(v) for v in obj]} if util.is_dictionary(obj): return self._flatten_dict_obj if util.is_type(obj): return _mktyperef if util.is_object(obj): return self._ref_obj_instance # else, what else? (methods, functions, old style classes...) return None def _ref_obj_instance(self, obj): """Reference an existing object or flatten if new """ if self._mkref(obj): # We've never seen this object so return its # json representation. return self._flatten_obj_instance(obj) # We've seen this object before so place an object # reference tag in the data. This avoids infinite recursion # when processing cyclical objects. return self._getref(obj) def _flatten_obj_instance(self, obj): """Recursively flatten an instance and return a json-friendly dict """ data = {} has_class = hasattr(obj, '__class__') has_dict = hasattr(obj, '__dict__') has_slots = not has_dict and hasattr(obj, '__slots__') has_getstate = has_dict and hasattr(obj, '__getstate__') has_getstate_support = has_getstate and hasattr(obj, '__setstate__') HandlerClass = handlers.get(type(obj)) if has_class and not util.is_module(obj): module, name = _getclassdetail(obj) if self.unpicklable: data[tags.OBJECT] = '%s.%s' % (module, name) # Check for a custom handler if HandlerClass: handler = HandlerClass(self) flat_obj = handler.flatten(obj, data) self._mkref(flat_obj) return flat_obj if util.is_module(obj): if self.unpicklable: data[tags.REPR] = '%s/%s' % (obj.__name__, obj.__name__) else: data = unicode(obj) return data if util.is_dictionary_subclass(obj): return self._flatten_dict_obj(obj, data) if has_dict: # Support objects that subclasses list and set if util.is_sequence_subclass(obj): return self._flatten_sequence_obj(obj, data) # Support objects with __getstate__(); this ensures that # both __setstate__() and __getstate__() are implemented if has_getstate_support: state = self._flatten(obj.__getstate__()) if self.unpicklable: data[tags.STATE] = state else: data = state return data # hack for zope persistent objects; this unghostifies the object getattr(obj, '_', None) return self._flatten_dict_obj(obj.__dict__, data) if util.is_sequence_subclass(obj): return self._flatten_sequence_obj(obj, data) if util.is_noncomplex(obj): return [self._flatten(v) for v in obj] if has_slots: return self._flatten_newstyle_with_slots(obj, data) def _flatten_dict_obj(self, obj, data=None): """Recursively call flatten() and return json-friendly dict """ if data is None: data = obj.__class__() flatten = self._flatten_key_value_pair for k, v in sorted(obj.items(), key=operator.itemgetter(0)): flatten(k, v, data) # the collections.defaultdict protocol if hasattr(obj, 'default_factory') and callable(obj.default_factory): flatten('default_factory', obj.default_factory, data) return data def _flatten_newstyle_with_slots(self, obj, data): """Return a json-friendly dict for new-style objects with __slots__. """ for k in obj.__slots__: self._flatten_key_value_pair(k, getattr(obj, k), data) return data def _flatten_key_value_pair(self, k, v, data): """Flatten a key/value pair into the passed-in dictionary.""" if not util.is_picklable(k, v): return data if not isinstance(k, (str, unicode)): if self.keys: k = tags.JSON_KEY + encode(k, reset=False, keys=True, context=self, backend=self.backend, make_refs=self.make_refs) else: try: k = repr(k) except: k = unicode(k) data[k] = self._flatten(v) return data def _flatten_sequence_obj(self, obj, data): """Return a json-friendly dict for a sequence subclass.""" if hasattr(obj, '__dict__'): self._flatten_dict_obj(obj.__dict__, data) value = [self._flatten(v) for v in obj] if self.unpicklable: data[tags.SEQ] = value else: return value return data def _mktyperef(obj): """Return a typeref dictionary >>> _mktyperef(AssertionError) {'py/type': '__builtin__.AssertionError'} """ return {tags.TYPE: '%s.%s' % (util.translate_module_name(obj.__module__), obj.__name__)} def _getclassdetail(obj): """Helper class to return the class of an object. >>> class Example(object): pass >>> _getclassdetail(Example()) ('jsonpickle.pickler', 'Example') >>> _getclassdetail(25) ('__builtin__', 'int') >>> _getclassdetail(None) ('__builtin__', 'NoneType') >>> _getclassdetail(False) ('__builtin__', 'bool') >>> _getclassdetail(AttributeError) ('__builtin__', 'type') """ cls = obj.__class__ module = getattr(cls, '__module__') name = getattr(cls, '__name__') return util.translate_module_name(module), name jsonpickle-0.6.1/jsonpickle/tags.py0000664000501500000240000000112112204623116017525 0ustar davidstaff00000000000000"""The jsonpickle.tags module provides the custom tags used for pickling and unpickling Python objects. These tags are keys into the flattened dictionaries created by the Pickler class. The Unpickler uses these custom key names to identify dictionaries that need to be specially handled. """ from jsonpickle.compat import set ID = 'py/id' OBJECT = 'py/object' TYPE = 'py/type' REPR = 'py/repr' REF = 'py/ref' TUPLE = 'py/tuple' SET = 'py/set' SEQ = 'py/seq' STATE = 'py/state' JSON_KEY = 'json://' # All reserved tag names RESERVED = set([OBJECT, TYPE, REPR, REF, TUPLE, SET, SEQ, STATE]) jsonpickle-0.6.1/jsonpickle/unpickler.py0000664000501500000240000002346712206077502020610 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # Copyright (C) 2009, 2011, 2013 David Aguilar (davvid -at- gmail.com) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import operator import sys import jsonpickle.util as util import jsonpickle.tags as tags import jsonpickle.handlers as handlers from jsonpickle.compat import set from jsonpickle.backend import JSONBackend def decode(string, backend=None, context=None, keys=False, reset=True): backend = _make_backend(backend) if context is None: context = Unpickler(keys=keys, backend=backend) return context.restore(backend.decode(string), reset=reset) def _make_backend(backend): if backend is None: return JSONBackend() else: return backend class Unpickler(object): def __init__(self, backend=None, keys=False): ## The current recursion depth ## Maps reference names to object instances self.backend = _make_backend(backend) self.keys = keys self._namedict = {} ## The namestack grows whenever we recurse into a child object self._namestack = [] ## Maps objects to their index in the _objs list self._obj_to_idx = {} self._objs = [] def reset(self): """Resets the object's internal state. """ self._namedict = {} self._namestack = [] self._obj_to_idx = {} self._objs = [] def restore(self, obj, reset=True): """Restores a flattened object to its original python state. Simply returns any of the basic builtin types >>> u = Unpickler() >>> u.restore('hello world') 'hello world' >>> u.restore({'key': 'value'}) {'key': 'value'} """ if reset: self.reset() return self._restore(obj) def _restore(self, obj): if has_tag(obj, tags.ID): restore = self._restore_id elif has_tag(obj, tags.REF): # Backwards compatibility restore = self._restore_ref elif has_tag(obj, tags.TYPE): restore = self._restore_type elif has_tag(obj, tags.REPR): # Backwards compatibility restore = self._restore_repr elif has_tag(obj, tags.OBJECT): restore = self._restore_object elif util.is_list(obj): restore = self._restore_list elif has_tag(obj, tags.TUPLE): restore = self._restore_tuple elif has_tag(obj, tags.SET): restore = self._restore_set elif util.is_dictionary(obj): restore = self._restore_dict else: restore = lambda x: x return restore(obj) def _restore_id(self, obj): return self._objs[obj[tags.ID]] def _restore_ref(self, obj): return self._namedict.get(obj[tags.REF]) def _restore_type(self, obj): typeref = loadclass(obj[tags.TYPE]) if typeref is None: return obj return typeref def _restore_repr(self, obj): obj = loadrepr(obj[tags.REPR]) return self._mkref(obj) def _restore_object(self, obj): cls = loadclass(obj[tags.OBJECT]) if cls is None: return self._mkref(obj) handler = handlers.get(cls) if handler is not None: # custom handler instance = handler(self).restore(obj) return self._mkref(instance) else: return self._restore_object_instance(obj, cls) def _restore_object_instance(self, obj, cls): factory = loadfactory(obj) args = getargs(obj) if args: args = self._restore(args) try: if hasattr(cls, '__new__'): # new style classes if factory: instance = cls.__new__(cls, factory, *args) instance.default_factory = factory else: instance = cls.__new__(cls, *args) else: instance = object.__new__(cls) except TypeError: # old-style classes try: instance = cls() except TypeError: # fail gracefully return self._mkref(obj) self._mkref(instance) # allow references in downstream objects if isinstance(instance, tuple): return instance return self._restore_object_instance_variables(obj, instance) def _restore_object_instance_variables(self, obj, instance): if hasattr(instance, '__setstate__') and has_tag(obj, tags.STATE): state = self._restore(obj[tags.STATE]) instance.__setstate__(state) return instance for k, v in sorted(obj.items(), key=operator.itemgetter(0)): # ignore the reserved attribute if k in tags.RESERVED: continue self._namestack.append(k) # step into the namespace value = self._restore(v) if (util.is_noncomplex(instance) or util.is_dictionary_subclass(instance)): instance[k] = value else: setattr(instance, k, value) # step out self._namestack.pop() # Handle list and set subclasses if has_tag(obj, tags.SEQ): if hasattr(instance, 'append'): for v in obj[tags.SEQ]: instance.append(self._restore(v)) if hasattr(instance, 'add'): for v in obj[tags.SEQ]: instance.add(self._restore(v)) return instance def _restore_list(self, obj): parent = [] self._mkref(parent) children = [self._restore(v) for v in obj] parent.extend(children) return parent def _restore_tuple(self, obj): return tuple([self._restore(v) for v in obj[tags.TUPLE]]) def _restore_set(self, obj): return set([self._restore(v) for v in obj[tags.SET]]) def _restore_dict(self, obj): data = {} for k, v in sorted(obj.items(), key=operator.itemgetter(0)): self._namestack.append(k) if self.keys and k.startswith(tags.JSON_KEY): k = decode(k[len(tags.JSON_KEY):], backend=self.backend, context=self, keys=True, reset=False) data[k] = self._restore(v) self._namestack.pop() return data def _refname(self): """Calculates the name of the current location in the JSON stack. This is called as jsonpickle traverses the object structure to create references to previously-traversed objects. This allows cyclical data structures such as doubly-linked lists. jsonpickle ensures that duplicate python references to the same object results in only a single JSON object definition and special reference tags to represent each reference. >>> u = Unpickler() >>> u._namestack = [] >>> u._refname() '/' >>> u._namestack = ['a'] >>> u._refname() '/a' >>> u._namestack = ['a', 'b'] >>> u._refname() '/a/b' """ return '/' + '/'.join(self._namestack) def _mkref(self, obj): """ >>> from jsonpickle._samples import Thing >>> thing = Thing('referenced-thing') >>> u = Unpickler() >>> u._mkref(thing) Thing("referenced-thing") >>> u._objs[0] Thing("referenced-thing") """ obj_id = id(obj) try: self._obj_to_idx[obj_id] except KeyError: self._obj_to_idx[obj_id] = len(self._objs) self._objs.append(obj) # Backwards compatibility: old versions of jsonpickle # produced "py/ref" references. self._namedict[self._refname()] = obj return obj def loadclass(module_and_name): """Loads the module and returns the class. >>> loadclass('jsonpickle._samples.Thing') >>> loadclass('does.not.exist') >>> loadclass('__builtin__.int')() 0 """ try: module, name = module_and_name.rsplit('.', 1) module = util.untranslate_module_name(module) __import__(module) return getattr(sys.modules[module], name) except: return None def loadfactory(obj): try: default_factory = obj['default_factory'] except KeyError: return None try: type_tag = default_factory[tags.TYPE] except: return None typeref = loadclass(type_tag) if typeref: del obj['default_factory'] return typeref return None def getargs(obj): try: seq_list = obj[tags.SEQ] obj_dict = obj[tags.OBJECT] except KeyError: return [] typeref = loadclass(obj_dict) if not typeref: return [] if hasattr(typeref, '_fields'): if len(typeref._fields) == len(seq_list): return seq_list return [] def loadrepr(reprstr): """Returns an instance of the object from the object's repr() string. It involves the dynamic specification of code. >>> loadrepr('jsonpickle._samples/jsonpickle._samples.Thing("json")') Thing("json") """ module, evalstr = reprstr.split('/') mylocals = locals() localname = module if '.' in localname: localname = module.split('.', 1)[0] mylocals[localname] = __import__(module) return eval(evalstr) def has_tag(obj, tag): """Helper class that tests to see if the obj is a dictionary and contains a particular key/tag. >>> obj = {'test': 1} >>> has_tag(obj, 'test') True >>> has_tag(obj, 'fail') False >>> has_tag(42, 'fail') False """ return type(obj) is dict and tag in obj jsonpickle-0.6.1/jsonpickle/util.py0000664000501500000240000001530412206355672017567 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. """Helper functions for pickling and unpickling. Most functions assist in determining the type of an object. """ import base64 import time import types from jsonpickle import tags from jsonpickle.compat import set from jsonpickle.compat import unicode from jsonpickle.compat import long from jsonpickle.compat import PY3 SEQUENCES = (list, set, tuple) SEQUENCES_SET = set(SEQUENCES) PRIMITIVES = set((str, unicode, bool, float, int, long)) def is_type(obj): """Returns True is obj is a reference to a type. >>> is_type(1) False >>> is_type(object) True >>> class Klass: pass >>> is_type(Klass) True """ if PY3: return type(obj) is type else: return type(obj) is type or type(obj) is types.ClassType def is_object(obj): """Returns True is obj is a reference to an object instance. >>> is_object(1) True >>> is_object(object()) True >>> is_object(lambda x: 1) False """ return (isinstance(obj, object) and type(obj) is not type and type(obj) is not types.FunctionType) def is_primitive(obj): """Helper method to see if the object is a basic data type. Strings, integers, longs, floats, booleans, and None are considered primitive and will return True when passed into *is_primitive()* >>> is_primitive(3) True >>> is_primitive([4,4]) False """ if obj is None: return True elif type(obj) in PRIMITIVES: return True return False def is_dictionary(obj): """Helper method for testing if the object is a dictionary. >>> is_dictionary({'key':'value'}) True """ return type(obj) is dict def is_sequence(obj): """Helper method to see if the object is a sequence (list, set, or tuple). >>> is_sequence([4]) True """ return type(obj) in SEQUENCES_SET def is_list(obj): """Helper method to see if the object is a Python list. >>> is_list([4]) True """ return type(obj) is list def is_set(obj): """Helper method to see if the object is a Python set. >>> is_set(set()) True """ return type(obj) is set def is_tuple(obj): """Helper method to see if the object is a Python tuple. >>> is_tuple((1,)) True """ return type(obj) is tuple def is_dictionary_subclass(obj): """Returns True if *obj* is a subclass of the dict type. *obj* must be a subclass and not the actual builtin dict. >>> class Temp(dict): pass >>> is_dictionary_subclass(Temp()) True """ #TODO add UserDict return (hasattr(obj, '__class__') and issubclass(obj.__class__, dict) and not is_dictionary(obj)) def is_sequence_subclass(obj): """Returns True if *obj* is a subclass of list, set or tuple. *obj* must be a subclass and not the actual builtin, such as list, set, tuple, etc.. >>> class Temp(list): pass >>> is_sequence_subclass(Temp()) True """ return ((issubclass(obj.__class__, SEQUENCES) or is_list_like(obj)) and not is_sequence(obj)) def is_noncomplex(obj): """Returns True if *obj* is a special (weird) class, that is more complex than primitive data types, but is not a full object. Including: * :class:`~time.struct_time` """ if type(obj) is time.struct_time: return True return False def is_function(obj): """Returns true if passed a function >>> is_function(lambda x: 1) True >>> is_function(locals) True >>> def method(): pass >>> is_function(method) True >>> is_function(1) False """ if type(obj) in (types.FunctionType, types.MethodType, types.LambdaType, types.BuiltinFunctionType, types.BuiltinMethodType): return True if not hasattr(obj, '__class__'): return False module = translate_module_name(obj.__class__.__module__) name = obj.__class__.__name__ return (module == '__builtin__' and name in ('function', 'builtin_function_or_method', 'instancemethod', 'method-wrapper')) def is_module(obj): """Returns True if passed a module >>> import os >>> is_module(os) True """ return type(obj) is types.ModuleType def is_picklable(name, value): """Return True if an object cannot be pickled >>> import os >>> is_picklable('os', os) True >>> def foo(): pass >>> is_picklable('foo', foo) False """ if name in tags.RESERVED: return False return not is_function(value) def is_installed(module): """Tests to see if ``module`` is available on the sys.path >>> is_installed('sys') True >>> is_installed('hopefullythisisnotarealmodule') False """ try: __import__(module) return True except ImportError as e: return False def is_list_like(obj): return hasattr(obj, '__getitem__') and hasattr(obj, 'append') def translate_module_name(module): """Rename builtin modules to a consistent (Python2) module name This is used so that references to Python's `builtins` module can be loaded in both Python 2 and 3. We remap to the "__builtin__" name and unmap it when importing. See untranslate_module_name() for the reverse operation. """ if (PY3 and module == 'builtins') or module == 'exceptions': # We map the Python2 `exceptions` module to `__builtin__` because # `__builtin__` is a superset and contains everything that is # available in `exceptions`, which makes the translation simpler. return '__builtin__' else: return module def untranslate_module_name(module): """Rename module names mention in JSON to names that we can import This reverses the translation applied by translate_module_name() to a module name available to the current version of Python. """ if PY3: # remap `__builtin__` and `exceptions` to the `builtins` module if module == '__builtin__': module = 'builtins' elif module == 'exceptions': module = 'builtins' return module def b64encode(data): payload = base64.b64encode(data) if PY3 and type(payload) is bytes: payload = payload.decode('ascii') return payload def b64decode(payload): if PY3 and type(payload) is not bytes: payload = bytes(payload, 'ascii') return base64.b64decode(payload) jsonpickle-0.6.1/jsonpickle/version.py0000664000501500000240000000002212206357273020265 0ustar davidstaff00000000000000VERSION = '0.6.1' jsonpickle-0.6.1/jsonpickle.egg-info/0000775000501500000240000000000012206357645017731 5ustar davidstaff00000000000000jsonpickle-0.6.1/jsonpickle.egg-info/dependency_links.txt0000664000501500000240000000000112206357643023775 0ustar davidstaff00000000000000 jsonpickle-0.6.1/jsonpickle.egg-info/PKG-INFO0000664000501500000240000000174212206357643021030 0ustar davidstaff00000000000000Metadata-Version: 1.1 Name: jsonpickle Version: 0.6.1 Summary: Python library for serializing any arbitrary object graph into JSON Home-page: http://jsonpickle.github.io/ Author: David Aguilar Author-email: davvid -at- gmail.com License: BSD Description: jsonpickle converts complex Python objects to and from JSON. Keywords: json pickle,json,pickle,marshal,serialization,JavaScript Object Notation Platform: POSIX Platform: Windows Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: JavaScript jsonpickle-0.6.1/jsonpickle.egg-info/SOURCES.txt0000664000501500000240000000143112206357645021614 0ustar davidstaff00000000000000COPYING MANIFEST.in README.rst Rakefile requirements-2.txt requirements-3.txt requirements-test.txt requirements.txt setup.py docs/source/api.rst docs/source/changelog.rst docs/source/conf.py docs/source/contrib.rst docs/source/index.rst jsonpickle/__init__.py jsonpickle/_samples.py jsonpickle/backend.py jsonpickle/compat.py jsonpickle/handlers.py jsonpickle/pickler.py jsonpickle/tags.py jsonpickle/unpickler.py jsonpickle/util.py jsonpickle/version.py jsonpickle.egg-info/PKG-INFO jsonpickle.egg-info/SOURCES.txt jsonpickle.egg-info/dependency_links.txt jsonpickle.egg-info/top_level.txt tests/backends_tests.py tests/benchmark.py tests/datetime_tests.py tests/document_test.py tests/jsonpickle_test.py tests/runtests.py tests/test_handlers.py tests/thirdparty_tests.py tests/util_tests.pyjsonpickle-0.6.1/jsonpickle.egg-info/top_level.txt0000664000501500000240000000001312206357643022453 0ustar davidstaff00000000000000jsonpickle jsonpickle-0.6.1/MANIFEST.in0000664000501500000240000000021112205337271015616 0ustar davidstaff00000000000000include COPYING README.rst include Rakefile include *.py include jsonpickle/*.py include tests/*.py include docs/source/** include *.txt jsonpickle-0.6.1/PKG-INFO0000664000501500000240000000174212206357645015177 0ustar davidstaff00000000000000Metadata-Version: 1.1 Name: jsonpickle Version: 0.6.1 Summary: Python library for serializing any arbitrary object graph into JSON Home-page: http://jsonpickle.github.io/ Author: David Aguilar Author-email: davvid -at- gmail.com License: BSD Description: jsonpickle converts complex Python objects to and from JSON. Keywords: json pickle,json,pickle,marshal,serialization,JavaScript Object Notation Platform: POSIX Platform: Windows Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: JavaScript jsonpickle-0.6.1/Rakefile0000664000501500000240000000276112204557545015550 0ustar davidstaff00000000000000VERSION_FILE = File.open('jsonpickle/version.py').gets.strip eval VERSION_FILE PYTHON_ENVS = [:env26, :env27, :env32, :env33] PYTHON_EXECS = {:env26 => "python2.6", :env27 => "python2.7", :env32 => "python3.2", :env33 => "python3.3"} def colorize(text, color) color_codes = { :black => 30, :red => 31, :green => 32, :yellow => 33, :blue => 34, :magenta => 35, :cyan => 36, :white => 37 } code = color_codes[color] if code == nil text else "\033[#{code}m#{text}\033[0m" end end def virtual_env(command, env="env33") sh "source #{env}/bin/activate ; #{command}" end def create_virtual_env(dir, python) sh "virtualenv #{dir} -p #{python}" end task :clean => [] do sh "rm -rf ~*" sh "rm -rf *.pyc *.pyo" sh "rm -rf data/" sh "rm -rf *.egg-info" sh "rm -rf dist/" end task :install => [] do sh "python --version" sh "ruby --version" sh "easy_install pip" end task :dev_env => [] do PYTHON_ENVS.each { |env| puts colorize("Environment #{env}", :blue) create_virtual_env(env, PYTHON_EXECS[env]) } end task :dependencies => [:dev_env] do PYTHON_ENVS.each { |env| puts colorize("Environment #{env}", :blue) virtual_env("pip install -r requirements.txt", "#{env}") virtual_env("pip install -r requirements-test.txt", "#{env}") } end task :tests => [] do PYTHON_ENVS.each { |env| puts colorize("Environment #{env}", :blue) virtual_env("nosetests", env) } end task :default => [:tests] jsonpickle-0.6.1/README.rst0000664000501500000240000000110412205340201015534 0ustar davidstaff00000000000000jsonpickle ========== jsonpickle is a library for the two-way conversion of complex Python objects and `JSON `_. jsonpickle builds upon the existing JSON encoders, such as simplejson, json, and demjson. For complete documentation, please visit the `jsonpickle homepage `_. Bug reports and merge requests are encouraged at the `jsonpickle repository on github `_. Install ======= :: python setup.py install License ======= Licensed under the BSD License. See COPYING for details. jsonpickle-0.6.1/requirements-2.txt0000664000501500000240000000006112206326015017501 0ustar davidstaff00000000000000feedparser simplejson demjson jsonlib yajl ujson jsonpickle-0.6.1/requirements-3.txt0000664000501500000240000000003412206356101017501 0ustar davidstaff00000000000000feedparser simplejson ujson jsonpickle-0.6.1/requirements-test.txt0000664000501500000240000000001512204557545020332 0ustar davidstaff00000000000000nose coveragejsonpickle-0.6.1/requirements.txt0000664000501500000240000000006112205350474017347 0ustar davidstaff00000000000000feedparser simplejson demjson jsonlib yajl ujson jsonpickle-0.6.1/setup.cfg0000664000501500000240000000007312206357645015717 0ustar davidstaff00000000000000[egg_info] tag_date = 0 tag_svn_revision = 0 tag_build = jsonpickle-0.6.1/setup.py0000664000501500000240000000346012206111224015570 0ustar davidstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # Copyright (C) 2009-2013 David Aguilar (davvid -at- gmail.com) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import os try: import setuptools as setup_mod except ImportError: import distutils.core as setup_mod here = os.path.dirname(__file__) version = os.path.join(here, 'jsonpickle', 'version.py') scope = {} exec(open(version).read(), scope) SETUP_ARGS = dict( name="jsonpickle", version=scope['VERSION'], description="Python library for serializing any " "arbitrary object graph into JSON", long_description = "jsonpickle converts complex Python objects to and " "from JSON.", author="David Aguilar", author_email="davvid -at- gmail.com", url="http://jsonpickle.github.io/", license="BSD", platforms=['POSIX', 'Windows'], keywords=['json pickle', 'json', 'pickle', 'marshal', 'serialization', 'JavaScript Object Notation'], classifiers=[ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Topic :: Software Development :: Libraries :: Python Modules", 'Development Status :: 5 - Production/Stable', "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: JavaScript", ], options={'clean': {'all': 1}}, packages=["jsonpickle"], ) if __name__ == '__main__': setup_mod.setup(**SETUP_ARGS) jsonpickle-0.6.1/tests/0000775000501500000240000000000012206357645015240 5ustar davidstaff00000000000000jsonpickle-0.6.1/tests/backends_tests.py0000664000501500000240000001200312206353615020573 0ustar davidstaff00000000000000import sys import unittest from warnings import warn import jsonpickle from jsonpickle._samples import Thing from jsonpickle.compat import unicode from jsonpickle.compat import PY2 from jsonpickle.compat import PY3 from jsonpickle.compat import PY32 SAMPLE_DATA = {'things': [Thing('data')]} class BackendTestCase(unittest.TestCase): def _is_installed(self, backend): if not jsonpickle.util.is_installed(backend): self.fail('%s not available; please install' % backend) def set_backend(self, *args): backend = args[0] self._is_installed(backend) jsonpickle.load_backend(*args) jsonpickle.set_preferred_backend(backend) def set_preferred_backend(self, backend): self._is_installed(backend) jsonpickle.set_preferred_backend(backend) def tearDown(self): # always reset to default backend jsonpickle.set_preferred_backend('json') def assertEncodeDecode(self, json_input): expect = SAMPLE_DATA actual = jsonpickle.decode(json_input) self.assertEqual(expect['things'][0].name, actual['things'][0].name) self.assertEqual(expect['things'][0].child, actual['things'][0].child) pickled = jsonpickle.encode(SAMPLE_DATA) actual = jsonpickle.decode(pickled) self.assertEqual(expect['things'][0].name, actual['things'][0].name) self.assertEqual(expect['things'][0].child, actual['things'][0].child) class JsonTestCase(BackendTestCase): def setUp(self): self.set_preferred_backend('json') def test_backend(self): expected_pickled = ( '{"things": [{' '"py/object": "jsonpickle._samples.Thing",' ' "name": "data",' ' "child": null}' ']}') self.assertEncodeDecode(expected_pickled) class SimpleJsonTestCase(BackendTestCase): def setUp(self): if PY32: return self.set_preferred_backend('simplejson') def test_backend(self): if PY32: self.skipTest('no simplejson for python3.2') return expected_pickled = ( '{"things": [{' '"py/object": "jsonpickle._samples.Thing",' ' "name": "data",' ' "child": null}' ']}') self.assertEncodeDecode(expected_pickled) def has_module(module): try: __import__(module) except ImportError: warn(module + ' module not available for testing, ' 'consider installing') return False return True class DemjsonTestCase(BackendTestCase): def setUp(self): if PY2: self.set_preferred_backend('demjson') def test_backend(self): if PY3: self.skipTest('no demjson for python3') return expected_pickled = unicode( '{"things":[{' '"child":null,' '"name":"data",' '"py/object":"jsonpickle._samples.Thing"}' ']}') self.assertEncodeDecode(expected_pickled) class JsonlibTestCase(BackendTestCase): def setUp(self): if PY2: self.set_preferred_backend('jsonlib') def test_backend(self): if PY3: self.skipTest('no jsonlib for python3') return expected_pickled = ( '{"things":[{' '"py\/object":"jsonpickle._samples.Thing",' '"name":"data","child":null}' ']}') self.assertEncodeDecode(expected_pickled) class YajlTestCase(BackendTestCase): def setUp(self): if PY2: self.set_preferred_backend('yajl') def test_backend(self): if PY3: self.skipTest('no yajl for python3') return expected_pickled = ( '{"things":[{' '"py/object":"jsonpickle._samples.Thing",' '"name":"data","child":null}' ']}') self.assertEncodeDecode(expected_pickled) class UJsonTestCase(BackendTestCase): def setUp(self): self.set_preferred_backend('ujson') def test_backend(self): expected_pickled = ( '{"things":[{' '"py\/object":"jsonpickle._samples.Thing",' '"name":"data","child":null}' ']}') self.assertEncodeDecode(expected_pickled) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(JsonTestCase)) suite.addTest(unittest.makeSuite(UJsonTestCase)) if not PY32: suite.addTest(unittest.makeSuite(SimpleJsonTestCase)) if PY2: if has_module('demjson'): suite.addTest(unittest.makeSuite(DemjsonTestCase)) if has_module('yajl'): suite.addTest(unittest.makeSuite(YajlTestCase)) if has_module('jsonlib'): suite.addTest(unittest.makeSuite(JsonlibTestCase)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') jsonpickle-0.6.1/tests/benchmark.py0000775000501500000240000000153312105131377017537 0ustar davidstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import sys import timeit IS_25_DOWN = sys.version_info[:2] <= (2, 5) number = 1000 mod = 'json' if IS_25_DOWN: mod = 'simplejson' json = """\ import feedparser import jsonpickle import jsonpickle.tests.thirdparty_tests as test doc = feedparser.parse(test.RSS_DOC) jsonpickle.set_preferred_backend('%s') pickled = jsonpickle.encode(doc) unpickled = jsonpickle.decode(pickled) if doc['feed']['title'] != unpickled['feed']['title']: print 'Not a match' """ % mod print 'Using %s' % mod json_test = timeit.Timer(stmt=json) print "%.9f sec/pass " % (json_test.timeit(number=number) / number) jsonpickle-0.6.1/tests/datetime_tests.py0000664000501500000240000000551412206075546020632 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2013 Jason R. Coombs # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import unittest import datetime import time import jsonpickle from jsonpickle._samples import ObjWithDate # UTC implementation from Python 2.7 docs class UTC(datetime.tzinfo): """UTC""" def utcoffset(self, dt): return datetime.timedelta() def tzname(self, dt): return "UTC" def dst(self, dt): return datetime.timedelta() utc = UTC() class DateTimeTests(unittest.TestCase): def _roundtrip(self, obj): """ pickle and then unpickle object, then assert the new object is the same as the original. """ pickled = jsonpickle.encode(obj) unpickled = jsonpickle.decode(pickled) self.assertEquals(obj, unpickled) def test_datetime(self): """ jsonpickle should pickle a datetime object """ self._roundtrip(datetime.datetime.now()) def test_date(self): """ jsonpickle should pickle a date object """ self._roundtrip(datetime.datetime.today()) def test_time(self): """ jsonpickle should pickle a time object """ self._roundtrip(datetime.datetime.now().time()) def test_timedelta(self): """ jsonpickle should pickle a timedelta object """ self._roundtrip(datetime.timedelta(days=3)) def test_utc(self): """ jsonpickle should be able to encode and decode a datetime with a simple, pickleable UTC tzinfo. """ self._roundtrip(datetime.datetime.utcnow().replace(tzinfo=utc)) def test_unpickleable(self): """ If 'unpickleable' is set on the Pickler, the date objects should be simple, human-readable strings. """ obj = datetime.datetime.now() pickler = jsonpickle.pickler.Pickler(unpicklable=False) flattened = pickler.flatten(obj) self.assertEqual(str(obj), flattened) def test_object_with_datetime(self): test_obj = ObjWithDate() json = jsonpickle.encode(test_obj) test_obj_decoded = jsonpickle.decode(json) self.assertEqual(test_obj_decoded.data['ts'], test_obj_decoded.data_ref['ts']) def test_struct_time(self): expect = time.struct_time([1,2,3,4,5,6,7,8,9]) json = jsonpickle.encode(expect) actual = jsonpickle.decode(json) self.assertEqual(type(actual), time.struct_time) self.assertEqual(expect, actual) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DateTimeTests)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') jsonpickle-0.6.1/tests/document_test.py0000664000501500000240000000257612204557545020500 0ustar davidstaff00000000000000import unittest import jsonpickle from jsonpickle._samples import Document, Section, Question class DocumentTestCase(unittest.TestCase): def test_cyclical(self): """Test that we can pickle cyclical data structure This test is ensures that we can reference objects which first appear within a list (in other words, not a top-level object or attribute). Later children will reference that object through its "_parent" field. This makes sure that we handle this case correctly. """ document = Document('My Document') section1 = Section('Section 1') section2 = Section('Section 2') question1 = Question('Question 1') question2 = Question('Question 2') question3 = Question('Question 3') question4 = Question('Question 4') document.add_child(section1) document.add_child(section2) section1.add_child(question1) section1.add_child(question2) section2.add_child(question3) section2.add_child(question4) pickled = jsonpickle.encode(document) unpickled = jsonpickle.decode(pickled) self.assertEqual(str(document), str(unpickled)) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DocumentTestCase)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') jsonpickle-0.6.1/tests/jsonpickle_test.py0000664000501500000240000007502512206353711021011 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # Copyright (C) 2009, 2011, 2013 David Aguilar # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import collections import datetime import doctest import os import time import unittest import sys import jsonpickle from jsonpickle import handlers from jsonpickle import tags from jsonpickle.compat import unicode from jsonpickle.compat import unichr from jsonpickle.compat import PY32 from jsonpickle._samples import ( BrokenReprThing, DictSubclass, ListSubclass, ListSubclassWithInit, NamedTuple, ObjWithJsonPickleRepr, OldStyleClass, SetSubclass, Thing, ThingWithSlots, ThingWithProps, ) class PicklingTestCase(unittest.TestCase): def setUp(self): self.pickler = jsonpickle.pickler.Pickler() self.unpickler = jsonpickle.unpickler.Unpickler() def test_string(self): self.assertEqual('a string', self.pickler.flatten('a string')) self.assertEqual('a string', self.unpickler.restore('a string')) def test_unicode(self): self.assertEqual(unicode('a string'), self.pickler.flatten('a string')) self.assertEqual(unicode('a string'), self.unpickler.restore('a string')) def test_int(self): self.assertEqual(3, self.pickler.flatten(3)) self.assertEqual(3, self.unpickler.restore(3)) def test_float(self): self.assertEqual(3.5, self.pickler.flatten(3.5)) self.assertEqual(3.5, self.unpickler.restore(3.5)) def test_boolean(self): self.assertTrue(self.pickler.flatten(True)) self.assertFalse(self.pickler.flatten(False)) self.assertTrue(self.unpickler.restore(True)) self.assertFalse(self.unpickler.restore(False)) def test_none(self): self.assertTrue(self.pickler.flatten(None) is None) self.assertTrue(self.unpickler.restore(None) is None) def test_list(self): # multiple types of values listA = [1, 35.0, 'value'] self.assertEqual(listA, self.pickler.flatten(listA)) self.assertEqual(listA, self.unpickler.restore(listA)) # nested list listB = [40, 40, listA, 6] self.assertEqual(listB, self.pickler.flatten(listB)) self.assertEqual(listB, self.unpickler.restore(listB)) # 2D list listC = [[1, 2], [3, 4]] self.assertEqual(listC, self.pickler.flatten(listC)) self.assertEqual(listC, self.unpickler.restore(listC)) # empty list listD = [] self.assertEqual(listD, self.pickler.flatten(listD)) self.assertEqual(listD, self.unpickler.restore(listD)) def test_list_subclass(self): obj = ListSubclass() obj.extend([1, 2, 3]) flattened = self.pickler.flatten(obj) self.assertTrue(tags.OBJECT in flattened) self.assertTrue(tags.SEQ in flattened) self.assertEqual(len(flattened[tags.SEQ]), 3) for v in obj: self.assertTrue(v in flattened[tags.SEQ]) restored = self.unpickler.restore(flattened) self.assertEqual(type(restored), ListSubclass) self.assertEqual(restored, obj) def test_list_subclass_with_data(self): obj = ListSubclass() obj.extend([1, 2, 3]) data = SetSubclass([1, 2, 3]) obj.data = data flattened = self.pickler.flatten(obj) restored = self.unpickler.restore(flattened) self.assertEqual(restored, obj) self.assertEqual(type(restored.data), SetSubclass) self.assertEqual(restored.data, data) def test_set(self): setlist = ['orange', 'apple', 'grape'] setA = set(setlist) flattened = self.pickler.flatten(setA) for s in setlist: self.assertTrue(s in flattened[tags.SET]) setA_pickled = {tags.SET: setlist} self.assertEqual(setA, self.unpickler.restore(setA_pickled)) def test_set_subclass(self): obj = SetSubclass([1, 2, 3]) flattened = self.pickler.flatten(obj) self.assertTrue(tags.OBJECT in flattened) self.assertTrue(tags.SEQ in flattened) self.assertEqual(len(flattened[tags.SEQ]), 3) for v in obj: self.assertTrue(v in flattened[tags.SEQ]) restored = self.unpickler.restore(flattened) self.assertEqual(type(restored), SetSubclass) self.assertEqual(restored, obj) def test_set_subclass_with_data(self): obj = SetSubclass([1, 2, 3]) data = ListSubclass() data.extend([1, 2, 3]) obj.data = data flattened = self.pickler.flatten(obj) restored = self.unpickler.restore(flattened) self.assertEqual(type(restored.data), ListSubclass) self.assertEqual(restored.data, data) def test_dict(self): dictA = {'key1': 1.0, 'key2': 20, 'key3': 'thirty'} self.assertEqual(dictA, self.pickler.flatten(dictA)) self.assertEqual(dictA, self.unpickler.restore(dictA)) dictB = {} self.assertEqual(dictB, self.pickler.flatten(dictB)) self.assertEqual(dictB, self.unpickler.restore(dictB)) def test_tuple(self): # currently all collections are converted to lists tupleA = (4, 16, 32) tupleA_pickled = {tags.TUPLE: [4, 16, 32]} self.assertEqual(tupleA_pickled, self.pickler.flatten(tupleA)) self.assertEqual(tupleA, self.unpickler.restore(tupleA_pickled)) tupleB = (4,) tupleB_pickled = {tags.TUPLE: [4]} self.assertEqual(tupleB_pickled, self.pickler.flatten(tupleB)) self.assertEqual(tupleB, self.unpickler.restore(tupleB_pickled)) def test_tuple_roundtrip(self): data = (1,2,3) newdata = jsonpickle.decode(jsonpickle.encode(data)) self.assertEqual(data, newdata) def test_set_roundtrip(self): data = set([1,2,3]) newdata = jsonpickle.decode(jsonpickle.encode(data)) self.assertEqual(data, newdata) def test_list_roundtrip(self): data = [1,2,3] newdata = jsonpickle.decode(jsonpickle.encode(data)) self.assertEqual(data, newdata) def test_defaultdict_roundtrip(self): """Make sure we can handle collections.defaultdict(list)""" # setup defaultdict = collections.defaultdict(list) defaultdict['a'] = 1 defaultdict['b'].append(2) defaultdict['c'] = collections.defaultdict(dict) # jsonpickle work your magic encoded = jsonpickle.encode(defaultdict) newdefaultdict = jsonpickle.decode(encoded) # jsonpickle never fails self.assertEqual(newdefaultdict['a'], 1) self.assertEqual(newdefaultdict['b'], [2]) self.assertEqual(type(newdefaultdict['c']), collections.defaultdict) self.assertEqual(defaultdict.default_factory, list) self.assertEqual(newdefaultdict.default_factory, list) def test_deque_roundtrip(self): """Make sure we can handle collections.deque""" old_deque = collections.deque([0, 1, 2]) encoded = jsonpickle.encode(old_deque) new_deque = jsonpickle.decode(encoded) self.assertNotEqual(encoded, 'nil') self.assertEqual(old_deque[0], 0) self.assertEqual(new_deque[0], 0) self.assertEqual(old_deque[1], 1) self.assertEqual(new_deque[1], 1) self.assertEqual(old_deque[2], 2) self.assertEqual(new_deque[2], 2) def test_namedtuple_roundtrip(self): old_nt = NamedTuple(0, 1, 2) encoded = jsonpickle.encode(old_nt) new_nt = jsonpickle.decode(encoded) self.assertEqual(type(old_nt), type(new_nt)) self.assertTrue(old_nt is not new_nt) self.assertEqual(old_nt.a, new_nt.a) self.assertEqual(old_nt.b, new_nt.b) self.assertEqual(old_nt.c, new_nt.c) self.assertEqual(old_nt[0], new_nt[0]) self.assertEqual(old_nt[1], new_nt[1]) self.assertEqual(old_nt[2], new_nt[2]) def test_class(self): inst = Thing('test name') inst.child = Thing('child name') flattened = self.pickler.flatten(inst) self.assertEqual('test name', flattened['name']) child = flattened['child'] self.assertEqual('child name', child['name']) inflated = self.unpickler.restore(flattened) self.assertEqual('test name', inflated.name) self.assertTrue(type(inflated) is Thing) self.assertEqual('child name', inflated.child.name) self.assertTrue(type(inflated.child) is Thing) def test_classlist(self): array = [Thing('one'), Thing('two'), 'a string'] flattened = self.pickler.flatten(array) self.assertEqual('one', flattened[0]['name']) self.assertEqual('two', flattened[1]['name']) self.assertEqual('a string', flattened[2]) inflated = self.unpickler.restore(flattened) self.assertEqual('one', inflated[0].name) self.assertTrue(type(inflated[0]) is Thing) self.assertEqual('two', inflated[1].name) self.assertTrue(type(inflated[1]) is Thing) self.assertEqual('a string', inflated[2]) def test_classdict(self): dict = {'k1':Thing('one'), 'k2':Thing('two'), 'k3':3} flattened = self.pickler.flatten(dict) self.assertEqual('one', flattened['k1']['name']) self.assertEqual('two', flattened['k2']['name']) self.assertEqual(3, flattened['k3']) inflated = self.unpickler.restore(flattened) self.assertEqual('one', inflated['k1'].name) self.assertTrue(type(inflated['k1']) is Thing) self.assertEqual('two', inflated['k2'].name) self.assertTrue(type(inflated['k2']) is Thing) self.assertEqual(3, inflated['k3']) #TODO show that non string keys fail def test_recursive(self): """create a recursive structure and test that we can handle it """ parent = Thing('parent') child = Thing('child') child.sibling = Thing('sibling') parent.self = parent parent.child = child parent.child.twin = child parent.child.parent = parent parent.child.sibling.parent = parent cloned = jsonpickle.decode(jsonpickle.encode(parent)) self.assertEqual(parent.name, cloned.name) self.assertEqual(parent.child.name, cloned.child.name) self.assertEqual(parent.child.sibling.name, cloned.child.sibling.name) self.assertEqual(cloned, cloned.child.parent) self.assertEqual(cloned, cloned.child.sibling.parent) self.assertEqual(cloned, cloned.child.twin.parent) self.assertEqual(cloned.child, cloned.child.twin) def test_newstyleslots(self): obj = ThingWithSlots(True, False) jsonstr = jsonpickle.encode(obj) newobj = jsonpickle.decode(jsonstr) self.assertTrue(newobj.a) self.assertFalse(newobj.b) def test_newstyleslots_with_children(self): obj = ThingWithSlots(Thing('a'), Thing('b')) jsonstr = jsonpickle.encode(obj) newobj = jsonpickle.decode(jsonstr) self.assertEqual(newobj.a.name, 'a') self.assertEqual(newobj.b.name, 'b') def test_oldstyleclass(self): obj = OldStyleClass() obj.value = 1234 flattened = self.pickler.flatten(obj) self.assertEqual(1234, flattened['value']) inflated = self.unpickler.restore(flattened) self.assertEqual(1234, inflated.value) def test_struct_time(self): expect = time.struct_time('123456789') flattened = self.pickler.flatten(expect) actual = self.unpickler.restore(flattened) self.assertEqual(expect, actual) def test_dictsubclass(self): obj = DictSubclass() obj['key1'] = 1 flattened = self.pickler.flatten(obj) self.assertEqual({'key1': 1, tags.OBJECT: 'jsonpickle._samples.DictSubclass' }, flattened) self.assertEqual(flattened[tags.OBJECT], 'jsonpickle._samples.DictSubclass') inflated = self.unpickler.restore(flattened) self.assertEqual(1, inflated['key1']) self.assertEqual(inflated.name, 'Test') def test_dictsubclass_notunpickable(self): self.pickler.unpicklable = False obj = DictSubclass() obj['key1'] = 1 flattened = self.pickler.flatten(obj) self.assertEqual(1, flattened['key1']) self.assertFalse(tags.OBJECT in flattened) inflated = self.unpickler.restore(flattened) self.assertEqual(1, inflated['key1']) def test_tuple_notunpicklable(self): self.pickler.unpicklable = False flattened = self.pickler.flatten(('one', 2, 3)) self.assertEqual(flattened, ['one', 2, 3]) def test_set_not_unpicklable(self): self.pickler.unpicklable = False flattened = self.pickler.flatten(set(['one', 2, 3])) self.assertTrue('one' in flattened) self.assertTrue(2 in flattened) self.assertTrue(3 in flattened) self.assertTrue(isinstance(flattened, list)) def test_datetime(self): obj = datetime.datetime.now() flattened = self.pickler.flatten(obj) self.assertTrue(tags.OBJECT in flattened) self.assertTrue('__reduce__' in flattened) inflated = self.unpickler.restore(flattened) self.assertEqual(obj, inflated) def test_datetime_inside_int_keys_defaults(self): t = datetime.time(hour=10) s = jsonpickle.encode({1:t, 2:t}) d = jsonpickle.decode(s) self.assertEqual(d["1"], d["2"]) self.assertTrue(d["1"] is d["2"]) self.assertTrue(isinstance(d["1"], datetime.time)) def test_datetime_inside_int_keys_with_keys_enabled(self): t = datetime.time(hour=10) s = jsonpickle.encode({1:t, 2:t}, keys=True) d = jsonpickle.decode(s, keys=True) self.assertEqual(d[1], d[2]) self.assertTrue(d[1] is d[2]) self.assertTrue(isinstance(d[1], datetime.time)) def test_broken_repr_dict_key(self): """Tests that we can pickle dictionaries with keys that have broken __repr__ implementations. """ br = BrokenReprThing('test') obj = { br: True } pickler = jsonpickle.pickler.Pickler() flattened = pickler.flatten(obj) self.assertTrue('' in flattened) self.assertTrue(flattened['']) def test_repr_not_unpickable(self): obj = datetime.datetime.now() pickler = jsonpickle.pickler.Pickler(unpicklable=False) flattened = pickler.flatten(obj) self.assertFalse(tags.REPR in flattened) self.assertFalse(tags.OBJECT in flattened) self.assertEqual(str(obj), flattened) def test_thing_with_module(self): obj = Thing('with-module') obj.themodule = os flattened = self.pickler.flatten(obj) inflated = self.unpickler.restore(flattened) self.assertEqual(inflated.themodule, os) def test_thing_with_submodule(self): from distutils import sysconfig obj = Thing('with-submodule') obj.submodule = sysconfig flattened = self.pickler.flatten(obj) inflated = self.unpickler.restore(flattened) self.assertEqual(inflated.submodule, sysconfig) def test_type_reference(self): """This test ensures that users can store references to types. """ obj = Thing('object-with-type-reference') # reference the built-in 'object' type obj.typeref = object flattened = self.pickler.flatten(obj) self.assertEqual(flattened['typeref'], { tags.TYPE: '__builtin__.object', }) inflated = self.unpickler.restore(flattened) self.assertEqual(inflated.typeref, object) def test_class_reference(self): """This test ensures that users can store references to classes. """ obj = Thing('object-with-class-reference') # reference the 'Thing' class (not an instance of the class) obj.classref = Thing flattened = self.pickler.flatten(obj) self.assertEqual(flattened['classref'], { tags.TYPE: 'jsonpickle._samples.Thing', }) inflated = self.unpickler.restore(flattened) self.assertEqual(inflated.classref, Thing) def test_supports_getstate_setstate(self): obj = ThingWithProps('object-which-defines-getstate-setstate') flattened = self.pickler.flatten(obj) self.assertTrue(flattened[tags.STATE].get('__identity__')) self.assertTrue(flattened[tags.STATE].get('nom')) inflated = self.unpickler.restore(flattened) self.assertEqual(obj, inflated) def test_references(self): obj_a = Thing('foo') obj_b = Thing('bar') coll = [obj_a, obj_b, obj_b] flattened = self.pickler.flatten(coll) inflated = self.unpickler.restore(flattened) self.assertEqual(len(inflated), len(coll)) for x in range(len(coll)): self.assertEqual(repr(coll[x]), repr(inflated[x])) def test_list_subclass_with_init(self): obj = ListSubclassWithInit('foo') self.assertEqual(obj.attr, 'foo') flattened = self.pickler.flatten(obj) inflated = self.unpickler.restore(flattened) self.assertEqual(type(inflated), ListSubclassWithInit) def test_builtin_error(self): expect = AssertionError json = jsonpickle.encode(expect) actual = jsonpickle.decode(json) self.assertEqual(expect, actual) self.assertTrue(expect is actual) class JSONPickleTestCase(unittest.TestCase): def setUp(self): self.obj = Thing('A name') self.expected_json = ( '{"'+tags.OBJECT+'": "jsonpickle._samples.Thing",' ' "name": "A name", "child": null}') def test_encode(self): expect = self.obj pickled = jsonpickle.encode(self.obj) actual = jsonpickle.decode(pickled) self.assertEqual(expect.name, actual.name) self.assertEqual(expect.child, actual.child) def test_encode_notunpicklable(self): expect = {'name': 'A name', 'child': None} pickled = jsonpickle.encode(self.obj, unpicklable=False) actual = jsonpickle.decode(pickled) self.assertEqual(expect['name'], actual['name']) def test_decode(self): unpickled = jsonpickle.decode(self.expected_json) self.assertEqual(self.obj.name, unpickled.name) self.assertEqual(type(self.obj), type(unpickled)) def test_json(self): expect = self.obj pickled = jsonpickle.encode(self.obj) actual = jsonpickle.decode(pickled) self.assertEqual(actual.name, expect.name) self.assertEqual(actual.child, expect.child) unpickled = jsonpickle.decode(self.expected_json) self.assertEqual(self.obj.name, unpickled.name) self.assertEqual(type(self.obj), type(unpickled)) def test_unicode_dict_keys(self): uni = unichr(0x1234) pickled = jsonpickle.encode({uni: uni}) unpickled = jsonpickle.decode(pickled) self.assertTrue(uni in unpickled) self.assertEqual(unpickled[uni], uni) def test_tuple_dict_keys_default(self): """Test that we handle dictionaries with tuples as keys.""" tuple_dict = {(1, 2): 3, (4, 5): { (7, 8): 9 }} pickled = jsonpickle.encode(tuple_dict) expect = {'(1, 2)': 3, '(4, 5)': {'(7, 8)': 9}} actual = jsonpickle.decode(pickled) self.assertEqual(expect, actual) tuple_dict = {(1, 2): [1, 2]} pickled = jsonpickle.encode(tuple_dict) unpickled = jsonpickle.decode(pickled) self.assertEqual(unpickled['(1, 2)'], [1, 2]) def test_tuple_dict_keys_with_keys_enabled(self): """Test that we handle dictionaries with tuples as keys.""" tuple_dict = {(1, 2): 3, (4, 5): { (7, 8): 9 }} pickled = jsonpickle.encode(tuple_dict, keys=True) expect = tuple_dict actual = jsonpickle.decode(pickled, keys=True) self.assertEqual(expect, actual) tuple_dict = {(1, 2): [1, 2]} pickled = jsonpickle.encode(tuple_dict, keys=True) unpickled = jsonpickle.decode(pickled, keys=True) self.assertEqual(unpickled[(1, 2)], [1, 2]) def test_datetime_dict_keys_defaults(self): """Test that we handle datetime objects as keys.""" datetime_dict = {datetime.datetime(2008, 12, 31): True} pickled = jsonpickle.encode(datetime_dict) expect = {'datetime.datetime(2008, 12, 31, 0, 0)': True} actual = jsonpickle.decode(pickled) self.assertEqual(expect, actual) def test_datetime_dict_keys_with_keys_enabled(self): """Test that we handle datetime objects as keys.""" datetime_dict = {datetime.datetime(2008, 12, 31): True} pickled = jsonpickle.encode(datetime_dict, keys=True) expect = datetime_dict actual = jsonpickle.decode(pickled, keys=True) self.assertEqual(expect, actual) def test_object_dict_keys(self): """Test that we handle random objects as keys. """ thing = Thing('random') pickled = jsonpickle.encode({thing: True}) unpickled = jsonpickle.decode(pickled) self.assertEqual(unpickled, {unicode('Thing("random")'): True}) def test_int_dict_keys_defaults(self): int_dict = {1000: [1, 2]} pickled = jsonpickle.encode(int_dict) unpickled = jsonpickle.decode(pickled) self.assertEqual(unpickled['1000'], [1, 2]) def test_int_dict_keys_with_keys_enabled(self): int_dict = {1000: [1, 2]} pickled = jsonpickle.encode(int_dict, keys=True) unpickled = jsonpickle.decode(pickled, keys=True) self.assertEqual(unpickled[1000], [1, 2]) def test_list_of_objects(self): """Test that objects in lists are referenced correctly""" a = Thing('a') b = Thing('b') pickled = jsonpickle.encode([a, b, b]) unpickled = jsonpickle.decode(pickled) self.assertEqual(unpickled[1], unpickled[2]) self.assertEqual(type(unpickled[0]), Thing) self.assertEqual(unpickled[0].name, 'a') self.assertEqual(unpickled[1].name, 'b') self.assertEqual(unpickled[2].name, 'b') def test_refs_keys_values(self): """Test that objects in dict keys are referenced correctly """ j = Thing('random') object_dict = {j: j} pickled = jsonpickle.encode(object_dict, keys=True) unpickled = jsonpickle.decode(pickled, keys=True) self.assertEqual(list(unpickled.keys()), list(unpickled.values())) def test_object_keys_to_list(self): """Test that objects in dict values are referenced correctly """ j = Thing('random') object_dict = {j: [j, j]} pickled = jsonpickle.encode(object_dict, keys=True) unpickled = jsonpickle.decode(pickled, keys=True) obj = list(unpickled.keys())[0] self.assertEqual(j.name, obj.name) self.assertTrue(obj is unpickled[obj][0]) self.assertTrue(obj is unpickled[obj][1]) def test_refs_in_objects(self): """Test that objects in lists are referenced correctly""" a = Thing('a') b = Thing('b') pickled = jsonpickle.encode([a, b, b]) unpickled = jsonpickle.decode(pickled) self.assertNotEqual(unpickled[0], unpickled[1]) self.assertEqual(unpickled[1], unpickled[2]) self.assertTrue(unpickled[1] is unpickled[2]) def test_refs_recursive(self): """Test that complicated recursive refs work""" a = Thing('a') a.self_list = [Thing('0'), Thing('1'), Thing('2')] a.first = a.self_list[0] a.stuff = {a.first: a.first} a.morestuff = {a.self_list[1]: a.stuff} pickle = jsonpickle.encode(a, keys=True) b = jsonpickle.decode(pickle, keys=True) item = b.self_list[0] self.assertEqual(b.first, item) self.assertEqual(b.stuff[b.first], item) self.assertEqual(b.morestuff[b.self_list[1]][b.first], item) def test_load_backend(self): """Test that we can call jsonpickle.load_backend() """ if PY32: self.skipTest('no simplejson for python 3.2') return jsonpickle.load_backend('simplejson', 'dumps', 'loads', ValueError) self.assertTrue(True) def test_set_preferred_backend_allows_magic(self): """Tests that we can use the pluggable backends magically """ backend = 'os.path' jsonpickle.load_backend(backend, 'split', 'join', AttributeError) jsonpickle.set_preferred_backend(backend) slash_hello, world = jsonpickle.encode('/hello/world') jsonpickle.remove_backend(backend) self.assertEqual(slash_hello, '/hello') self.assertEqual(world, 'world') def test_load_backend_submodule(self): """Test that we can load a submodule as a backend """ jsonpickle.load_backend('os.path', 'split', 'join', AttributeError) self.assertTrue('os.path' in jsonpickle.json._backend_names and 'os.path' in jsonpickle.json._encoders and 'os.path' in jsonpickle.json._decoders and 'os.path' in jsonpickle.json._encoder_options and 'os.path' in jsonpickle.json._decoder_exceptions) def _backend_is_partially_loaded(self, backend): """Return True if the specified backend is incomplete""" return (backend in jsonpickle.json._backend_names or backend in jsonpickle.json._encoders or backend in jsonpickle.json._decoders or backend in jsonpickle.json._encoder_options or backend in jsonpickle.json._decoder_exceptions) def test_load_backend_skips_bad_encode(self): """Test that we ignore bad encoders""" jsonpickle.load_backend('os.path', 'bad!', 'split', AttributeError) self.failIf(self._backend_is_partially_loaded('os.path')) def test_load_backend_skips_bad_decode(self): """Test that we ignore bad decoders""" jsonpickle.load_backend('os.path', 'join', 'bad!', AttributeError) self.failIf(self._backend_is_partially_loaded('os.path')) def test_load_backend_skips_bad_decoder_exceptions(self): """Test that we ignore bad decoder exceptions""" jsonpickle.load_backend('os.path', 'join', 'split', 'bad!') self.failIf(self._backend_is_partially_loaded('os.path')) def test_list_item_reference(self): thing = Thing('parent') thing.child = Thing('child') thing.child.refs = [thing] encoded = jsonpickle.encode(thing) decoded = jsonpickle.decode(encoded) self.assertEqual(id(decoded.child.refs[0]), id(decoded)) def test_reference_to_list(self): thing = Thing('parent') thing.a = [1] thing.b = thing.a thing.b.append(thing.a) thing.b.append([thing.a]) encoded = jsonpickle.encode(thing) decoded = jsonpickle.decode(encoded) self.assertEqual(decoded.a[0], 1) self.assertEqual(decoded.b[0], 1) self.assertEqual(id(decoded.a), id(decoded.b)) self.assertEqual(id(decoded.a), id(decoded.a[1])) self.assertEqual(id(decoded.a), id(decoded.a[2][0])) def test_repr_using_jsonpickle(self): thing = ObjWithJsonPickleRepr() thing.child = ObjWithJsonPickleRepr() thing.child.parent = thing encoded = jsonpickle.encode(thing) decoded = jsonpickle.decode(encoded) self.assertEqual(id(decoded), id(decoded.child.parent)) def test_ordered_dict(self): if sys.version_info < (2, 7): return d = collections.OrderedDict() d.update(c=3) d.update(a=1) d.update(b=2) encoded = jsonpickle.encode(d) decoded = jsonpickle.decode(encoded) self.assertEqual(d, decoded) def test_make_refs_disabled_list(self): obj_a = Thing('foo') obj_b = Thing('bar') coll = [obj_a, obj_b, obj_b] encoded = jsonpickle.encode(coll, make_refs=False) decoded = jsonpickle.decode(encoded) self.assertEqual(len(decoded), 3) self.assertTrue(decoded[0] is not decoded[1]) self.assertTrue(decoded[1] is not decoded[2]) def test_make_refs_disabled_reference_to_list(self): thing = Thing('parent') thing.a = [1] thing.b = thing.a thing.b.append(thing.a) thing.b.append([thing.a]) encoded = jsonpickle.encode(thing, make_refs=False) decoded = jsonpickle.decode(encoded) self.assertEqual(decoded.a[0], 1) self.assertEqual(decoded.b[0:3], '[1,') self.assertEqual(decoded.a[1][0:3], '[1,') self.assertEqual(decoded.a[2][0][0:3], '[1,') # Test classes for ExternalHandlerTestCase class Mixin(object): def ok(self): return True class UnicodeMixin(unicode, Mixin): def __add__(self, rhs): obj = super(UnicodeMixin, self).__add__(rhs) return UnicodeMixin(obj) class UnicodeMixinHandler(handlers.BaseHandler): def flatten(self, obj, data): data['value'] = obj return data def restore(self, obj): return UnicodeMixin(obj['value']) handlers.register(UnicodeMixin, UnicodeMixinHandler) class ExternalHandlerTestCase(unittest.TestCase): def test_unicode_mixin(self): obj = UnicodeMixin('test') self.assertEqual(type(obj), UnicodeMixin) self.assertEqual(unicode(obj), unicode('test')) # Encode into JSON content = jsonpickle.encode(obj) # Resurrect from JSON new_obj = jsonpickle.decode(content) new_obj += ' passed' self.assertEqual(unicode(new_obj), unicode('test passed')) self.assertEqual(type(new_obj), UnicodeMixin) self.assertTrue(new_obj.ok()) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(PicklingTestCase)) suite.addTest(unittest.makeSuite(JSONPickleTestCase)) suite.addTest(unittest.makeSuite(ExternalHandlerTestCase)) suite.addTest(doctest.DocTestSuite(jsonpickle.pickler)) suite.addTest(doctest.DocTestSuite(jsonpickle.unpickler)) suite.addTest(doctest.DocTestSuite(jsonpickle)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') jsonpickle-0.6.1/tests/runtests.py0000775000501500000240000000204412204574047017477 0ustar davidstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import os import sys testdir = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(1, os.path.dirname(testdir)) import unittest import util_tests import jsonpickle_test import thirdparty_tests import backends_tests import document_test import datetime_tests def suite(): suite = unittest.TestSuite() suite.addTest(util_tests.suite()) suite.addTest(util_tests.suite()) suite.addTest(jsonpickle_test.suite()) suite.addTest(document_test.suite()) suite.addTest(thirdparty_tests.suite()) suite.addTest(backends_tests.suite()) suite.addTest(datetime_tests.suite()) return suite def main(): #unittest.main(defaultTest='suite') return unittest.TextTestRunner(verbosity=2).run(suite()) if __name__ == '__main__': sys.exit(not main().wasSuccessful()) jsonpickle-0.6.1/tests/test_handlers.py0000664000501500000240000000271412204557545020454 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2013 Jason R. Coombs # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import unittest import jsonpickle class CustomObject(object): "A class to be serialized by a custom handler" def __eq__(self, other): return True class NullHandler(jsonpickle.handlers.BaseHandler): _handles = CustomObject, def flatten(self, obj, data): return data def restore(self, obj): return CustomObject() class HandlerTests(unittest.TestCase): def roundtrip(self, ob): encoded = jsonpickle.encode(ob) decoded = jsonpickle.decode(encoded) self.assertEqual(decoded, ob) return decoded def test_references(self): """ Ensure objects handled by a custom handler are properly dereferenced. """ ob = CustomObject() # create a dictionary which contains several references to ob subject = dict(a=ob, b=ob, c=ob) # ensure the subject can be roundtripped new_subject = self.roundtrip(subject) assert new_subject['a'] is new_subject['b'] assert new_subject['b'] is new_subject['c'] def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(HandlerTests, 'test_references')) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') jsonpickle-0.6.1/tests/thirdparty_tests.py0000664000501500000240000000547712206347176021241 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import unittest import jsonpickle RSS_DOC = """ Sample Feed For documentation <em>only</em> <p>Copyright 2005, Mark Pilgrim</p>< Sample Toolkit tag:feedparser.org,2005-11-09:/docs/examples/atom10.xml 2005-11-09T11:56:34Z First entry title tag:feedparser.org,2005-11-09:/docs/examples/atom10.xml:3 2005-11-09T00:23:47Z 2005-11-09T11:56:34Z Mark Pilgrim http://diveintomark.org/ mark@example.org Joe http://example.org/joe/ joe@example.org Sam http://example.org/sam/ sam@example.org Watch out for nasty tricks
Watch out for nasty tricks
""" class FeedParserTest(unittest.TestCase): def setUp(self): try: import feedparser except ImportError as e: self.fail("feedparser module not available, please install") self.doc = feedparser.parse(RSS_DOC) def test(self): pickled = jsonpickle.encode(self.doc) unpickled = jsonpickle.decode(pickled) self.assertEquals(self.doc['feed']['title'], unpickled['feed']['title']) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(FeedParserTest, 'test')) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') jsonpickle-0.6.1/tests/util_tests.py0000664000501500000240000001216712206075547020016 0ustar davidstaff00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008 John Paulett (john -at- paulett.org) # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. import unittest import doctest import time import jsonpickle.util from jsonpickle.compat import unicode from jsonpickle.compat import long from jsonpickle.util import is_dictionary from jsonpickle.util import is_dictionary_subclass from jsonpickle.util import is_function from jsonpickle.util import is_list from jsonpickle.util import is_noncomplex from jsonpickle.util import is_primitive from jsonpickle.util import is_set from jsonpickle.util import is_sequence from jsonpickle.util import is_sequence_subclass from jsonpickle.util import is_tuple from jsonpickle._samples import Thing, ListSubclass, DictSubclass class UtilTestCase(unittest.TestCase): def test_is_primitive_int(self): self.assertTrue(is_primitive(0)) self.assertTrue(is_primitive(3)) self.assertTrue(is_primitive(-3)) def test_is_primitive_float(self): self.assertTrue(is_primitive(0)) self.assertTrue(is_primitive(3.5)) self.assertTrue(is_primitive(-3.5)) self.assertTrue(is_primitive(float(3))) def test_is_primitive_long(self): self.assertTrue(is_primitive(long(3))) def test_is_primitive_bool(self): self.assertTrue(is_primitive(True)) self.assertTrue(is_primitive(False)) def test_is_primitive_None(self): self.assertTrue(is_primitive(None)) def test_is_primitive_str(self): self.assertTrue(is_primitive('hello')) self.assertTrue(is_primitive('')) def test_is_primitive_unicode(self): self.assertTrue(is_primitive(unicode('hello'))) self.assertTrue(is_primitive(unicode(''))) self.assertTrue(is_primitive(unicode('hello'))) def test_is_primitive_list(self): self.assertFalse(is_primitive([])) self.assertFalse(is_primitive([4, 4])) def test_is_primitive_dict(self): self.assertFalse(is_primitive({'key':'value'})) self.assertFalse(is_primitive({})) def test_is_primitive_tuple(self): self.assertFalse(is_primitive((1, 3))) self.assertFalse(is_primitive((1,))) def test_is_primitive_set(self): self.assertFalse(is_primitive(set([1, 3]))) def test_is_primitive_object(self): self.assertFalse(is_primitive(Thing('test'))) def test_is_list_list(self): self.assertTrue(is_list([1, 2])) def test_is_list_set(self): self.assertTrue(is_set(set([1, 2]))) def test_is_list_tuple(self): self.assertTrue(is_tuple((1, 2))) def test_is_list_dict(self): self.assertFalse(is_list({'key':'value'})) self.assertFalse(is_set({'key':'value'})) self.assertFalse(is_tuple({'key':'value'})) def test_is_list_other(self): self.assertFalse(is_list(1)) self.assertFalse(is_set(1)) self.assertFalse(is_tuple(1)) def test_is_sequence_various(self): self.assertTrue(is_sequence([])) self.assertTrue(is_sequence(tuple())) self.assertTrue(is_sequence(set())) def test_is_dictionary_dict(self): self.assertTrue(is_dictionary({})) def test_is_dicitonary_sequences(self): self.assertFalse(is_dictionary([])) self.assertFalse(is_dictionary(set())) def test_is_dictionary_tuple(self): self.assertFalse(is_dictionary(tuple())) def test_is_dictionary_primitive(self): self.assertFalse(is_dictionary(int())) self.assertFalse(is_dictionary(None)) self.assertFalse(is_dictionary(str())) def test_is_dictionary_subclass_dict(self): self.assertFalse(is_dictionary_subclass({})) def test_is_dictionary_subclass_subclass(self): self.assertTrue(is_dictionary_subclass(DictSubclass())) def test_is_sequence_subclass_subclass(self): self.assertTrue(is_sequence_subclass(ListSubclass())) def test_is_sequence_subclass_list(self): self.assertFalse(is_sequence_subclass([])) def test_is_noncomplex_time_struct(self): t = time.struct_time('123456789') self.assertTrue(is_noncomplex(t)) def test_is_noncomplex_other(self): self.assertFalse(is_noncomplex('a')) def test_is_function_builtins(self): self.assertTrue(is_function(globals)) def test_is_function_lambda(self): self.assertTrue(is_function(lambda: False)) def test_is_function_instance_method(self): class Foo(object): def method(self): pass @staticmethod def staticmethod(): pass @classmethod def classmethod(cls): pass f = Foo() self.assertTrue(is_function(f.method)) self.assertTrue(is_function(f.staticmethod)) self.assertTrue(is_function(f.classmethod)) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(UtilTestCase)) suite.addTest(doctest.DocTestSuite(jsonpickle.util)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite')