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