From e416988b755d533cc0126fe7ee74023cb913f7aa Mon Sep 17 00:00:00 2001 From: Alexander Tikhonov <fatal1ty.rnd@gmail.com> Date: Sun, 19 Nov 2023 21:03:27 +0300 Subject: [PATCH] Rename base codec to basic codec and update README --- README.md | 423 ++++++++++++++---- mashumaro/codecs/__init__.py | 8 +- mashumaro/codecs/{base.py => basic.py} | 12 +- ...test_base_codec.py => test_basic_codec.py} | 27 +- tests/test_data_types.py | 35 +- .../test_dialects.py | 10 +- .../test_parent_by_field.py | 2 +- .../test_parent_via_config.py | 2 +- .../test_union_by_field.py | 18 +- tests/test_exceptions.py | 4 +- tests/test_self.py | 6 +- 11 files changed, 386 insertions(+), 161 deletions(-) rename mashumaro/codecs/{base.py => basic.py} (92%) rename tests/test_codecs/{test_base_codec.py => test_basic_codec.py} (89%) diff --git a/README.md b/README.md index 7fd86de6..b662c3ff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ <img alt="logo" width="175" src="https://raw.githubusercontent.com/Fatal1ty/mashumaro/ac2f924591d488dbd9a776a6b1ae7dede2d8c73e/img/logo.svg"> -###### Fast and well tested serialization library on top of dataclasses +###### Fast and well tested serialization library [![Build Status](https://github.com/Fatal1ty/mashumaro/workflows/tests/badge.svg)](https://github.com/Fatal1ty/mashumaro/actions) [![Coverage Status](https://coveralls.io/repos/github/Fatal1ty/mashumaro/badge.svg?branch=master)](https://coveralls.io/github/Fatal1ty/mashumaro?branch=master) @@ -11,35 +11,37 @@ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) </div> -When using dataclasses, you often need to dump and load objects based on the -schema you have. Mashumaro not only lets you save and load things in different -ways, but it also does it _super quick_. +In Python, you often need to dump and load objects based on the schema you +have. It can be a dataclass model, a list of third-party generic classes or +whatever. Mashumaro not only lets you save and load things in different ways, +but it also does it _super quick_. **Key features** * 🚀 One of the fastest libraries * ☝️ Mature and time-tested * 👶 Easy to use out of the box * ⚙️ Highly customizable -* 🎉 Built-in support for JSON, YAML, MessagePack, TOML +* 🎉 Built-in support for JSON, YAML, TOML, MessagePack * 📦 Built-in support for almost all Python types including typing-extensions * 📝 JSON Schema generation Table of contents --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +* [Introduction](#introduction) * [Installation](#installation) * [Changelog](#changelog) -* [Supported serialization formats](#supported-serialization-formats) * [Supported data types](#supported-data-types) * [Usage example](#usage-example) * [How does it work?](#how-does-it-work) * [Benchmark](#benchmark) -* [Serialization mixins](#serialization-mixins) - * [`DataClassDictMixin`](#dataclassdictmixin) - * [`DataClassJSONMixin`](#dataclassjsonmixin) - * [`DataClassORJSONMixin`](#dataclassorjsonmixin) - * [`DataClassMessagePackMixin`](#dataclassmessagepackmixin) - * [`DataClassYAMLMixin`](#dataclassyamlmixin) - * [`DataClassTOMLMixin`](#dataclasstomlmixin) +* [Supported serialization formats](#supported-serialization-formats) + * [Basic form](#basic-form) + * [JSON](#json) + * [json library](#json-library) + * [orjson library](#orjson-library) + * [YAML](#yaml) + * [TOML](#toml) + * [MessagePack](#messagepack) * [Customization](#customization) * [`SerializableType` interface](#serializabletype-interface) * [User-defined types](#user-defined-types) @@ -101,8 +103,34 @@ Table of contents * [Extending JSON Schema](#extending-json-schema) * [JSON Schema and custom serialization methods](#json-schema-and-custom-serialization-methods) +Introduction +------------------------------------------------------------------------------- + +This library provides two fundamentally different approaches to converting +your data to and from various formats. Each of them is useful in different +situations: + +* Codecs +* Mixins + +Codecs are represented by a set of decoder / encoder classes and +decode / encode functions for each supported format. You can use them +to convert data of any python built-in and third-party type to JSON, YAML, +TOML, MessagePack or a basic form accepted by other serialization formats. +For example, you can convert a list of datetime objects to JSON array +containing string-represented datetimes and vice versa. + +Mixins are primarily for dataclass models. They are represented by mixin +classes that add methods for converting to and from JSON, YAML, TOML, +MessagePack or a basic form accepted by other serialization formats. +If you have a root dataclass model, then it will be the easiest way to make it +serializable. All you have to do is inherit a particular mixin class. + +In addition to serialization functionality, this library also provides JSON +Schema builder that can be used in places where interoperability matters. + Installation --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- Use pip to install: ```shell @@ -126,30 +154,13 @@ of Python. Changelog --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- This project follows the principles of [Semantic Versioning](https://semver.org). Changelog is available on [GitHub Releases page](https://github.com/Fatal1ty/mashumaro/releases). -Supported serialization formats --------------------------------------------------------------------------------- - -This library adds methods for dumping to and loading from the -following formats: - -* [plain dict](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict) -* [JSON](https://www.json.org) -* [YAML](https://yaml.org) -* [MessagePack](https://msgpack.org) -* [TOML](https://toml.io) - -Plain dict can be useful when you need to pass a dict object to a -third-party library, such as a client for MongoDB. - -You can find the documentation for the specific serialization format [below](#serialization-mixins). - Supported data types --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- There is support for generic types from the standard [`typing`](https://docs.python.org/3/library/typing.html) module: * [`List`](https://docs.python.org/3/library/typing.html#typing.List) @@ -265,7 +276,7 @@ for arbitrary types: * [third-party generic types](#third-party-generic-types) Usage example --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- ```python from enum import Enum @@ -309,7 +320,7 @@ Portfolio.from_json(json_string) # same as my_portfolio ``` How does it work? --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- This library works by taking the schema of the data and generating a specific parser and builder for exactly that schema, taking into account the @@ -323,7 +334,7 @@ To minimize the import time, you can explicitly enable [lazy compilation](#lazy_compilation-config-option). Benchmark --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- * macOS 13.3.1 Ventura * Apple M1 @@ -361,126 +372,246 @@ pip install -r requirements-dev.txt ./benchmark/run.sh ``` -Serialization mixins --------------------------------------------------------------------------------- +Supported serialization formats +------------------------------------------------------------------------------- + +This library has built-in support for multiple popular formats: + +* [JSON](https://www.json.org) +* [YAML](https://yaml.org) +* [TOML](https://toml.io) +* [MessagePack](https://msgpack.org) + +There are preconfigured codecs and mixin classes. As for codecs, you are +offered to choose between convenience and efficiency. When you need to decode +or encode structured data more than once, then it's preferable to create +a decoder or encoder specifically for that structure. For one-time use with +default settings it may be convenient to use global functions that create +a disposable decoder or encoder under the hood. + +### Basic form -`mashumaro` provides mixins for each serialization format. +Basic form denotes a python object consisting only of basic data types +supported by most serialization formats. These types are: +[`str`](https://docs.python.org/3/library/stdtypes.html#str), +[`int`](https://docs.python.org/3/library/functions.html#int), +[`float`](https://docs.python.org/3/library/functions.html#float), +[`bool`](https://docs.python.org/3/library/stdtypes.html#bltin-boolean-values), +[`list`](https://docs.python.org/3/library/stdtypes.html#list), +[`dict`](https://docs.python.org/3/library/stdtypes.html#dict). -#### [`DataClassDictMixin`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/dict.py#L14) +This is also a starting point you can play with for a comprehensive +transformation of your data. + +Efficient decoder and encoder can be used as: -Can be imported in two ways: +```python +from mashumaro.codecs import BasicDecoder, BasicEncoder +# or from mashumaro.codecs.basic import BasicDecoder, BasicEncoder + +decoder = BasicDecoder(<shape_type>) +decoder.decode(...) + +encoder = BasicEncoder(<shape_type>) +encoder.encode(...) +``` + +Convenient functions are recommended to be used as: +```python +import mashumaro.codecs.basic as basic_codec + +basic_codec.decode(..., <shape_type>) +basic_codec.encode(..., <shape_type>) +``` + +Mixin can be used as: ```python from mashumaro import DataClassDictMixin -from mashumaro.mixins.dict import DataClassDictMixin +# or from mashumaro.mixins.dict import DataClassDictMixin + +@dataclass +class MyModel(DataClassDictMixin): + ... + +MyModel.from_dict(...) +MyModel(...).to_dict() ``` -The core mixin that adds serialization functionality to a dataclass. -This mixin is a base class for all other serialization format mixins. -It adds methods [`from_dict`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/dict.py#L38-L47) -and [`to_dict`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/dict.py#L27-L36). +> [!TIP]\ +> You don't need to inherit `DataClassDictMixin` along with other serialization +> mixins because it's a base class for them. + +### JSON + +[JSON](https://www.json.org) is a lightweight data-interchange format. You can +choose between standard library +[json](https://docs.python.org/3/library/json.html) for compatibility and +third-party dependency [orjson](https://pypi.org/project/orjson/) for better +performance. -#### [`DataClassJSONMixin`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/json.py#L14) +#### json library -Can be imported as: +Efficient decoder and encoder can be used as: ```python -from mashumaro.mixins.json import DataClassJSONMixin +from mashumaro.codecs.json import JSONDecoder, JSONEncoder + +decoder = JSONDecoder(<shape_type>) +decoder.decode(...) + +encoder = JSONEncoder(<shape_type>) +encoder.encode(...) ``` -This mixin adds json serialization functionality to a dataclass. -It adds methods [`from_json`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/json.py#L24-L31) -and [`to_json`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/json.py#L17-L22). +Convenient functions can be used as : +```python +from mashumaro.codecs.json import json_decode, json_encode -#### [`DataClassORJSONMixin`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/orjson.py#L30) +json_decode(..., <shape_type>) +json_encode(..., <shape_type>) +``` -Can be imported as: +Convenient function aliases are recommended to be used as: ```python -from mashumaro.mixins.orjson import DataClassORJSONMixin +import mashumaro.codecs.json as json_codec + +json_codec.decode(...<shape_type>) +json_codec.encode(..., <shape_type>) ``` -This mixin adds json serialization functionality to a dataclass using -a third-party [`orjson`](https://pypi.org/project/orjson/) library. -It adds methods [`from_json`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/orjson.pyi#L33-L39), -[`to_jsonb`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/orjson.pyi#L19-L25), -[`to_json`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/orjson.pyi#L26-L32). +Mixin can be used as: +```python +from mashumaro.mixins.json import DataClassJSONMixin -In order to use this mixin, the [`orjson`](https://pypi.org/project/orjson/) package must be installed. -You can install it manually or using an extra option for `mashumaro`: +@dataclass +class MyModel(DataClassJSONMixin): + ... + +MyModel.from_json(...) +MyModel(...).to_json() +``` + +#### orjson library + +In order to use [`orjson`](https://pypi.org/project/orjson/) library, it must +be installed manually or using an extra option for `mashumaro`: ```shell pip install mashumaro[orjson] ``` -Using this mixin the following data types will be handled by +The following data types will be handled by [`orjson`](https://pypi.org/project/orjson/) library by default: * [`datetime`](https://docs.python.org/3/library/datetime.html#datetime.datetime) * [`date`](https://docs.python.org/3/library/datetime.html#datetime.date) * [`time`](https://docs.python.org/3/library/datetime.html#datetime.time) * [`uuid.UUID`](https://docs.python.org/3/library/uuid.html#uuid.UUID) -#### [`DataClassMessagePackMixin`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/msgpack.py#L36) - -Can be imported as: +Efficient decoder and encoder can be used as: ```python -from mashumaro.mixins.msgpack import DataClassMessagePackMixin -``` +from mashumaro.codecs.orjson import ORJSONDecoder, ORJSONEncoder -This mixin adds MessagePack serialization functionality to a dataclass. -It adds methods [`from_msgpack`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/msgpack.py#L59-L66) -and [`to_msgpack`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/msgpack.py#L52-L57). +decoder = ORJSONDecoder(<shape_type>) +decoder.decode(...) -In order to use this mixin, the [`msgpack`](https://pypi.org/project/msgpack/) package must be installed. -You can install it manually or using an extra option for `mashumaro`: +encoder = ORJSONEncoder(<shape_type>) +encoder.encode(...) +``` -```shell -pip install mashumaro[msgpack] +Convenient functions can be used as : +```python +from mashumaro.codecs.orjson import json_decode, json_encode + +json_decode(..., <shape_type>) +json_encode(..., <shape_type>) ``` -Using this mixin the following data types will be handled by -[`msgpack`](https://pypi.org/project/msgpack/) library by default: -* [`bytes`](https://docs.python.org/3/library/stdtypes.html#bytes) -* [`bytearray`](https://docs.python.org/3/library/stdtypes.html#bytearray) +Convenient function aliases are recommended to be used as: +```python +import mashumaro.codecs.orjson as json_codec -#### [`DataClassYAMLMixin`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/yaml.py#L27) +json_codec.decode(...<shape_type>) +json_codec.encode(..., <shape_type>) +``` -Can be imported as: +Mixin can be used as: ```python -from mashumaro.mixins.yaml import DataClassYAMLMixin +from mashumaro.mixins.orjson import DataClassORJSONMixin + +@dataclass +class MyModel(DataClassORJSONMixin): + ... + +MyModel.from_json(...) +MyModel(...).to_json() +MyModel(...).to_jsonb() ``` -This mixin adds YAML serialization functionality to a dataclass. -It adds methods [`from_yaml`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/yaml.py#L37-L44) -and [`to_yaml`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/yaml.py#L30-L35). +### YAML -In order to use this mixin, the [`pyyaml`](https://pypi.org/project/PyYAML/) package must be installed. +[YAML](https://yaml.org) is a human-friendly data serialization language for +all programming languages. In order to use this format, the +[`pyyaml`](https://pypi.org/project/PyYAML/) package must be installed. You can install it manually or using an extra option for `mashumaro`: ```shell pip install mashumaro[yaml] ``` -#### [`DataClassTOMLMixin`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/toml.py#L33) +Efficient decoder and encoder can be used as: +```python +from mashumaro.codecs.yaml import YAMLDecoder, YAMLEncoder + +decoder = YAMLDecoder(<shape_type>) +decoder.decode(...) + +encoder = YAMLEncoder(<shape_type>) +encoder.encode(...) +``` -Can be imported as: +Convenient functions can be used as : ```python -from mashumaro.mixins.toml import DataClassTOMLMixin +from mashumaro.codecs.yaml import yaml_decode, yaml_encode + +yaml_decode(..., <shape_type>) +yaml_encode(..., <shape_type>) ``` -This mixin adds TOML serialization functionality to a dataclass. -It adds methods [`from_toml`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/toml.py#L56-L63) -and [`to_toml`](https://github.com/Fatal1ty/mashumaro/blob/master/mashumaro/mixins/toml.py#L49-L54). +Convenient function aliases are recommended to be used as: +```python +import mashumaro.codecs.yaml as yaml_codec + +yaml_codec.decode(...<shape_type>) +yaml_codec.encode(..., <shape_type>) +``` + +Mixin can be used as: +```python +from mashumaro.mixins.yaml import DataClassYAMLMixin + +@dataclass +class MyModel(DataClassYAMLMixin): + ... -In order to use this mixin, the [`tomli`](https://pypi.org/project/tomli/) and +MyModel.from_yaml(...) +MyModel(...).to_yaml() +``` + +### TOML + +[TOML](https://toml.io) is config file format for humans. +In order to use this format, the [`tomli`](https://pypi.org/project/tomli/) and [`tomli-w`](https://pypi.org/project/tomli-w/) packages must be installed. In Python 3.11+, `tomli` is included as [`tomlib`](https://docs.python.org/3/library/tomllib.html) standard library -module and can be used my this mixin. -You can install the missing packages manually or using an extra option for `mashumaro`: +module and is used for this format. You can install the missing packages +manually or using an extra option +for `mashumaro`: ```shell pip install mashumaro[toml] ``` -Using this mixin the following data types will be handled by +The following data types will be handled by [`tomli`](https://pypi.org/project/tomli/)/ [`tomli-w`](https://pypi.org/project/tomli-w/) library by default: * [`datetime`](https://docs.python.org/3/library/datetime.html#datetime.datetime) @@ -489,8 +620,102 @@ Using this mixin the following data types will be handled by Fields with value `None` will be omitted on serialization because TOML doesn't support null values. +Efficient decoder and encoder can be used as: +```python +from mashumaro.codecs.toml import TOMLDecoder, TOMLEncoder + +decoder = TOMLDecoder(<shape_type>) +decoder.decode(...) + +encoder = TOMLEncoder(<shape_type>) +encoder.encode(...) +``` + +Convenient functions can be used as : +```python +from mashumaro.codecs.toml import toml_decode, toml_encode + +toml_decode(..., <shape_type>) +toml_encode(..., <shape_type>) +``` + +Convenient function aliases are recommended to be used as: +```python +import mashumaro.codecs.toml as toml_codec + +toml_codec.decode(...<shape_type>) +toml_codec.encode(..., <shape_type>) +``` + +Mixin can be used as: +```python +from mashumaro.mixins.toml import DataClassTOMLMixin + +@dataclass +class MyModel(DataClassTOMLMixin): + ... + +MyModel.from_toml(...) +MyModel(...).to_toml() +``` + +### MessagePack + +[MessagePack](https://msgpack.org) is an efficient binary serialization format. +In order to use this mixin, the [`msgpack`](https://pypi.org/project/msgpack/) +package must be installed. You can install it manually or using an extra +option for `mashumaro`: + +```shell +pip install mashumaro[msgpack] +``` + +The following data types will be handled by +[`msgpack`](https://pypi.org/project/msgpack/) library by default: +* [`bytes`](https://docs.python.org/3/library/stdtypes.html#bytes) +* [`bytearray`](https://docs.python.org/3/library/stdtypes.html#bytearray) + +Efficient decoder and encoder can be used as: +```python +from mashumaro.codecs.msgpack import MessagePackDecoder, MessagePackEncoder + +decoder = MessagePackDecoder(<shape_type>) +decoder.decode(...) + +encoder = MessagePackEncoder(<shape_type>) +encoder.encode(...) +``` + +Convenient functions can be used as : +```python +from mashumaro.codecs.msgpack import msgpack_decode, msgpack_encode + +msgpack_decode(..., <shape_type>) +msgpack_encode(..., <shape_type>) +``` + +Convenient function aliases are recommended to be used as: +```python +import mashumaro.codecs.msgpack as msgpack_codec + +msgpack_codec.decode(...<shape_type>) +msgpack_codec.encode(..., <shape_type>) +``` + +Mixin can be used as: +```python +from mashumaro.mixins.msgpack import DataClassMessagePackMixin + +@dataclass +class MyModel(DataClassMessagePackMixin): + ... + +MyModel.from_msgpack(...) +MyModel(...).to_msgpack() +``` + Customization --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- Customization options of `mashumaro` are extensive and will most likely cover your needs. When it comes to non-standard data types and non-standard serialization support, you can do the following: @@ -2488,7 +2713,7 @@ Note that you can add an additional `context` argument using the [corresponding](#add-context-keyword-argument) code generation option. JSON Schema --------------------------------------------------------------------------------- +------------------------------------------------------------------------------- You can build JSON Schema not only for dataclasses but also for any other [supported](#supported-data-types) data diff --git a/mashumaro/codecs/__init__.py b/mashumaro/codecs/__init__.py index 1755102a..69c322e0 100644 --- a/mashumaro/codecs/__init__.py +++ b/mashumaro/codecs/__init__.py @@ -1,8 +1,6 @@ -from .base import Decoder, Encoder, decode, encode +from .basic import BasicDecoder, BasicEncoder __all__ = [ - "Decoder", - "Encoder", - "decode", - "encode", + "BasicDecoder", + "BasicEncoder", ] diff --git a/mashumaro/codecs/base.py b/mashumaro/codecs/basic.py similarity index 92% rename from mashumaro/codecs/base.py rename to mashumaro/codecs/basic.py index bbb99159..e40dd396 100644 --- a/mashumaro/codecs/base.py +++ b/mashumaro/codecs/basic.py @@ -17,7 +17,7 @@ T = TypeVar("T") -class Decoder(Generic[T]): +class BasicDecoder(Generic[T]): @overload def __init__( self, @@ -55,7 +55,7 @@ def decode(self, data: Any) -> T: # pragma: no cover ... -class Encoder(Generic[T]): +class BasicEncoder(Generic[T]): @overload def __init__( self, @@ -94,16 +94,16 @@ def encode(self, obj: T) -> Any: # pragma: no cover def decode(data: Any, shape_type: Union[Type[T], Any]) -> T: - return Decoder(shape_type).decode(data) + return BasicDecoder(shape_type).decode(data) def encode(obj: T, shape_type: Union[Type[T], Any]) -> Any: - return Encoder(shape_type).encode(obj) + return BasicEncoder(shape_type).encode(obj) __all__ = [ - "Decoder", - "Encoder", + "BasicDecoder", + "BasicEncoder", "decode", "encode", ] diff --git a/tests/test_codecs/test_base_codec.py b/tests/test_codecs/test_basic_codec.py similarity index 89% rename from tests/test_codecs/test_base_codec.py rename to tests/test_codecs/test_basic_codec.py index d135f2f4..cc0de5dc 100644 --- a/tests/test_codecs/test_base_codec.py +++ b/tests/test_codecs/test_basic_codec.py @@ -5,7 +5,8 @@ import pytest from typing_extensions import Literal -from mashumaro.codecs import Decoder, Encoder, decode, encode +from mashumaro.codecs import BasicDecoder, BasicEncoder +from mashumaro.codecs.basic import decode, encode from mashumaro.dialect import Dialect from tests.entities import ( DataClassWithoutMixin, @@ -119,7 +120,7 @@ def test_encode(shape_type, encoded, decoded): def test_decoder_with_default_dialect(): - decoder = Decoder(List[date], default_dialect=MyDialect) + decoder = BasicDecoder(List[date], default_dialect=MyDialect) assert decoder.decode([738785, 738786]) == [ date(2023, 9, 22), date(2023, 9, 23), @@ -127,7 +128,7 @@ def test_decoder_with_default_dialect(): def test_encoder_with_default_dialect(): - encoder = Encoder(List[date], default_dialect=MyDialect) + encoder = BasicEncoder(List[date], default_dialect=MyDialect) assert encoder.encode([date(2023, 9, 22), date(2023, 9, 23)]) == [ 738785, 738786, @@ -135,7 +136,7 @@ def test_encoder_with_default_dialect(): def test_pre_decoder_func(): - decoder = Decoder(List[date], pre_decoder_func=lambda v: v.split(",")) + decoder = BasicDecoder(List[date], pre_decoder_func=lambda v: v.split(",")) assert decoder.decode("2023-09-22,2023-09-23") == [ date(2023, 9, 22), date(2023, 9, 23), @@ -143,7 +144,7 @@ def test_pre_decoder_func(): def test_post_encoder_func(): - encoder = Encoder(List[date], post_encoder_func=lambda v: ",".join(v)) + encoder = BasicEncoder(List[date], post_encoder_func=lambda v: ",".join(v)) assert ( encoder.encode( [ @@ -160,7 +161,7 @@ def test_post_encoder_func(): [[Union[date, datetime], "foo"], [Literal["foo"], "bar"]], ) def test_value_error_on_decode(shape_type, invalid_value): - decoder = Decoder(shape_type) + decoder = BasicDecoder(shape_type) with pytest.raises(ValueError) as e: decoder.decode(invalid_value) assert type(e.value) is ValueError @@ -171,7 +172,7 @@ def test_value_error_on_decode(shape_type, invalid_value): [[Union[date, datetime], "foo"], [Literal["foo"], "bar"]], ) def test_value_error_on_encode(shape_type, invalid_value): - encoder = Encoder(shape_type) + encoder = BasicEncoder(shape_type) with pytest.raises(ValueError) as e: encoder.encode(invalid_value) assert type(e.value) is ValueError @@ -189,8 +190,8 @@ class MyClass: l1: Literal["l1"] l2: Literal["l2"] - decoder = Decoder(MyClass) - encoder = Encoder(MyClass) + decoder = BasicDecoder(MyClass) + encoder = BasicEncoder(MyClass) data = { "td1": {"x": "2023-11-15", "y": 1}, "td2": {"x": "2023-11-15", "y": 2}, @@ -221,8 +222,8 @@ class MyClass: x1: Foo x2: Bar - decoder = Decoder(MyClass) - encoder = Encoder(MyClass) + decoder = BasicDecoder(MyClass) + encoder = BasicEncoder(MyClass) data = {"x1": {"foo": "foo"}, "x2": {"bar": "bar"}} obj = MyClass(x1=Foo("foo"), x2=Bar("bar")) assert decoder.decode(data) == obj @@ -235,8 +236,8 @@ class MyClass: x1: MyGenericDataClass[str] x2: MyGenericDataClass[date] - decoder = Decoder(MyClass) - encoder = Encoder(MyClass) + decoder = BasicDecoder(MyClass) + encoder = BasicEncoder(MyClass) data = {"x1": {"x": "2023-11-15"}, "x2": {"x": "2023-11-15"}} obj = MyClass( x1=MyGenericDataClass("2023-11-15"), diff --git a/tests/test_data_types.py b/tests/test_data_types.py index c60b84be..6055ca9c 100644 --- a/tests/test_data_types.py +++ b/tests/test_data_types.py @@ -43,7 +43,8 @@ from typing_extensions import Final, LiteralString from mashumaro import DataClassDictMixin -from mashumaro.codecs import Decoder, Encoder, decode, encode +from mashumaro.codecs import BasicDecoder, BasicEncoder +from mashumaro.codecs.basic import decode, encode from mashumaro.config import BaseConfig from mashumaro.core.const import PEP_585_COMPATIBLE, PY_39_MIN from mashumaro.exceptions import ( @@ -1223,8 +1224,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": ["1", "2.0"]}) == obj assert obj.to_dict() == {"x": [1, 2.0]} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": ["1", "2.0"]}) == obj assert encoder.encode(obj) == {"x": [1, 2.0]} @@ -1238,8 +1239,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": ["1"]}) == obj assert obj.to_dict() == {"x": [1, 2.0]} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": ["1"]}) == obj assert encoder.encode(obj) == {"x": [1, 2.0]} @@ -1253,8 +1254,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": ["1", "2.0"]}) == obj assert obj.to_dict() == {"x": ["1", "2.0"]} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": ["1", "2.0"]}) == obj assert encoder.encode(obj) == {"x": ["1", "2.0"]} @@ -1380,8 +1381,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": {"x": 33, "y": 42}}) == obj assert obj.to_dict() == {"x": {"x": 33, "y": 42}} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": {"x": 33, "y": 42}}) == obj assert encoder.encode(obj) == {"x": {"x": 33, "y": 42}} @@ -1399,8 +1400,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": {"x": 33, "y": 42}}) == obj assert obj.to_dict() == {"x": {"x": 33, "y": 42}} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": {"x": 33, "y": 42}}) == obj assert encoder.encode(obj) == {"x": {"x": 33, "y": 42}} @@ -1414,8 +1415,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": {"x": "2023-01-22", "y": "42"}}) == obj assert obj.to_dict() == {"x": {"x": "2023-01-22", "y": 42}} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": {"x": "2023-01-22", "y": "42"}}) == obj assert encoder.encode(obj) == {"x": {"x": "2023-01-22", "y": 42}} @@ -1429,8 +1430,8 @@ class DataClass(DataClassDictMixin): assert DataClass.from_dict({"x": {"x": "2023-01-22", "y": "42"}}) == obj assert obj.to_dict() == {"x": {"x": "2023-01-22", "y": 42}} - decoder = Decoder(DataClass) - encoder = Encoder(DataClass) + decoder = BasicDecoder(DataClass) + encoder = BasicEncoder(DataClass) assert decoder.decode({"x": {"x": "2023-01-22", "y": "42"}}) == obj assert encoder.encode(obj) == {"x": {"x": "2023-01-22", "y": 42}} @@ -1503,7 +1504,7 @@ class Config(BaseConfig): @pytest.mark.parametrize("value_info", inner_values) def test_decoder(value_info): x_type, x_value, x_value_dumped = value_info - decoder = Decoder(x_type) + decoder = BasicDecoder(x_type) assert decoder.decode(x_value_dumped) == x_value assert decode(x_value_dumped, x_type) == x_value @@ -1511,6 +1512,6 @@ def test_decoder(value_info): @pytest.mark.parametrize("value_info", inner_values) def test_encoder(value_info): x_type, x_value, x_value_dumped = value_info - encoder = Encoder(x_type) + encoder = BasicEncoder(x_type) assert encoder.encode(x_value) == x_value_dumped assert encode(x_value, x_type) == x_value_dumped diff --git a/tests/test_discriminated_unions/test_dialects.py b/tests/test_discriminated_unions/test_dialects.py index 0ba646de..ab966cdf 100644 --- a/tests/test_discriminated_unions/test_dialects.py +++ b/tests/test_discriminated_unions/test_dialects.py @@ -4,7 +4,7 @@ from typing_extensions import Annotated, Literal -from mashumaro.codecs import Decoder +from mashumaro.codecs import BasicDecoder from mashumaro.codecs.orjson import ORJSONDecoder from mashumaro.config import ADD_DIALECT_SUPPORT, BaseConfig from mashumaro.dialect import Dialect @@ -199,7 +199,7 @@ def test_passing_dialect_to_config_based_variant_subtypes(): assert Variant1.from_dict( {"type": 1, "x": "2023-06-03", "y": [["1", 2], ["3", 4]]} ) == Variant1Subtype1(date(2023, 6, 3), {"1": 2, "3": 4}) - decoder1 = Decoder(_Variant1, default_dialect=DefaultDialect) + decoder1 = BasicDecoder(_Variant1, default_dialect=DefaultDialect) assert decoder1.decode( {"type": 1, "x": "2023-06-03", "y": [["1", 2], ["3", 4]]} ) == _Variant1Subtype1(date(2023, 6, 3), {"1": 2, "3": 4}) @@ -215,7 +215,7 @@ def test_passing_dialect_to_config_based_variant_subtypes(): assert Variant1.from_dict( {"type": 1, "x": 738674, "y": {"1": 2, "3": 4}}, dialect=MyDialect ) == Variant1Subtype1(date(2023, 6, 3), {"1": 2, "3": 4}) - decoder3 = Decoder(_Variant1, default_dialect=MyDialect) + decoder3 = BasicDecoder(_Variant1, default_dialect=MyDialect) assert decoder3.decode( {"type": 1, "x": 738674, "y": {"1": 2, "3": 4}} ) == _Variant1Subtype1(date(2023, 6, 3), {"1": 2, "3": 4}) @@ -269,7 +269,7 @@ def test_passing_dialect_to_annotation_based_variant_subtypes(): assert Variant2Wrapper.from_dict( {"x": {"type": 1, "x": "2023-06-03", "y": [["1", 2], ["3", 4]]}} ) == Variant2Wrapper(Variant2Subtype1(date(2023, 6, 3), {"1": 2, "3": 4})) - decoder1 = Decoder(_Variant2Wrapper) + decoder1 = BasicDecoder(_Variant2Wrapper) assert decoder1.decode( {"x": {"type": 1, "x": "2023-06-03", "y": [["1", 2], ["3", 4]]}} ) == _Variant2Wrapper( @@ -341,7 +341,7 @@ def test_passing_dialect_to_annotation_based_union_subtypes(): assert Variant34Wrapper.from_dict( {"x": {"type": 3, "x": "2023-06-03", "y": [["1", 2], ["3", 4]]}} ) == Variant34Wrapper(Variant3Subtype(date(2023, 6, 3), {"1": 2, "3": 4})) - decoder1 = Decoder(_Variant34Wrapper) + decoder1 = BasicDecoder(_Variant34Wrapper) assert decoder1.decode( {"x": {"type": 3, "x": "2023-06-03", "y": [["1", 2], ["3", 4]]}} ) == _Variant34Wrapper( diff --git a/tests/test_discriminated_unions/test_parent_by_field.py b/tests/test_discriminated_unions/test_parent_by_field.py index 450e5e50..cc076ef7 100644 --- a/tests/test_discriminated_unions/test_parent_by_field.py +++ b/tests/test_discriminated_unions/test_parent_by_field.py @@ -5,7 +5,7 @@ from typing_extensions import Annotated, Literal from mashumaro import DataClassDictMixin -from mashumaro.codecs import decode +from mashumaro.codecs.basic import decode from mashumaro.exceptions import InvalidFieldValue from mashumaro.types import Discriminator diff --git a/tests/test_discriminated_unions/test_parent_via_config.py b/tests/test_discriminated_unions/test_parent_via_config.py index 1c2279a9..6b9dd90e 100644 --- a/tests/test_discriminated_unions/test_parent_via_config.py +++ b/tests/test_discriminated_unions/test_parent_via_config.py @@ -6,7 +6,7 @@ from typing_extensions import Literal from mashumaro import DataClassDictMixin -from mashumaro.codecs import decode, encode +from mashumaro.codecs.basic import decode from mashumaro.config import BaseConfig from mashumaro.exceptions import ( InvalidFieldValue, diff --git a/tests/test_discriminated_unions/test_union_by_field.py b/tests/test_discriminated_unions/test_union_by_field.py index c615848c..ad8db133 100644 --- a/tests/test_discriminated_unions/test_union_by_field.py +++ b/tests/test_discriminated_unions/test_union_by_field.py @@ -7,7 +7,7 @@ from typing_extensions import Annotated, Final, Literal from mashumaro import DataClassDictMixin -from mashumaro.codecs import Decoder +from mashumaro.codecs import BasicDecoder from mashumaro.exceptions import InvalidFieldValue from mashumaro.types import Discriminator @@ -342,7 +342,7 @@ class VariantWitCustomTaggerOwner( def test_by_field_with_supertypes(): - decoder = Decoder(_ByFieldWithSupertypes) + decoder = BasicDecoder(_ByFieldWithSupertypes) for func, cls in ( (ByFieldWithSupertypes.from_dict, ByFieldWithSupertypes), @@ -391,7 +391,7 @@ def test_by_field_with_supertypes(): def test_by_field_with_subtypes(): - decoder = Decoder(_ByFieldWithSubtypes) + decoder = BasicDecoder(_ByFieldWithSubtypes) for func, cls in ( (ByFieldWithSubtypes.from_dict, ByFieldWithSubtypes), @@ -441,7 +441,7 @@ def test_by_field_with_subtypes(): def test_by_field_with_supertypes_and_subtypes(): - decoder = Decoder(_ByFieldWithSupertypesAndSubtypes) + decoder = BasicDecoder(_ByFieldWithSupertypesAndSubtypes) for func, cls in ( ( @@ -514,7 +514,7 @@ def test_by_field_with_supertypes_and_subtypes(): def test_by_supertypes(): - decoder = Decoder(_BySupertypes) + decoder = BasicDecoder(_BySupertypes) for func, cls in ( (BySupertypes.from_dict, BySupertypes), @@ -573,7 +573,7 @@ def test_by_supertypes(): def test_by_subtypes(): - decoder = Decoder(_BySubtypes) + decoder = BasicDecoder(_BySubtypes) for func, cls in ( (BySubtypes.from_dict, BySubtypes), @@ -632,7 +632,7 @@ def test_by_subtypes(): def test_by_supertypes_and_subtypes(): - decoder = Decoder(_BySupertypesAndSubtypes) + decoder = BasicDecoder(_BySupertypesAndSubtypes) for func, cls in ( (BySupertypesAndSubtypes.from_dict, BySupertypesAndSubtypes), @@ -702,7 +702,7 @@ def test_by_supertypes_and_subtypes(): def test_tuple_with_discriminated_elements(): - decoder = Decoder(_ByFieldAndByFieldWithSubtypesInOneField) + decoder = BasicDecoder(_ByFieldAndByFieldWithSubtypesInOneField) for func, cls in ( ( @@ -734,7 +734,7 @@ def test_tuple_with_discriminated_elements(): def test_by_field_with_subtypes_with_custom_variant_tagger(): - decoder = Decoder(_VariantWitCustomTaggerOwner) + decoder = BasicDecoder(_VariantWitCustomTaggerOwner) for func, cls in ( (VariantWitCustomTaggerOwner.from_dict, VariantWitCustomTaggerOwner), diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 5fe0ffa1..a87030ff 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -4,7 +4,7 @@ import pytest from mashumaro import DataClassDictMixin -from mashumaro.codecs import Decoder +from mashumaro.codecs import BasicDecoder from mashumaro.core.meta.helpers import type_name from mashumaro.exceptions import ( InvalidFieldValue, @@ -183,7 +183,7 @@ class MyClass(DataClassDictMixin): f"__mashumaro_from_dict__ method should be a dict instance" ) - decoder = Decoder(MyClass) + decoder = BasicDecoder(MyClass) with pytest.raises(ValueError) as exc_info: decoder.decode(42) assert str(exc_info.value) == ( diff --git a/tests/test_self.py b/tests/test_self.py index c11b5938..90380ee1 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -5,7 +5,7 @@ from typing_extensions import Self from mashumaro import DataClassDictMixin -from mashumaro.codecs import Decoder, Encoder +from mashumaro.codecs import BasicDecoder, BasicEncoder from mashumaro.codecs.orjson import ORJSONDecoder, ORJSONEncoder from mashumaro.mixins.orjson import DataClassORJSONMixin @@ -45,8 +45,8 @@ def test_dataclass_dict_with_self(): def test_dataclass_dict_with_self_without_mixin(): - decoder = Decoder(DataClassDictWithoutMixin) - encoder = Encoder(DataClassDictWithoutMixin) + decoder = BasicDecoder(DataClassDictWithoutMixin) + encoder = BasicEncoder(DataClassDictWithoutMixin) obj = DataClassDictWithoutMixin(DataClassDictWithoutMixin()) assert encoder.encode(obj) == {"next": {"next": None}} assert decoder.decode({"next": {"next": None}}) == obj