summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2024-04-30 10:54:43 +0100
committerColin Watson <cjwatson@debian.org>2024-04-30 10:54:43 +0100
commitfeed4abac8c218ea828bb60e5595a5512294c271 (patch)
treee1c806ed3b8fdf272f11183f167b37063ab5a5ff
parent5ed72213c2a573c62dddb0dd4e99571b8cfc3c08 (diff)
New upstream version 3.0.4+dfsg
-rw-r--r--.gitignore1
-rw-r--r--CHANGES.rst17
-rw-r--r--Makefile4
-rw-r--r--PKG-INFO158
-rw-r--r--README.rst38
-rw-r--r--contrib/pyqt-reduce-handler.py4
-rw-r--r--docs/conf.py13
-rw-r--r--garden.yaml61
-rw-r--r--jsonpickle.egg-info/PKG-INFO158
-rw-r--r--jsonpickle.egg-info/SOURCES.txt3
-rw-r--r--jsonpickle.egg-info/requires.txt16
-rw-r--r--jsonpickle/__init__.py2
-rw-r--r--jsonpickle/backend.py2
-rw-r--r--jsonpickle/compat.py2
-rw-r--r--jsonpickle/ext/numpy.py2
-rw-r--r--jsonpickle/ext/pandas.py2
-rw-r--r--jsonpickle/handlers.py2
-rw-r--r--jsonpickle/pickler.py65
-rw-r--r--jsonpickle/tags.py2
-rw-r--r--jsonpickle/unpickler.py105
-rw-r--r--jsonpickle/util.py16
-rw-r--r--requirements-dev.txt20
-rw-r--r--setup.cfg18
-rw-r--r--setup.py6
-rw-r--r--tests/backend_test.py10
-rw-r--r--tests/collections_test.py2
-rw-r--r--tests/datetime_test.py4
-rw-r--r--tests/ecdsa_test.py2
-rw-r--r--tests/jsonpickle_test.py35
-rw-r--r--tests/numpy_test.py2
-rw-r--r--tests/object_test.py2
-rw-r--r--tests/pandas_test.py2
-rw-r--r--tests/sklearn_test.py2
-rw-r--r--tests/sqlalchemy_test.py8
-rw-r--r--tests/util_test.py3
-rw-r--r--tests/wizard_test.py2
-rw-r--r--tests/zoneinfo_test.py2
-rw-r--r--tox.ini4
38 files changed, 303 insertions, 494 deletions
diff --git a/.gitignore b/.gitignore
index 2f2f7d8..b94e4b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
/env*
/htmlcov
/tags
+/pages
/.pytest_cache
/.tox
/.idea
diff --git a/CHANGES.rst b/CHANGES.rst
index 5fdf89c..a9d69a1 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,13 @@
+v3.0.4
+======
+ * Fixed an issue with django.SafeString and other classes inheriting from
+ str having read-only attribute errors (#478) (+481)
+ * The test suite was made compatible with `pytest-ruff>=0.3.0`. (+482)
+ * A `garden.yaml` file was added for use with the
+ `garden <https://crates.io/crates/garden-tools>_` command runner. (+486)
+ * The test suite was updated to avoid deprecated SQLALchemy APIs.
+ * The `jaraco.packaging.sphinx` documentation dependency was removed.
+
v3.0.3
======
* Compatibilty with Pandas and Cython 3.0 was added. (#460) (+477)
@@ -5,7 +15,8 @@ v3.0.3
could return a ``None`` module. (#447)
* Fixed a bug where unpickling a missing class would return a different object
instead of ``None``. (+471)
- * Fixed the handling of missing classes when setting ``on_missing`` to ``warn`` or ``error``. (+471)
+ * Fixed the handling of missing classes when setting ``on_missing`` to ``warn``
+ or ``error``. (+471)
* The test suite was made compatible with Python 3.12.
* The tox configuration was updated to generate code coverage reports.
* The suite now uses ``ruff`` to validate python code.
@@ -336,7 +347,7 @@ v0.7.1
======
* Added support for Python 3.4.
- * Added support for :class:`posix.stat_result`.
+ * Added support for `posix.stat_result`.
v0.7.0
======
@@ -365,7 +376,7 @@ v0.6.0
* Python 3 support!
* :class:`time.struct_time` is now serialized using the built-in
- :class:`jsonpickle.handlers.SimpleReduceHandler`.
+ `jsonpickle.handlers.SimpleReduceHandler`.
v0.5.0
======
diff --git a/Makefile b/Makefile
index 5aa41c3..8115dce 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ JOB_COUNT := $(shell printf '%s' "$(JOB_FLAGS)" | sed -e 's/-j//')
DASH_J := $(shell echo -- $(JOB_FLAGS) -j$(nproc) | grep -o -e '-j[0-9]\+' | head -n 1)
NUM_JOBS := $(shell printf %s "$(DASH_J)" | sed -e 's/-j//')
-TESTCMD ?= $(PYTEST) -p no:cacheprovier
+TESTCMD ?= $(PYTEST)
BENCHMARKCMD ?= $(BENCHMARK)
TOXCMD ?= $(TOX) run-parallel --parallel-live
ifdef V
@@ -68,7 +68,7 @@ help::
.PHONY: help
test::
- $(TESTCMD) $(flags)
+ $(TESTCMD) jsonpickle tests $(flags)
.PHONY: test
tox::
diff --git a/PKG-INFO b/PKG-INFO
index cfaca35..bed22d5 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,7 +1,7 @@
Metadata-Version: 2.1
Name: jsonpickle
-Version: 3.0.3
-Summary: Python library for serializing any arbitrary object graph into JSON
+Version: 3.0.4
+Summary: Serialize any Python object to JSON
Home-page: https://github.com/jsonpickle/jsonpickle
Author: David Aguilar
Author-email: davvid@gmail.com
@@ -18,162 +18,14 @@ Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: JavaScript
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Provides-Extra: docs
+Provides-Extra: packaging
Provides-Extra: testing
License-File: LICENSE
-.. image:: https://img.shields.io/pypi/v/jsonpickle.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/jsonpickle.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/jsonpickle
-
-.. image:: https://readthedocs.org/projects/jsonpickle/badge/?version=latest
- :target: https://jsonpickle.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://github.com/jsonpickle/jsonpickle/actions/workflows/test.yml/badge.svg
- :target: https://github.com/jsonpickle/jsonpickle/actions
- :alt: Github Actions
-
-.. image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
- :target: https://github.com/jsonpickle/jsonpickle/blob/main/COPYING
- :alt: BSD
-
-
-jsonpickle
-==========
-jsonpickle is a library for the two-way conversion of complex Python objects
-and `JSON <http://json.org/>`_. jsonpickle builds upon the existing JSON
-encoders, such as simplejson, json, and ujson.
-
-.. warning::
-
- jsonpickle can execute arbitrary Python code.
-
- Please see the Security section for more details.
-
-
-For complete documentation, please visit the
-`jsonpickle documentation <http://jsonpickle.readthedocs.io/>`_.
-
-Bug reports and merge requests are encouraged at the
-`jsonpickle repository on github <https://github.com/jsonpickle/jsonpickle>`_.
-
-Why jsonpickle?
-===============
-Data serialized with python's pickle (or cPickle or dill) is not easily readable outside of python. Using the json format, jsonpickle allows simple data types to be stored in a human-readable format, and more complex data types such as numpy arrays and pandas dataframes, to be machine-readable on any platform that supports json. E.g., unlike pickled data, jsonpickled data stored in an Amazon S3 bucket is indexible by Amazon's Athena.
-
-Security
-========
-
-jsonpickle should be treated the same as the
-`Python stdlib pickle module <https://docs.python.org/3/library/pickle.html>`_
-from a security perspective.
-
-.. warning::
-
- The jsonpickle module **is not secure**. Only unpickle data you trust.
-
- It is possible to construct malicious pickle data which will **execute
- arbitrary code during unpickling**. Never unpickle data that could have come
- from an untrusted source, or that could have been tampered with.
-
- Consider signing data with an HMAC if you need to ensure that it has not
- been tampered with.
-
- Safer deserialization approaches, such as reading JSON directly,
- may be more appropriate if you are processing untrusted data.
-
-
-Install
-=======
-
-Install from pip for the latest stable release:
-
-::
-
- pip install jsonpickle
-
-Install from github for the latest changes:
-
-::
-
- pip install git+https://github.com/jsonpickle/jsonpickle.git
-
-If you have the files checked out for development:
-
-::
-
- git clone https://github.com/jsonpickle/jsonpickle.git
- cd jsonpickle
- python setup.py develop
-
-
-Numpy Support
-=============
-jsonpickle includes a built-in numpy extension. If would like to encode
-sklearn models, numpy arrays, and other numpy-based data then you must
-enable the numpy extension by registering its handlers::
-
- >>> import jsonpickle.ext.numpy as jsonpickle_numpy
- >>> jsonpickle_numpy.register_handlers()
-
-Pandas Support
-==============
-jsonpickle includes a built-in pandas extension. If would like to encode
-pandas DataFrame or Series objects then you must enable the pandas extension
-by registering its handlers::
-
- >>> import jsonpickle.ext.pandas as jsonpickle_pandas
- >>> jsonpickle_pandas.register_handlers()
-
-Development
-===========
-
-Use `make` to run the unit tests::
-
- make test
-
-`pytest` is used to run unit tests internally.
-
-A `tox` target is provided to run tests using all installed and supported Python versions::
-
- make tox
-
-`jsonpickle` itself has no dependencies beyond the Python stdlib.
-`tox` is required for testing when using the `tox` test runner only.
-
-The testing requirements are specified in `requirements-dev.txt`.
-It is recommended to create a virtualenv and run tests from within the
-virtualenv, or use a tool such as `vx <https://github.com/davvid/vx/>`_
-to activate the virtualenv without polluting the shell environment::
-
- python3 -mvenv env3x
- vx env3x pip install --requirement requirements-dev.txt
- vx env3x make test
-
-`jsonpickle` supports multiple Python versions, so using a combination of
-multiple virtualenvs and `tox` is useful in order to catch compatibility
-issues when developing.
-
-GPG Signing
-===========
-Releases before v3.0.0 are signed with `davvid's key <https://keys.openpgp.org/vks/v1/by-fingerprint/FA41BF59C1B48E8C5F3DA61C8CE26BF4A9F606B0>`_. v3.0.0 and after are likely signed by `Theelx's key <https://github.com/Theelx.gpg>`_. All upcoming releases should be signed by one of these two keys, usually Theelx's key.
-
-jsonpickleJS
-============
-`jsonpickleJS <https://github.com/cuthbertLab/jsonpickleJS>`_
-is a javascript implementation of jsonpickle by Michael Scott Cuthbert.
-jsonpickleJS can be extremely useful for projects that have parallel data
-structures between Python and Javascript.
-
-License
-=======
-Licensed under the BSD License. See COPYING for details.
-See jsonpickleJS/LICENSE for details about the jsonpickleJS license.
+Python library for serializing arbitrary object graphs into JSON
diff --git a/README.rst b/README.rst
index 88c838f..735fd36 100644
--- a/README.rst
+++ b/README.rst
@@ -21,7 +21,7 @@
jsonpickle
==========
jsonpickle is a library for the two-way conversion of complex Python objects
-and `JSON <http://json.org/>`_. jsonpickle builds upon the existing JSON
+and `JSON <http://json.org/>`_. jsonpickle builds upon existing JSON
encoders, such as simplejson, json, and ujson.
.. warning::
@@ -78,31 +78,17 @@ Install from github for the latest changes:
pip install git+https://github.com/jsonpickle/jsonpickle.git
-If you have the files checked out for development:
-::
-
- git clone https://github.com/jsonpickle/jsonpickle.git
- cd jsonpickle
- python setup.py develop
-
-
-Numpy Support
+Numpy/Pandas Support
=============
-jsonpickle includes a built-in numpy extension. If would like to encode
-sklearn models, numpy arrays, and other numpy-based data then you must
-enable the numpy extension by registering its handlers::
+jsonpickle includes built-in numpy and pandas extensions. If you would
+like to encode sklearn models, numpy arrays, pandas DataFrames, and other
+numpy/pandas-based data, then you must enable the numpy and/or pandas
+extensions by registering their handlers::
>>> import jsonpickle.ext.numpy as jsonpickle_numpy
- >>> jsonpickle_numpy.register_handlers()
-
-Pandas Support
-==============
-jsonpickle includes a built-in pandas extension. If would like to encode
-pandas DataFrame or Series objects then you must enable the pandas extension
-by registering its handlers::
-
>>> import jsonpickle.ext.pandas as jsonpickle_pandas
+ >>> jsonpickle_numpy.register_handlers()
>>> jsonpickle_pandas.register_handlers()
Development
@@ -136,16 +122,8 @@ issues when developing.
GPG Signing
===========
-Releases before v3.0.0 are signed with `davvid's key <https://keys.openpgp.org/vks/v1/by-fingerprint/FA41BF59C1B48E8C5F3DA61C8CE26BF4A9F606B0>`_. v3.0.0 and after are likely signed by `Theelx's key <https://github.com/Theelx.gpg>`_. All upcoming releases should be signed by one of these two keys, usually Theelx's key.
-
-jsonpickleJS
-============
-`jsonpickleJS <https://github.com/cuthbertLab/jsonpickleJS>`_
-is a javascript implementation of jsonpickle by Michael Scott Cuthbert.
-jsonpickleJS can be extremely useful for projects that have parallel data
-structures between Python and Javascript.
+Unfortunately, while versions of jsonpickle before 3.0.1 should still be signed, GPG signing support was removed from PyPi (https://blog.pypi.org/posts/2023-05-23-removing-pgp/) back in May 2023.
License
=======
Licensed under the BSD License. See COPYING for details.
-See jsonpickleJS/LICENSE for details about the jsonpickleJS license.
diff --git a/contrib/pyqt-reduce-handler.py b/contrib/pyqt-reduce-handler.py
index 287505b..387380d 100644
--- a/contrib/pyqt-reduce-handler.py
+++ b/contrib/pyqt-reduce-handler.py
@@ -1,13 +1,13 @@
"""
This example demonstrates how to add a custom handler to serialize
-Qt's QPointF class jsonpickle using PyQt4.
+Qt's QPointF class jsonpickle using qtpy.
"""
import sys
import unittest
-from PyQt4 import QtCore
+from qtpy import QtCore
import jsonpickle
from jsonpickle import handlers
diff --git a/docs/conf.py b/docs/conf.py
index ccd44a7..a82ded7 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -6,10 +6,6 @@ try:
except ImportError:
furo = None
try:
- import jaraco.packaging.sphinx as jaraco_packaging_sphinx
-except ImportError:
- jaraco_packaging_sphinx = None
-try:
import rst.linker as rst_linker
except ImportError:
rst_linker = None
@@ -23,9 +19,8 @@ extensions = [
'sphinx.ext.todo',
'sphinx.ext.coverage',
]
-if jaraco_packaging_sphinx is not None:
- extensions += ['jaraco.packaging.sphinx']
+project = 'jsonpickle'
master_doc = 'index'
if furo is not None:
@@ -34,17 +29,19 @@ if furo is not None:
# Link dates and other references in the changelog
if rst_linker is not None:
extensions += ['rst.linker']
+
+package_url = 'https://github.com/jsonpickle/jsonpickle'
link_files = {
'../CHANGES.rst': dict(
using=dict(GH='https://github.com'),
replace=[
dict(
pattern=r'(Issue #|\B#)(?P<issue>\d+)',
- url='{package_url}/issues/{issue}',
+ url=package_url + '/issues/{issue}',
),
dict(
pattern=r'\B\+(?P<pull>\d+)',
- url='{package_url}/pull/{pull}',
+ url=package_url + '/pull/{pull}',
),
dict(
pattern=r'(?m:^((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n)',
diff --git a/garden.yaml b/garden.yaml
new file mode 100644
index 0000000..0e6707f
--- /dev/null
+++ b/garden.yaml
@@ -0,0 +1,61 @@
+# Use "cargo install garden-tools" to install garden https://gitlab.com/garden-rs/garden
+#
+# # Usage
+# garden doc # Build the jsonpickle.github.io documentation
+# garden fmt # Apply code formatting
+# garden test # Run tests
+# garden tox # Run tests using "tox"
+# garden bench # Run benchmarks
+trees:
+ jsonpickle:
+ description: Two-way conversion of complex Python objects into JSON.
+ path: ${GARDEN_CONFIG_DIR}
+ url: git@github.com:jsonpickle/jsonpickle.git
+ remotes:
+ Theelx: git@github.com:Theelx/jsonpickle.git
+ davvid: git@github.com:davvid/jsonpickle.git
+ commands:
+ bench: |
+ python3 -m pytest \
+ --benchmark-verbose \
+ --benchmark-only \
+ --benchmark-histogram=./images/benchmark-${timestamp} \
+ ./jsonpickle_benchmarks.py
+ check>:
+ - test
+ - check/fmt
+ - doc
+ check/fmt: garden ${GARDEN_CMD_VERBOSE} fmt -- --check
+ clean: |
+ rm -fr build dist jsonpickle.egg-info
+ find . -type d -name __pycache__ -print0 | xargs -0 rm -fr
+ doc: |
+ test -d pages || garden grow pages
+ ${activate} python3 -m sphinx docs pages
+ fmt: |
+ ${activate}
+ black --skip-string-normalization --target-version py37 "$@" jsonpickle tests
+ isort --profile=black "$@" jsonpickle tests
+ setup: |
+ python3 -m venv env3
+ garden ${GARDEN_CMD_VERBOSE} setup/dev
+ setup/dev: ${activate} pip install --editable '.[dev,docs]'
+ setup/packaging: ${activate} pip install --editable '.[packaging]'
+ test: ${activate} python3 -m pytest ${GARDEN_CMD_VERBOSE} "$@" jsonpickle tests
+ tox: tox run-parallel --parallel-live "$@" ${GARDEN_CMD_VERBOSE}
+ wheel: |
+ ${activate}
+ python3 -m build -n .
+ publish: |
+ ${activate}
+ twine upload dist/*.whl dist/*.tar.gz
+ pages:
+ description: The jsonpickle.github.io website.
+ url: git@github.com:jsonpickle/jsonpickle.github.io.git
+
+variables:
+ activate: |
+ test -n "$VIRTUAL_ENV" ||
+ test ! -d env3 ||
+ source env3/bin/activate
+ timestamp: $ date +%Y-%m-%dT%T%z
diff --git a/jsonpickle.egg-info/PKG-INFO b/jsonpickle.egg-info/PKG-INFO
index cfaca35..bed22d5 100644
--- a/jsonpickle.egg-info/PKG-INFO
+++ b/jsonpickle.egg-info/PKG-INFO
@@ -1,7 +1,7 @@
Metadata-Version: 2.1
Name: jsonpickle
-Version: 3.0.3
-Summary: Python library for serializing any arbitrary object graph into JSON
+Version: 3.0.4
+Summary: Serialize any Python object to JSON
Home-page: https://github.com/jsonpickle/jsonpickle
Author: David Aguilar
Author-email: davvid@gmail.com
@@ -18,162 +18,14 @@ Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: JavaScript
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Provides-Extra: docs
+Provides-Extra: packaging
Provides-Extra: testing
License-File: LICENSE
-.. image:: https://img.shields.io/pypi/v/jsonpickle.svg
- :target: `PyPI link`_
-
-.. image:: https://img.shields.io/pypi/pyversions/jsonpickle.svg
- :target: `PyPI link`_
-
-.. _PyPI link: https://pypi.org/project/jsonpickle
-
-.. image:: https://readthedocs.org/projects/jsonpickle/badge/?version=latest
- :target: https://jsonpickle.readthedocs.io/en/latest/?badge=latest
-
-.. image:: https://github.com/jsonpickle/jsonpickle/actions/workflows/test.yml/badge.svg
- :target: https://github.com/jsonpickle/jsonpickle/actions
- :alt: Github Actions
-
-.. image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
- :target: https://github.com/jsonpickle/jsonpickle/blob/main/COPYING
- :alt: BSD
-
-
-jsonpickle
-==========
-jsonpickle is a library for the two-way conversion of complex Python objects
-and `JSON <http://json.org/>`_. jsonpickle builds upon the existing JSON
-encoders, such as simplejson, json, and ujson.
-
-.. warning::
-
- jsonpickle can execute arbitrary Python code.
-
- Please see the Security section for more details.
-
-
-For complete documentation, please visit the
-`jsonpickle documentation <http://jsonpickle.readthedocs.io/>`_.
-
-Bug reports and merge requests are encouraged at the
-`jsonpickle repository on github <https://github.com/jsonpickle/jsonpickle>`_.
-
-Why jsonpickle?
-===============
-Data serialized with python's pickle (or cPickle or dill) is not easily readable outside of python. Using the json format, jsonpickle allows simple data types to be stored in a human-readable format, and more complex data types such as numpy arrays and pandas dataframes, to be machine-readable on any platform that supports json. E.g., unlike pickled data, jsonpickled data stored in an Amazon S3 bucket is indexible by Amazon's Athena.
-
-Security
-========
-
-jsonpickle should be treated the same as the
-`Python stdlib pickle module <https://docs.python.org/3/library/pickle.html>`_
-from a security perspective.
-
-.. warning::
-
- The jsonpickle module **is not secure**. Only unpickle data you trust.
-
- It is possible to construct malicious pickle data which will **execute
- arbitrary code during unpickling**. Never unpickle data that could have come
- from an untrusted source, or that could have been tampered with.
-
- Consider signing data with an HMAC if you need to ensure that it has not
- been tampered with.
-
- Safer deserialization approaches, such as reading JSON directly,
- may be more appropriate if you are processing untrusted data.
-
-
-Install
-=======
-
-Install from pip for the latest stable release:
-
-::
-
- pip install jsonpickle
-
-Install from github for the latest changes:
-
-::
-
- pip install git+https://github.com/jsonpickle/jsonpickle.git
-
-If you have the files checked out for development:
-
-::
-
- git clone https://github.com/jsonpickle/jsonpickle.git
- cd jsonpickle
- python setup.py develop
-
-
-Numpy Support
-=============
-jsonpickle includes a built-in numpy extension. If would like to encode
-sklearn models, numpy arrays, and other numpy-based data then you must
-enable the numpy extension by registering its handlers::
-
- >>> import jsonpickle.ext.numpy as jsonpickle_numpy
- >>> jsonpickle_numpy.register_handlers()
-
-Pandas Support
-==============
-jsonpickle includes a built-in pandas extension. If would like to encode
-pandas DataFrame or Series objects then you must enable the pandas extension
-by registering its handlers::
-
- >>> import jsonpickle.ext.pandas as jsonpickle_pandas
- >>> jsonpickle_pandas.register_handlers()
-
-Development
-===========
-
-Use `make` to run the unit tests::
-
- make test
-
-`pytest` is used to run unit tests internally.
-
-A `tox` target is provided to run tests using all installed and supported Python versions::
-
- make tox
-
-`jsonpickle` itself has no dependencies beyond the Python stdlib.
-`tox` is required for testing when using the `tox` test runner only.
-
-The testing requirements are specified in `requirements-dev.txt`.
-It is recommended to create a virtualenv and run tests from within the
-virtualenv, or use a tool such as `vx <https://github.com/davvid/vx/>`_
-to activate the virtualenv without polluting the shell environment::
-
- python3 -mvenv env3x
- vx env3x pip install --requirement requirements-dev.txt
- vx env3x make test
-
-`jsonpickle` supports multiple Python versions, so using a combination of
-multiple virtualenvs and `tox` is useful in order to catch compatibility
-issues when developing.
-
-GPG Signing
-===========
-Releases before v3.0.0 are signed with `davvid's key <https://keys.openpgp.org/vks/v1/by-fingerprint/FA41BF59C1B48E8C5F3DA61C8CE26BF4A9F606B0>`_. v3.0.0 and after are likely signed by `Theelx's key <https://github.com/Theelx.gpg>`_. All upcoming releases should be signed by one of these two keys, usually Theelx's key.
-
-jsonpickleJS
-============
-`jsonpickleJS <https://github.com/cuthbertLab/jsonpickleJS>`_
-is a javascript implementation of jsonpickle by Michael Scott Cuthbert.
-jsonpickleJS can be extremely useful for projects that have parallel data
-structures between Python and Javascript.
-
-License
-=======
-Licensed under the BSD License. See COPYING for details.
-See jsonpickleJS/LICENSE for details about the jsonpickleJS license.
+Python library for serializing arbitrary object graphs into JSON
diff --git a/jsonpickle.egg-info/SOURCES.txt b/jsonpickle.egg-info/SOURCES.txt
index 670e895..74d0613 100644
--- a/jsonpickle.egg-info/SOURCES.txt
+++ b/jsonpickle.egg-info/SOURCES.txt
@@ -11,12 +11,11 @@ README.rst
appveyor.yml
azure-pipelines.yml
conftest.py
+garden.yaml
jsonpickle_benchmarks.py
pyproject.toml
pytest.ini
-requirements-dev.txt
setup.cfg
-setup.py
skeleton.md
tox.ini
.github/FUNDING.yml
diff --git a/jsonpickle.egg-info/requires.txt b/jsonpickle.egg-info/requires.txt
index 8e94eb9..be11eb1 100644
--- a/jsonpickle.egg-info/requires.txt
+++ b/jsonpickle.egg-info/requires.txt
@@ -4,21 +4,27 @@ importlib_metadata
[docs]
furo
-jaraco.packaging>=9
rst.linker>=1.9
sphinx
+[packaging]
+build
+twine
+
[testing]
+bson
ecdsa
feedparser
numpy
pandas
pymongo
pytest!=3.7.3,>=3.5
+pytest-benchmark
+pytest-benchmark[histogram]
pytest-checkdocs>=1.2.3
pytest-cov
pytest-enabler>=1.0.1
-pytest-ruff
+pytest-ruff>=0.2.1
scikit-learn
simplejson
sqlalchemy
@@ -26,3 +32,9 @@ ujson
[testing:python_version < "3.12"]
gmpy2
+
+[testing:python_version <= "3.10"]
+scipy
+
+[testing:python_version > "3.10"]
+scipy>=1.9.3
diff --git a/jsonpickle/__init__.py b/jsonpickle/__init__.py
index a41f7ac..54af6b5 100644
--- a/jsonpickle/__init__.py
+++ b/jsonpickle/__init__.py
@@ -67,8 +67,6 @@ added to JSON::
that contain non-string dictionary keys.
"""
-from __future__ import absolute_import, division, unicode_literals
-
# Export other names not in __all__
from .backend import JSONBackend # noqa: F401
from .backend import json
diff --git a/jsonpickle/backend.py b/jsonpickle/backend.py
index 1faa13f..2e9ebb9 100644
--- a/jsonpickle/backend.py
+++ b/jsonpickle/backend.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
from .compat import string_types
diff --git a/jsonpickle/compat.py b/jsonpickle/compat.py
index 320c64d..e971aeb 100644
--- a/jsonpickle/compat.py
+++ b/jsonpickle/compat.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
import queue # noqa
import sys
from collections.abc import Iterator as abc_iterator # noqa
diff --git a/jsonpickle/ext/numpy.py b/jsonpickle/ext/numpy.py
index af2bf71..fd321d3 100644
--- a/jsonpickle/ext/numpy.py
+++ b/jsonpickle/ext/numpy.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import
-
import ast
import json
import sys
diff --git a/jsonpickle/ext/pandas.py b/jsonpickle/ext/pandas.py
index c49b86b..475b77e 100644
--- a/jsonpickle/ext/pandas.py
+++ b/jsonpickle/ext/pandas.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import
-
import zlib
from io import StringIO
diff --git a/jsonpickle/handlers.py b/jsonpickle/handlers.py
index 6a5162a..2109f6b 100644
--- a/jsonpickle/handlers.py
+++ b/jsonpickle/handlers.py
@@ -8,8 +8,6 @@ A handler can be bound to other types by calling
"""
-from __future__ import absolute_import, division, unicode_literals
-
import array
import copy
import datetime
diff --git a/jsonpickle/pickler.py b/jsonpickle/pickler.py
index 7b37e57..d95f792 100644
--- a/jsonpickle/pickler.py
+++ b/jsonpickle/pickler.py
@@ -1,11 +1,9 @@
# Copyright (C) 2008 John Paulett (john -at- paulett.org)
-# Copyright (C) 2009-2018 David Aguilar (davvid -at- gmail.com)
+# Copyright (C) 2009-2024 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.
-from __future__ import absolute_import, division, unicode_literals
-
import decimal
import inspect
import itertools
@@ -36,6 +34,7 @@ def encode(
indent=None,
separators=None,
include_properties=False,
+ handle_readonly=False,
):
"""Return a JSON formatted representation of value, a Python object.
@@ -54,10 +53,6 @@ def encode(
)
before your pickling code.
- :param max_depth: 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.
:param make_refs: 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
@@ -67,15 +62,25 @@ def encode(
dictionary keys instead of coercing them into strings via `repr()`.
This is typically what you want if you need to support Integer or
objects as dictionary keys.
- :param numeric_keys: Only use this option if the backend supports integer
- dict keys natively. This flag tells jsonpickle to leave numeric keys
- as-is rather than conforming them to json-friendly strings.
- Using ``keys=True`` is the typical solution for integer keys, so only
- use this if you have a specific use case where you want to allow the
- backend to handle serialization of numeric dict keys.
+ :param max_depth: 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.
+ :param reset: Custom pickle handlers that use the `Pickler.flatten` method or
+ `jsonpickle.encode` function must call `encode` with `reset=False`
+ in order to retain object references during pickling.
+ This flag is not typically used outside of a custom handler or
+ `__getstate__` implementation.
+ :param backend: If set to an instance of jsonpickle.backend.JSONBackend,
+ jsonpickle will use that backend for deserialization.
:param warn: If set to True then jsonpickle will warn when it
returns None for an object which it cannot pickle
(e.g. file descriptors).
+ :param context: Supply a pre-built Pickler or Unpickler object to the
+ `jsonpickle.encode` and `jsonpickle.decode` machinery instead
+ of creating a new instance. The `context` represents the currently
+ active Pickler and Unpickler objects when custom handlers are
+ invoked by jsonpickle.
:param max_iter: If set to a non-negative integer then jsonpickle will
consume at most `max_iter` items when pickling iterators.
:param use_decimal: If set to True jsonpickle will allow Decimal
@@ -91,6 +96,12 @@ def encode(
NOTE: A side-effect of the above settings is that float values will be
converted to Decimal when converting to json.
+ :param numeric_keys: Only use this option if the backend supports integer
+ dict keys natively. This flag tells jsonpickle to leave numeric keys
+ as-is rather than conforming them to json-friendly strings.
+ Using ``keys=True`` is the typical solution for integer keys, so only
+ use this if you have a specific use case where you want to allow the
+ backend to handle serialization of numeric dict keys.
:param use_base85:
If possible, use base85 to encode binary data. Base85 bloats binary data
by 1/4 as opposed to base64, which expands it by 1/3. This argument is
@@ -118,6 +129,11 @@ def encode(
meant to be used if processing the json outside of Python. Certain types
such as sets will not pickle due to not having a native-json equivalent.
Defaults to ``False``.
+ :param handle_readonly:
+ Handle objects with readonly methods, such as Django's SafeString. This
+ basically prevents jsonpickle from raising an exception for such objects.
+ You MUST set ``handle_readonly=True`` for the decoding if you encode with
+ this flag set to ``True``.
>>> encode('my string') == '"my string"'
True
@@ -143,6 +159,8 @@ def encode(
use_base85=use_base85,
fail_safe=fail_safe,
include_properties=include_properties,
+ handle_readonly=handle_readonly,
+ original_object=value,
)
return backend.encode(
context.flatten(value, reset=reset), indent=indent, separators=separators
@@ -190,6 +208,8 @@ class Pickler(object):
use_base85=False,
fail_safe=None,
include_properties=False,
+ handle_readonly=False,
+ original_object=None,
):
self.unpicklable = unpicklable
self.make_refs = make_refs
@@ -212,6 +232,8 @@ class Pickler(object):
self._use_decimal = use_decimal
# A cache of objects that have already been flattened.
self._flattened = {}
+ # Used for util.is_readonly, see +483
+ self.handle_readonly = handle_readonly
if self.use_base85:
self._bytes_tag = tags.B85
@@ -224,6 +246,8 @@ class Pickler(object):
self.fail_safe = fail_safe
self.include_properties = include_properties
+ self._original_object = original_object
+
def _determine_sort_keys(self):
for _, options in getattr(self.backend, '_encoder_options', {}).values():
if options.get("sort_keys", False):
@@ -293,9 +317,11 @@ class Pickler(object):
return pretend_new or is_new
def _getref(self, obj):
+ """Return a "py/id" entry for the specified object"""
return {tags.ID: self._objs.get(id(obj))}
def _flatten(self, obj):
+ """Flatten an object and its guts into a json-safe representation"""
if self.unpicklable and self.make_refs:
result = self._flatten_impl(obj)
else:
@@ -417,6 +443,13 @@ class Pickler(object):
"""Flatten a key/value pair into the passed-in dictionary."""
if not util.is_picklable(k, v):
return data
+ # TODO: use inspect.getmembers_static on 3.11+ because it avoids dynamic attribute lookups
+ if (
+ self.handle_readonly
+ and k in {attr for attr, val in inspect.getmembers(self._original_object)}
+ and util.is_readonly(self._original_object, k, v)
+ ):
+ return data
if k is None:
k = 'null' # for compatibility with common json encoders
@@ -458,7 +491,7 @@ class Pickler(object):
# i don't like lambdas
def valid_property(x):
- return not x[0].startswith("__") and x[0] not in allslots_set
+ return not x[0].startswith('__') and x[0] not in allslots_set
properties = [
x[0] for x in inspect.getmembers(obj.__class__) if valid_property(x)
@@ -611,7 +644,9 @@ class Pickler(object):
data[tags.OBJECT] = class_name
if has_getnewargs_ex:
- data[tags.NEWARGSEX] = list(map(self._flatten, obj.__getnewargs_ex__()))
+ data[tags.NEWARGSEX] = [
+ self._flatten(arg) for arg in obj.__getnewargs_ex__()
+ ]
if has_getnewargs and not has_getnewargs_ex:
data[tags.NEWARGS] = self._flatten(obj.__getnewargs__())
diff --git a/jsonpickle/tags.py b/jsonpickle/tags.py
index 62c80a2..9f2bb22 100644
--- a/jsonpickle/tags.py
+++ b/jsonpickle/tags.py
@@ -7,8 +7,6 @@ these custom key names to identify dictionaries
that need to be specially handled.
"""
-from __future__ import absolute_import, division, unicode_literals
-
BYTES = 'py/bytes'
B64 = 'py/b64'
B85 = 'py/b85'
diff --git a/jsonpickle/unpickler.py b/jsonpickle/unpickler.py
index 21afb07..ec1e9dc 100644
--- a/jsonpickle/unpickler.py
+++ b/jsonpickle/unpickler.py
@@ -1,11 +1,9 @@
# Copyright (C) 2008 John Paulett (john -at- paulett.org)
-# Copyright (C) 2009-2018 David Aguilar (davvid -at- gmail.com)
+# Copyright (C) 2009-2024 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.
-from __future__ import absolute_import, division, unicode_literals
-
import dataclasses
import sys
import warnings
@@ -25,42 +23,53 @@ def decode(
classes=None,
v1_decode=False,
on_missing="ignore",
+ handle_readonly=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.
-
- The keyword argument 'classes' defaults to None.
- If set to a single class, or a sequence (list, set, tuple) of classes,
- then the classes will be made available when constructing objects.
- If set to a dictionary of class names to class objects, the class object
- will be provided to jsonpickle to deserialize the class name into.
- This can be used to give jsonpickle access to local classes that are not
- available through the global module import scope, and the dict method can
- be used to deserialize encoded objects into a new class.
-
- The keyword argument 'safe' defaults to False.
- If set to True, eval() is avoided, but backwards-compatible
- (pre-0.7.0) deserialization of repr-serialized objects is disabled.
-
- The keyword argument 'backend' defaults to None.
- If set to an instance of jsonpickle.backend.JSONBackend, jsonpickle
- will use that backend for deserialization.
-
- The keyword argument 'v1_decode' defaults to False.
- If set to True it enables you to decode objects serialized in jsonpickle v1.
- Please do not attempt to re-encode the objects in the v1 format! Version 2's
- format fixes issue #255, and allows dictionary identity to be preserved
- through an encode/decode cycle.
-
- The keyword argument 'on_missing' defaults to 'ignore'.
- If set to 'error', it will raise an error if the class it's decoding is not
- found. If set to 'warn', it will warn you in said case. If set to a
- non-awaitable function, it will call said callback function with the class
- name (a string) as the only parameter. Strings passed to on_missing are
- lowercased automatically.
+ :param backend: If set to an instance of jsonpickle.backend.JSONBackend, jsonpickle
+ will use that backend for deserialization.
+
+ :param context: Supply a pre-built Pickler or Unpickler object to the
+ `jsonpickle.encode` and `jsonpickle.decode` machinery instead
+ of creating a new instance. The `context` represents the currently
+ active Pickler and Unpickler objects when custom handlers are
+ invoked by jsonpickle.
+
+ :param keys: If set to True then jsonpickle will decode non-string dictionary keys
+ into python objects via the jsonpickle protocol.
+
+ :param reset: Custom pickle handlers that use the `Pickler.flatten` method or
+ `jsonpickle.encode` function must call `encode` with `reset=False`
+ in order to retain object references during pickling.
+ This flag is not typically used outside of a custom handler or
+ `__getstate__` implementation.
+
+ :param safe: If set to True, eval() is avoided, but backwards-compatible
+ (pre-0.7.0) deserialization of repr-serialized objects is disabled.
+
+ :param classes: If set to a single class, or a sequence (list, set, tuple) of classes,
+ then the classes will be made available when constructing objects.
+ If set to a dictionary of class names to class objects, the class object
+ will be provided to jsonpickle to deserialize the class name into.
+ This can be used to give jsonpickle access to local classes that are not
+ available through the global module import scope, and the dict method can
+ be used to deserialize encoded objects into a new class.
+
+ :param v1_decode: If set to True it enables you to decode objects serialized in jsonpickle v1.
+ Please do not attempt to re-encode the objects in the v1 format! Version 2's
+ format fixes issue #255, and allows dictionary identity to be preserved
+ through an encode/decode cycle.
+
+ :param on_missing: If set to 'error', it will raise an error if the class it's decoding is not
+ found. If set to 'warn', it will warn you in said case. If set to a
+ non-awaitable function, it will call said callback function with the class
+ name (a string) as the only parameter. Strings passed to on_missing are
+ lowercased automatically.
+
+ :param handle_readonly: If set to True, the Unpickler will handle objects encoded with
+ 'handle_readonly' properly. Do not set this flag for objects not encoded
+ with 'handle_readonly' set to True.
>>> decode('"my string"') == 'my string'
@@ -83,6 +92,7 @@ def decode(
safe=safe,
v1_decode=v1_decode,
on_missing=on_missing,
+ handle_readonly=handle_readonly,
)
data = backend.decode(string)
return context.restore(data, reset=reset, classes=classes)
@@ -151,10 +161,12 @@ class _IDProxy(_Proxy):
def _obj_setattr(obj, attr, proxy):
+ """Use setattr to update a proxy entry"""
setattr(obj, attr, proxy.get())
def _obj_setvalue(obj, idx, proxy):
+ """Use obj[key] assignments to update a proxy entry"""
obj[idx] = proxy.get()
@@ -296,13 +308,20 @@ def has_tag_dict(obj, tag):
class Unpickler(object):
def __init__(
- self, backend=None, keys=False, safe=False, v1_decode=False, on_missing="ignore"
+ self,
+ backend=None,
+ keys=False,
+ safe=False,
+ v1_decode=False,
+ on_missing='ignore',
+ handle_readonly=False,
):
self.backend = backend or json
self.keys = keys
self.safe = safe
self.v1_decode = v1_decode
self.on_missing = on_missing
+ self.handle_readonly = handle_readonly
self.reset()
@@ -409,7 +428,7 @@ class Unpickler(object):
def _mkref(self, obj):
obj_id = id(obj)
try:
- self._obj_to_idx[obj_id]
+ _ = self._obj_to_idx[obj_id]
except KeyError:
self._obj_to_idx[obj_id] = len(self._objs)
self._objs.append(obj)
@@ -626,11 +645,14 @@ class Unpickler(object):
# i think this is the only way to set frozen dataclass attrs
object.__setattr__(instance, k, value)
except AttributeError as e:
- # some objects may raise this for read-only attributes (#422)
+ # some objects may raise this for read-only attributes (#422) (#478)
if (
hasattr(instance, "__slots__")
and not len(instance.__slots__)
and issubclass(instance.__class__, int)
+ and self.handle_readonly
+ # we have to handle this separately because of +483
+ and issubclass(instance.__class__, str)
):
continue
raise e
@@ -697,7 +719,7 @@ class Unpickler(object):
return instance
- def _restore_object_instance(self, obj, cls, class_name=""):
+ def _restore_object_instance(self, obj, cls, class_name=''):
# This is a placeholder proxy object which allows child objects to
# reference the parent object before it has been instantiated.
proxy = _Proxy()
@@ -719,7 +741,7 @@ class Unpickler(object):
is_oldstyle = not (isinstance(cls, type) or getattr(cls, '__meta__', None))
try:
- if (not is_oldstyle) and hasattr(cls, '__new__'):
+ if not is_oldstyle and hasattr(cls, '__new__'):
# new style classes
if factory:
instance = cls.__new__(cls, factory, *args, **kwargs)
@@ -832,6 +854,7 @@ class Unpickler(object):
return tuple([self._restore(v) for v in obj[tags.TUPLE]])
def _restore_tags(self, obj):
+ """Return the restoration function for the specified object"""
try:
if not tags.RESERVED <= set(obj) and type(obj) not in (list, dict):
diff --git a/jsonpickle/util.py b/jsonpickle/util.py
index eadbcc1..6b3f551 100644
--- a/jsonpickle/util.py
+++ b/jsonpickle/util.py
@@ -8,8 +8,6 @@
"""Helper functions for pickling and unpickling. Most functions assist in
determining the type of an object.
"""
-from __future__ import absolute_import, division, unicode_literals
-
import base64
import collections
import inspect
@@ -397,6 +395,20 @@ def is_cython_function(obj):
)
+def is_readonly(obj, attr, value):
+ # CPython 3.11+ has 0-cost try/except, please use up-to-date versions!
+ try:
+ setattr(obj, attr, value)
+ return False
+ except AttributeError:
+ # this is okay, it means the attribute couldn't be set
+ return True
+ except TypeError:
+ # this should only be happening when obj is a dict
+ # as these errors happen when attr isn't a str
+ return True
+
+
def in_dict(obj, key, default=False):
"""
Returns true if key exists in obj.__dict__; false if not in.
diff --git a/requirements-dev.txt b/requirements-dev.txt
deleted file mode 100644
index bac9bad..0000000
--- a/requirements-dev.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-bson
-coverage
-ecdsa
-feedparser
-gmpy2; python_version<"3.12"
-numpy
-pandas
-pymongo
-pytest
-pytest-cov
-pytest-enabler
-pytest-benchmark
-pytest-benchmark[histogram]
-pytest-ruff
-scikit-learn
-scipy>=1.9.3; python_version>"3.10"
-scipy; python_version<="3.10"
-simplejson
-sqlalchemy>=1.2.19
-ujson
diff --git a/setup.cfg b/setup.cfg
index 6573d40..e0bf0a5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,8 +3,8 @@ license_file = LICENSE
name = jsonpickle
author = David Aguilar
author_email = davvid@gmail.com
-description = Python library for serializing any arbitrary object graph into JSON
-long_description = file:README.rst
+description = Serialize any Python object to JSON
+long_description = Python library for serializing arbitrary object graphs into JSON
url = https://github.com/jsonpickle/jsonpickle
classifiers =
Development Status :: 5 - Production/Stable
@@ -17,6 +17,7 @@ classifiers =
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
+ Programming Language :: Python :: 3.12
Programming Language :: JavaScript
Operating System :: OS Independent
Topic :: Software Development :: Libraries :: Python Modules
@@ -45,25 +46,30 @@ testing =
pytest-checkdocs >= 1.2.3
pytest-cov
pytest-enabler >= 1.0.1
- pytest-ruff
+ pytest-ruff >= 0.2.1
+ bson
ecdsa
feedparser
gmpy2; python_version<"3.12"
numpy
pandas
pymongo
+ pytest-benchmark
+ pytest-benchmark[histogram]
scikit-learn
+ scipy>=1.9.3; python_version>"3.10"
+ scipy; python_version<="3.10"
simplejson
sqlalchemy
ujson
docs =
sphinx
- jaraco.packaging >= 9
rst.linker >= 1.9
furo
-
-[options.entry_points]
+packaging =
+ build
+ twine
[egg_info]
tag_build =
diff --git a/setup.py b/setup.py
deleted file mode 100644
index bac24a4..0000000
--- a/setup.py
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env python
-
-import setuptools
-
-if __name__ == "__main__":
- setuptools.setup()
diff --git a/tests/backend_test.py b/tests/backend_test.py
index 80bb1f4..bfb388e 100644
--- a/tests/backend_test.py
+++ b/tests/backend_test.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
import decimal
import unittest
from hashlib import md5
@@ -54,7 +52,7 @@ class BackendBase(SkippableTest):
# always reset to default backend
jsonpickle.set_preferred_backend('json')
- def assertEncodeDecode(self, json_input):
+ def assert_roundtrip(self, json_input):
expect = SAMPLE_DATA
actual = jsonpickle.decode(json_input)
self.assertEqual(expect['things'][0].name, actual['things'][0].name)
@@ -95,7 +93,7 @@ class JsonTestCase(BackendBase):
'"child": null} '
']}'
)
- self.assertEncodeDecode(expected_pickled)
+ self.assert_roundtrip(expected_pickled)
class SimpleJsonTestCase(BackendBase):
@@ -110,7 +108,7 @@ class SimpleJsonTestCase(BackendBase):
'"child": null}'
']}'
)
- self.assertEncodeDecode(expected_pickled)
+ self.assert_roundtrip(expected_pickled)
def test_decimal(self):
# Default behavior: Decimal is preserved
@@ -170,7 +168,7 @@ class UJsonTestCase(BackendBase):
'"name":"data","child":null}'
']}'
)
- self.assertEncodeDecode(expected_pickled)
+ self.assert_roundtrip(expected_pickled)
def suite():
diff --git a/tests/collections_test.py b/tests/collections_test.py
index e522097..a1ebce9 100644
--- a/tests/collections_test.py
+++ b/tests/collections_test.py
@@ -33,8 +33,6 @@
"""
Unit tests for collections
"""
-from __future__ import absolute_import, division, print_function, unicode_literals
-
import sys
from collections import OrderedDict, defaultdict
diff --git a/tests/datetime_test.py b/tests/datetime_test.py
index 06f96ae..e51cc11 100644
--- a/tests/datetime_test.py
+++ b/tests/datetime_test.py
@@ -179,10 +179,10 @@ class DateTimeSimpleTestCase(unittest.TestCase):
now = datetime.datetime.now()
SaoPaulo = ZoneInfo('America/Sao_Paulo')
- USEastern = ZoneInfo('US/Eastern')
+ NewYork = ZoneInfo('America/New_York')
now_sp = now.replace(tzinfo=SaoPaulo)
- now_us = now.replace(tzinfo=USEastern)
+ now_us = now.replace(tzinfo=NewYork)
self._roundtrip(now_sp)
self._roundtrip(now_us)
diff --git a/tests/ecdsa_test.py b/tests/ecdsa_test.py
index 4ae4fe2..b8d5937 100644
--- a/tests/ecdsa_test.py
+++ b/tests/ecdsa_test.py
@@ -1,7 +1,5 @@
"""Test serializing ecdsa keys"""
-from __future__ import absolute_import, division, unicode_literals
-
import unittest
import pytest
diff --git a/tests/jsonpickle_test.py b/tests/jsonpickle_test.py
index 3a98a4d..bf7ff74 100644
--- a/tests/jsonpickle_test.py
+++ b/tests/jsonpickle_test.py
@@ -1,11 +1,9 @@
# Copyright (C) 2008 John Paulett (john -at- paulett.org)
-# Copyright (C) 2009-2021 David Aguilar (davvid -at- gmail.com)
+# Copyright (C) 2009-2024 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.
-from __future__ import absolute_import, division, unicode_literals
-
import collections
import os
import unittest
@@ -51,6 +49,9 @@ class Capture:
self.args = args
self.kwargs = kwargs
+ def __repr__(self):
+ return object.__repr__(self) + ('(%r, %r)' % (self.args, self.kwargs))
+
class ThingWithProps:
def _get_identity(self):
@@ -164,6 +165,15 @@ class MyPropertiesDict:
)
+# see issue #478
+class SafeData:
+ __slots__ = ()
+
+
+class SafeString(str, SafeData):
+ __slots__ = ()
+
+
def on_missing_callback(class_name):
# not actually a runtime problem but it doesn't matter
warnings.warn("The unpickler couldn't find %s" % class_name, RuntimeWarning)
@@ -194,13 +204,13 @@ class PicklingTestCase(unittest.TestCase):
self.assertEqual({tags.B64: encoded}, self.pickler.flatten(data))
def test_decode_base85(self):
- pickled = {tags.B85: 'P{Y4;Xv4O{u^=-c'}
- expected = u'P\u00ffth\u00f6\u00f1 3!'.encode('utf-8')
+ expected = 'Pÿthöñ 3!'.encode('utf-8')
+ pickled = {tags.B85: util.b85encode(expected)}
self.assertEqual(expected, self.unpickler.restore(pickled))
def test_base85_still_handles_base64(self):
- pickled = {tags.B64: 'UMO/dGjDtsOxIDMh'}
- expected = u'P\u00ffth\u00f6\u00f1 3!'.encode('utf-8')
+ expected = 'Pÿthöñ 3!'.encode('utf-8')
+ pickled = {tags.B64: util.b64encode(expected)}
self.assertEqual(expected, self.unpickler.restore(pickled))
def test_string(self):
@@ -990,6 +1000,17 @@ class JSONPickleTestCase(SkippableTest):
pickler.flatten([liszt, liszt, liszt, liszt, liszt])
self.assertEqual(pickler._depth, -1)
+ def test_readonly_attrs(self):
+ s = SafeString("test")
+ pickled = jsonpickle.encode(s, handle_readonly=True)
+ pickled_json_dict = jsonpickle.backend.json.loads(pickled)
+ # make sure it's giving concise output by erroring if it includes
+ # a default method which is unnecessary
+ self.assertTrue("join" not in pickled_json_dict)
+ unpickled = jsonpickle.decode(pickled)
+ self.assertEqual(unpickled.__class__, SafeString)
+ self.assertEqual(unpickled, s)
+
class PicklableNamedTuple(object):
"""
diff --git a/tests/numpy_test.py b/tests/numpy_test.py
index 580e81f..694f21f 100644
--- a/tests/numpy_test.py
+++ b/tests/numpy_test.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
import datetime
import warnings
diff --git a/tests/object_test.py b/tests/object_test.py
index beb7b23..2434057 100644
--- a/tests/object_test.py
+++ b/tests/object_test.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
import array
import collections
import datetime
diff --git a/tests/pandas_test.py b/tests/pandas_test.py
index 530449f..532563f 100644
--- a/tests/pandas_test.py
+++ b/tests/pandas_test.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
import datetime
import pytest
diff --git a/tests/sklearn_test.py b/tests/sklearn_test.py
index 210e098..853cc79 100644
--- a/tests/sklearn_test.py
+++ b/tests/sklearn_test.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division, unicode_literals
-
import pytest
try:
diff --git a/tests/sqlalchemy_test.py b/tests/sqlalchemy_test.py
index 381812e..002b5a9 100644
--- a/tests/sqlalchemy_test.py
+++ b/tests/sqlalchemy_test.py
@@ -8,6 +8,7 @@ import jsonpickle
try:
import sqlalchemy as sqa
+ from sqlalchemy import orm
from sqlalchemy.ext import declarative
from sqlalchemy.orm import Session
@@ -16,7 +17,12 @@ except ImportError:
HAS_SQA = False
if HAS_SQA:
- Base = declarative.declarative_base()
+ # sqlalchemy.ext.declarative.declarative_base() was deprecated in SQLAlchemy 2.0
+ # and replaced by sqlalchemy.orm.declarative_base().
+ if hasattr(orm, 'declarative_base'):
+ Base = orm.declarative_base()
+ else:
+ Base = declarative.declarative_base()
class Table(Base):
__tablename__ = 'table'
diff --git a/tests/util_test.py b/tests/util_test.py
index 4d8da07..8456631 100644
--- a/tests/util_test.py
+++ b/tests/util_test.py
@@ -4,9 +4,6 @@
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.
-
-from __future__ import absolute_import, division, unicode_literals
-
import doctest
import io
import time
diff --git a/tests/wizard_test.py b/tests/wizard_test.py
index ef3014e..b303d43 100644
--- a/tests/wizard_test.py
+++ b/tests/wizard_test.py
@@ -6,8 +6,6 @@ Includes functionality to assist with adding compatibility to jsonpickle.
"""
-from __future__ import absolute_import, division, unicode_literals
-
import collections
import unittest
diff --git a/tests/zoneinfo_test.py b/tests/zoneinfo_test.py
index 5c7f3db..3d87c15 100644
--- a/tests/zoneinfo_test.py
+++ b/tests/zoneinfo_test.py
@@ -20,7 +20,7 @@ if sys.version_info >= (3, 9):
"""
jsonpickle should pickle a zoneinfo object
"""
- self._roundtrip(ZoneInfo("Australia/Queensland"))
+ self._roundtrip(ZoneInfo("Australia/Brisbane"))
def suite():
suite = unittest.TestSuite()
diff --git a/tox.ini b/tox.ini
index 7a3d447..07e4b8c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,10 +4,12 @@ envlist = clean,py36,py37,py38,py39,py310,py311,py312,report
skip_missing_interpreters = true
[testenv]
+allowlist_externals =
+ ruff
passenv =
FORCE_COLOR
commands =
- python3 -m pytest -p no:cacheprovider --cov --cov-append --cov-report=term-missing jsonpickle tests {posargs}
+ python3 -m pytest --cov --cov-append --cov-report=term-missing jsonpickle tests {posargs}
depends =
{py36,py37,py38,py39,py310,py311,py312}: clean
report: py36,py37,py38,py39,py310,py311,py312