diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-08-08 23:34:20 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-08-08 23:52:51 -0400 |
| commit | 104e6907284e602a8485f32fc67fd6af0c00e4d0 (patch) | |
| tree | fd628fbc353341fa31953b5f55be98b9748e6bea /lib/sqlalchemy | |
| parent | d8da7f5ac544f3dd853a221faa5fab4ff788e25b (diff) | |
| download | sqlalchemy-104e6907284e602a8485f32fc67fd6af0c00e4d0.tar.gz | |
Correct name for json_serializer / json_deserializer, document and test
The dialects that support json are supposed to take arguments
``json_serializer`` and ``json_deserializer`` at the create_engine() level,
however the SQLite dialect calls them ``_json_serilizer`` and
``_json_deserilalizer``. The names have been corrected, the old names are
accepted with a change warning, and these parameters are now documented as
:paramref:`.create_engine.json_serializer` and
:paramref:`.create_engine.json_deserializer`.
Fixes: #4798
Change-Id: I1dbfe439b421fe9bb7ff3594ef455af8156f8851
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/dialects/sqlite/base.py | 25 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/create.py | 16 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/sqltypes.py | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/suite/test_types.py | 26 |
4 files changed, 85 insertions, 2 deletions
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index ef8507d05..78ce18ac6 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -1426,18 +1426,39 @@ class SQLiteDialect(default.DefaultDialect): _broken_fk_pragma_quotes = False _broken_dotted_colnames = False + @util.deprecated_params( + _json_serializer=( + "1.3.7", + "The _json_serializer argument to the SQLite dialect has " + "been renamed to the correct name of json_serializer. The old " + "argument name will be removed in a future release.", + ), + _json_deserializer=( + "1.3.7", + "The _json_deserializer argument to the SQLite dialect has " + "been renamed to the correct name of json_deserializer. The old " + "argument name will be removed in a future release.", + ), + ) def __init__( self, isolation_level=None, native_datetime=False, + json_serializer=None, + json_deserializer=None, _json_serializer=None, _json_deserializer=None, **kwargs ): default.DefaultDialect.__init__(self, **kwargs) self.isolation_level = isolation_level - self._json_serializer = _json_serializer - self._json_deserializer = _json_deserializer + + if _json_serializer: + json_serializer = _json_serializer + if _json_deserializer: + json_deserializer = _json_deserializer + self._json_serializer = json_serializer + self._json_deserializer = json_deserializer # this flag used by pysqlite dialect, and perhaps others in the # future, to indicate the driver is handling date/timestamp diff --git a/lib/sqlalchemy/engine/create.py b/lib/sqlalchemy/engine/create.py index 035953e99..cc8304131 100644 --- a/lib/sqlalchemy/engine/create.py +++ b/lib/sqlalchemy/engine/create.py @@ -234,6 +234,22 @@ def create_engine(url, **kwargs): :ref:`session_transaction_isolation` - for the ORM + :param json_deserializer: for dialects that support the :class:`.JSON` + datatype, this is a Python callable that will convert a JSON string + to a Python object. By default, the Python ``json.loads`` function is + used. + + .. versionchanged:: 1.3.7 The SQLite dialect renamed this from + ``_json_deserializer``. + + :param json_serializer: for dialects that support the :class:`.JSON` + datatype, this is a Python callable that will render a given object + as JSON. By default, the Python ``json.dumps`` function is used. + + .. versionchanged:: 1.3.7 The SQLite dialect renamed this from + ``_json_serializer``. + + :param label_length=None: optional integer value which limits the size of dynamically generated column labels to that many characters. If less than 6, labels are generated as diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 38731fcbe..631352ceb 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -2042,6 +2042,26 @@ class JSON(Indexable, TypeEngine): values, but care must be taken as to the value of the :paramref:`.JSON.none_as_null` in these cases. + The JSON serializer and deserializer used by :class:`.JSON` defaults to + Python's ``json.dumps`` and ``json.loads`` functions; in the case of the + psycopg2 dialect, psycopg2 may be using its own custom loader function. + + In order to affect the serializer / deserializer, they are currently + configurable at the :func:`.create_engine` level via the + :paramref:`.create_engine.json_serializer` and + :paramref:`.create_engine.json_deserializer` parameters. For example, + to turn off ``ensure_ascii``:: + + engine = create_engine( + "sqlite://", + json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False)) + + .. versionchanged:: 1.3.7 + + SQLite dialect's ``json_serializer`` and ``json_deserializer`` + parameters renamed from ``_json_serializer`` and + ``_json_deserializer``. + .. seealso:: :class:`.postgresql.JSON` diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py index 1e02c0e74..3320dd93c 100644 --- a/lib/sqlalchemy/testing/suite/test_types.py +++ b/lib/sqlalchemy/testing/suite/test_types.py @@ -2,8 +2,12 @@ import datetime import decimal +import json + +import mock from .. import config +from .. import engines from .. import fixtures from ..assertions import eq_ from ..config import requirements @@ -727,6 +731,28 @@ class JSONTest(_LiteralRoundTripFixture, fixtures.TablesTest): eq_(row, (data_element,)) + def test_round_trip_custom_json(self): + data_table = self.tables.data_table + data_element = self.data1 + + js = mock.Mock(side_effect=json.dumps) + jd = mock.Mock(side_effect=json.loads) + engine = engines.testing_engine( + options=dict(json_serializer=js, json_deserializer=jd) + ) + + # support sqlite :memory: database... + data_table.create(engine, checkfirst=True) + engine.execute( + data_table.insert(), {"name": "row1", "data": data_element} + ) + + row = engine.execute(select([data_table.c.data])).first() + + eq_(row, (data_element,)) + eq_(js.mock_calls, [mock.call(data_element)]) + eq_(jd.mock_calls, [mock.call(json.dumps(data_element))]) + def test_round_trip_none_as_sql_null(self): col = self.tables.data_table.c["nulldata"] |
