sqlalchemy-serializer-meta 0.3.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlalchemy_serializer_meta-0.3.0/sqlalchemy_serializer_meta.egg-info → sqlalchemy_serializer_meta-1.0.0}/PKG-INFO +26 -11
- sqlalchemy_serializer_meta-1.0.0/pyproject.toml +66 -0
- sqlalchemy_serializer_meta-1.0.0/setup.cfg +4 -0
- sqlalchemy_serializer_meta-1.0.0/setup.py +4 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/__init__.py +52 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/compat.py +14 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/exceptions.py +77 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/fields.py +62 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/serializer.py +703 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/transformers.py +62 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta/validator.py +27 -0
- {sqlalchemy_serializer_meta-0.3.0 → sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta.egg-info}/PKG-INFO +26 -11
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta.egg-info/SOURCES.txt +20 -0
- sqlalchemy_serializer_meta-1.0.0/sqlalchemy_serializer_meta.egg-info/requires.txt +13 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_flask_integration.py +94 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_model_serializer.py +238 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_serializers.py +117 -0
- sqlalchemy_serializer_meta-1.0.0/tests/test_transformers_and_validators.py +96 -0
- sqlalchemy_serializer_meta-0.3.0/setup.cfg +0 -7
- sqlalchemy_serializer_meta-0.3.0/setup.py +0 -26
- sqlalchemy_serializer_meta-0.3.0/sqlalchemy_serializer_meta/__init__.py +0 -426
- sqlalchemy_serializer_meta-0.3.0/sqlalchemy_serializer_meta/serializer.py +0 -199
- sqlalchemy_serializer_meta-0.3.0/sqlalchemy_serializer_meta/validator.py +0 -20
- sqlalchemy_serializer_meta-0.3.0/sqlalchemy_serializer_meta.egg-info/SOURCES.txt +0 -12
- sqlalchemy_serializer_meta-0.3.0/sqlalchemy_serializer_meta.egg-info/requires.txt +0 -3
- {sqlalchemy_serializer_meta-0.3.0 → sqlalchemy_serializer_meta-1.0.0}/LICENSE +0 -0
- {sqlalchemy_serializer_meta-0.3.0 → sqlalchemy_serializer_meta-1.0.0}/README.md +0 -0
- {sqlalchemy_serializer_meta-0.3.0 → sqlalchemy_serializer_meta-1.0.0}/sqlalchemy_serializer_meta.egg-info/dependency_links.txt +0 -0
- {sqlalchemy_serializer_meta-0.3.0 → sqlalchemy_serializer_meta-1.0.0}/sqlalchemy_serializer_meta.egg-info/top_level.txt +0 -0
|
@@ -1,24 +1,39 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemy_serializer_meta
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
5
|
-
|
|
6
|
-
Author: Waheed Khaled Elhariri
|
|
7
|
-
Author-email: waheedkhaled95@gmail.com
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: High-performance DRF-style serializer for SQLAlchemy and Flask models with Pydantic v2 validation
|
|
5
|
+
Author-email: Waheed Khaled Elhariri <waheedkhaled95@gmail.com>
|
|
8
6
|
License: MIT
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Project-URL: Homepage, https://github.com/Wehda23/sqlalchemy_serializer_meta
|
|
8
|
+
Project-URL: Repository, https://github.com/Wehda23/sqlalchemy_serializer_meta
|
|
9
|
+
Project-URL: Issues, https://github.com/Wehda23/sqlalchemy_serializer_meta/issues
|
|
10
|
+
Keywords: sqlalchemy,serializer,pydantic,flask,drf,rest,orm
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Framework :: Flask
|
|
23
|
+
Requires-Python: >=3.9
|
|
17
24
|
Description-Content-Type: text/markdown
|
|
18
25
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: sqlalchemy
|
|
20
|
-
Requires-Dist: pydantic
|
|
21
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: sqlalchemy>=1.4.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: typing_extensions>=4.6.0; python_version < "3.11"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
34
|
+
Requires-Dist: Flask>=2.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: Flask-SQLAlchemy>=3.0.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
22
37
|
|
|
23
38
|
# Serializer Module for Flask Application
|
|
24
39
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sqlalchemy_serializer_meta"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "High-performance DRF-style serializer for SQLAlchemy and Flask models with Pydantic v2 validation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Waheed Khaled Elhariri", email = "waheedkhaled95@gmail.com" }
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
keywords = ["sqlalchemy", "serializer", "pydantic", "flask", "drf", "rest", "orm"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Framework :: Flask",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"sqlalchemy>=1.4.0",
|
|
32
|
+
"pydantic>=2.0.0",
|
|
33
|
+
"typing_extensions>=4.6.0; python_version < '3.11'",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7.4.0",
|
|
39
|
+
"pytest-cov>=4.1.0",
|
|
40
|
+
"ruff>=0.4.0",
|
|
41
|
+
"mypy>=1.10.0",
|
|
42
|
+
"Flask>=2.0.0",
|
|
43
|
+
"Flask-SQLAlchemy>=3.0.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/Wehda23/sqlalchemy_serializer_meta"
|
|
48
|
+
Repository = "https://github.com/Wehda23/sqlalchemy_serializer_meta"
|
|
49
|
+
Issues = "https://github.com/Wehda23/sqlalchemy_serializer_meta/issues"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["."]
|
|
53
|
+
include = ["sqlalchemy_serializer_meta*"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
minversion = "7.0"
|
|
57
|
+
addopts = "-ra -q --strict-markers"
|
|
58
|
+
testpaths = ["tests"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = "py39"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "W", "I"]
|
|
66
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
sqlalchemy_serializer_meta
|
|
5
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
6
|
+
|
|
7
|
+
A fast, flexible DRF-style serializer for SQLAlchemy and Flask models with Pydantic v2 validation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .compat import Self
|
|
11
|
+
from .exceptions import SerializerError, ValidationError
|
|
12
|
+
from .fields import Field, SerializerMethodField
|
|
13
|
+
from .serializer import (
|
|
14
|
+
AbstractSerializer,
|
|
15
|
+
Creatable,
|
|
16
|
+
Deletable,
|
|
17
|
+
ExtendsPydantic,
|
|
18
|
+
ModelSerializer,
|
|
19
|
+
Representable,
|
|
20
|
+
Saveable,
|
|
21
|
+
Serializer,
|
|
22
|
+
SerializerInterface,
|
|
23
|
+
ToInstance,
|
|
24
|
+
Updatable,
|
|
25
|
+
Validatable,
|
|
26
|
+
)
|
|
27
|
+
from .transformers import to_jsonable_python
|
|
28
|
+
from .validator import BaseValidator
|
|
29
|
+
|
|
30
|
+
__version__ = "1.0.0"
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"AbstractSerializer",
|
|
34
|
+
"BaseValidator",
|
|
35
|
+
"Creatable",
|
|
36
|
+
"Deletable",
|
|
37
|
+
"ExtendsPydantic",
|
|
38
|
+
"Field",
|
|
39
|
+
"ModelSerializer",
|
|
40
|
+
"Representable",
|
|
41
|
+
"Saveable",
|
|
42
|
+
"Self",
|
|
43
|
+
"Serializer",
|
|
44
|
+
"SerializerError",
|
|
45
|
+
"SerializerInterface",
|
|
46
|
+
"SerializerMethodField",
|
|
47
|
+
"ToInstance",
|
|
48
|
+
"Updatable",
|
|
49
|
+
"Validatable",
|
|
50
|
+
"ValidationError",
|
|
51
|
+
"to_jsonable_python",
|
|
52
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Compatibility helpers across Python and dependency versions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
if sys.version_info >= (3, 11):
|
|
10
|
+
from typing import Self
|
|
11
|
+
else:
|
|
12
|
+
from typing_extensions import Self
|
|
13
|
+
|
|
14
|
+
__all__ = ["Self"]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Custom exception hierarchy for serializers and validation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SerializerError(Exception):
|
|
11
|
+
"""
|
|
12
|
+
Standard serializer exception.
|
|
13
|
+
|
|
14
|
+
Supports both conventional exception signatures and legacy two-argument signatures:
|
|
15
|
+
- raise SerializerError("Error message")
|
|
16
|
+
- raise SerializerError(ValueError, "Error message")
|
|
17
|
+
- raise SerializerError("Error message", field="email", code="invalid")
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
*args: Any,
|
|
23
|
+
field: Optional[str] = None,
|
|
24
|
+
code: str = "invalid",
|
|
25
|
+
) -> None:
|
|
26
|
+
super().__init__(*args)
|
|
27
|
+
self.field = field
|
|
28
|
+
self.code = code
|
|
29
|
+
self.exception_class: Optional[type] = None
|
|
30
|
+
|
|
31
|
+
if len(args) == 1:
|
|
32
|
+
self.message = str(args[0])
|
|
33
|
+
self.exception_class = self.__class__
|
|
34
|
+
elif len(args) >= 2:
|
|
35
|
+
first, second = args[0], args[1]
|
|
36
|
+
if isinstance(first, type) and issubclass(first, BaseException):
|
|
37
|
+
self.exception_class = first
|
|
38
|
+
self.message = str(second)
|
|
39
|
+
else:
|
|
40
|
+
self.message = str(second)
|
|
41
|
+
self.exception_class = self.__class__
|
|
42
|
+
else:
|
|
43
|
+
self.message = "Serializer error"
|
|
44
|
+
self.exception_class = self.__class__
|
|
45
|
+
|
|
46
|
+
def to_dict(self) -> dict:
|
|
47
|
+
type_name = self.exception_class.__name__ if self.exception_class else self.__class__.__name__
|
|
48
|
+
res = {
|
|
49
|
+
"msg": self.message,
|
|
50
|
+
"type": type_name,
|
|
51
|
+
"code": self.code,
|
|
52
|
+
}
|
|
53
|
+
if self.field:
|
|
54
|
+
res["field"] = self.field
|
|
55
|
+
return res
|
|
56
|
+
|
|
57
|
+
def __str__(self) -> str:
|
|
58
|
+
return self.message
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ValidationError(SerializerError):
|
|
62
|
+
"""
|
|
63
|
+
Validation error raised when data fails schema or field-level constraints.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
detail: Any,
|
|
69
|
+
field: Optional[str] = None,
|
|
70
|
+
code: str = "invalid",
|
|
71
|
+
) -> None:
|
|
72
|
+
if isinstance(detail, (dict, list)):
|
|
73
|
+
self.detail = detail
|
|
74
|
+
super().__init__(str(detail), field=field, code=code)
|
|
75
|
+
else:
|
|
76
|
+
self.detail = str(detail)
|
|
77
|
+
super().__init__(str(detail), field=field, code=code)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Field definitions and special serializer descriptors.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Callable, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Field:
|
|
11
|
+
"""
|
|
12
|
+
Base representation of a serializer field.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
source: Optional[str] = None,
|
|
18
|
+
read_only: bool = False,
|
|
19
|
+
write_only: bool = False,
|
|
20
|
+
required: bool = True,
|
|
21
|
+
default: Any = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.source = source
|
|
24
|
+
self.read_only = read_only
|
|
25
|
+
self.write_only = write_only
|
|
26
|
+
self.required = required
|
|
27
|
+
self.default = default
|
|
28
|
+
self.field_name: Optional[str] = None
|
|
29
|
+
|
|
30
|
+
def bind(self, field_name: str) -> None:
|
|
31
|
+
self.field_name = field_name
|
|
32
|
+
if self.source is None:
|
|
33
|
+
self.source = field_name
|
|
34
|
+
|
|
35
|
+
def get_attribute(self, instance: Any) -> Any:
|
|
36
|
+
if self.source:
|
|
37
|
+
return getattr(instance, self.source, self.default)
|
|
38
|
+
return self.default
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SerializerMethodField(Field):
|
|
42
|
+
"""
|
|
43
|
+
Read-only field that gets its value by calling a method on the serializer class.
|
|
44
|
+
Method defaults to `get_<field_name>(instance)`.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, method_name: Optional[str] = None) -> None:
|
|
48
|
+
super().__init__(read_only=True, required=False)
|
|
49
|
+
self.method_name = method_name
|
|
50
|
+
|
|
51
|
+
def bind(self, field_name: str) -> None:
|
|
52
|
+
super().bind(field_name)
|
|
53
|
+
if self.method_name is None:
|
|
54
|
+
self.method_name = f"get_{field_name}"
|
|
55
|
+
|
|
56
|
+
def to_representation(self, serializer: Any, instance: Any) -> Any:
|
|
57
|
+
method: Optional[Callable] = getattr(serializer, self.method_name, None)
|
|
58
|
+
if method is None or not callable(method):
|
|
59
|
+
raise AttributeError(
|
|
60
|
+
f"Serializer '{serializer.__class__.__name__}' is missing method '{self.method_name}'"
|
|
61
|
+
)
|
|
62
|
+
return method(instance)
|