enumplus 1.0.0__tar.gz → 1.1.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.
Files changed (31) hide show
  1. {enumplus-1.0.0 → enumplus-1.1.0}/PKG-INFO +1 -1
  2. {enumplus-1.0.0 → enumplus-1.1.0}/enumplus/__init__.py +1 -1
  3. {enumplus-1.0.0 → enumplus-1.1.0}/enumplus/enum.py +95 -10
  4. {enumplus-1.0.0 → enumplus-1.1.0}/enumplus/pydantic.py +13 -1
  5. {enumplus-1.0.0 → enumplus-1.1.0}/enumplus/serialize.py +9 -1
  6. {enumplus-1.0.0 → enumplus-1.1.0}/pyproject.toml +1 -1
  7. enumplus-1.1.0/tests/test_new_features.py +304 -0
  8. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_public_api.py +1 -1
  9. enumplus-1.1.0/tests/test_pydantic.py +79 -0
  10. enumplus-1.0.0/tests/test_pydantic.py +0 -43
  11. {enumplus-1.0.0 → enumplus-1.1.0}/.github/FUNDING.yml +0 -0
  12. {enumplus-1.0.0 → enumplus-1.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {enumplus-1.0.0 → enumplus-1.1.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  14. {enumplus-1.0.0 → enumplus-1.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {enumplus-1.0.0 → enumplus-1.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {enumplus-1.0.0 → enumplus-1.1.0}/.github/workflows/ci.yml +0 -0
  17. {enumplus-1.0.0 → enumplus-1.1.0}/.github/workflows/release.yml +0 -0
  18. {enumplus-1.0.0 → enumplus-1.1.0}/.gitignore +0 -0
  19. {enumplus-1.0.0 → enumplus-1.1.0}/CODE_OF_CONDUCT.md +0 -0
  20. {enumplus-1.0.0 → enumplus-1.1.0}/CONTRIBUTING.md +0 -0
  21. {enumplus-1.0.0 → enumplus-1.1.0}/LICENSE +0 -0
  22. {enumplus-1.0.0 → enumplus-1.1.0}/README.md +0 -0
  23. {enumplus-1.0.0 → enumplus-1.1.0}/SECURITY.md +0 -0
  24. {enumplus-1.0.0 → enumplus-1.1.0}/SUPPORT.md +0 -0
  25. {enumplus-1.0.0 → enumplus-1.1.0}/tests/__init__.py +0 -0
  26. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_compatibility.py +0 -0
  27. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_enum.py +0 -0
  28. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_metadata_unpacking.py +0 -0
  29. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_ordered.py +0 -0
  30. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_serialize.py +0 -0
  31. {enumplus-1.0.0 → enumplus-1.1.0}/tests/test_type_hints.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: enumplus
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Enhanced Python enums with metadata, serialization, and choices
5
5
  Author-email: Mathias Paulenko <mathias.paulenko@outlook.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  from enumplus.enum import Enum, OrderedEnum
2
2
  from enumplus.serialize import SerializableEncoder
3
3
 
4
- __version__ = "0.1.0"
4
+ __version__ = "1.1.0"
5
5
 
6
6
  __all__ = ["Enum", "OrderedEnum", "SerializableEncoder", "__version__"]
@@ -27,6 +27,21 @@ class EnumMeta(enum.EnumMeta):
27
27
  member_names: list[str] = list(getattr(namespace, "_member_names", []))
28
28
  last_values: list[Any] = getattr(namespace, "_last_values", [])
29
29
 
30
+ class_config: dict[str, Any] = {}
31
+ _config_keys = {"serialize_by_name"}
32
+
33
+ ns_member_names = getattr(namespace, "_member_names", None)
34
+
35
+ for key in _config_keys:
36
+ if key in namespace:
37
+ class_config[key] = namespace[key]
38
+ dict.__delitem__(namespace, key)
39
+ if ns_member_names is not None and key in ns_member_names:
40
+ if isinstance(ns_member_names, dict):
41
+ del ns_member_names[key]
42
+ else:
43
+ ns_member_names.remove(key)
44
+
30
45
  for key, value in list(namespace.items()):
31
46
  if isinstance(value, tuple) and len(value) == 2 and isinstance(value[1], dict):
32
47
  actual_value, metadata = value
@@ -38,6 +53,12 @@ class EnumMeta(enum.EnumMeta):
38
53
 
39
54
  new_cls = super().__new__(cls, name, bases, namespace, **kwargs)
40
55
 
56
+ for key, value in class_config.items():
57
+ setattr(new_cls, key, value)
58
+
59
+ if not hasattr(new_cls, "serialize_by_name"):
60
+ new_cls.serialize_by_name = False
61
+
41
62
  members: list[Any] = list(new_cls)
42
63
  for index, member in enumerate(members):
43
64
  metadata = member_metadata.get(member.name, {})
@@ -53,6 +74,8 @@ class EnumMeta(enum.EnumMeta):
53
74
 
54
75
  return new_cls
55
76
 
77
+ serialize_by_name: bool = False
78
+
56
79
  def __contains__(cls, item: Any) -> bool:
57
80
  if isinstance(item, cls):
58
81
  return True
@@ -72,7 +95,10 @@ class Enum(enum.Enum, metaclass=EnumMeta):
72
95
 
73
96
  @property
74
97
  def label(self) -> str:
75
- return self._label_
98
+ label = self._label_
99
+ if callable(label):
100
+ return str(label())
101
+ return str(label)
76
102
 
77
103
  @property
78
104
  def metadata(self) -> dict[str, Any]:
@@ -89,7 +115,7 @@ class Enum(enum.Enum, metaclass=EnumMeta):
89
115
  ) from None
90
116
 
91
117
  def __str__(self) -> str:
92
- return self._label_
118
+ return self.label
93
119
 
94
120
  def __repr__(self) -> str:
95
121
  return f"<{type(self).__name__}.{self.name}: {self.value!r}>"
@@ -107,20 +133,36 @@ class Enum(enum.Enum, metaclass=EnumMeta):
107
133
  return [(member.value, member.label) for member in cls]
108
134
 
109
135
  @classmethod
110
- def from_value(cls, value: Any, default: Any = _SENTINEL) -> Enum:
136
+ def from_value(
137
+ cls, value: Any, default: Any = _SENTINEL, *, case_insensitive: bool = False
138
+ ) -> Enum:
111
139
  member: Any
112
- for member in cls:
113
- if member.value == value:
114
- return cast(Enum, member)
140
+ if case_insensitive and isinstance(value, str):
141
+ lowered = value.lower()
142
+ for member in cls:
143
+ if isinstance(member.value, str) and member.value.lower() == lowered:
144
+ return cast(Enum, member)
145
+ else:
146
+ for member in cls:
147
+ if member.value == value:
148
+ return cast(Enum, member)
115
149
  if default is not _SENTINEL:
116
150
  return cast(Enum, default)
117
151
  raise ValueError(f"{value!r} is not a valid {cls.__name__} value")
118
152
 
119
153
  @classmethod
120
- def from_name(cls, name: str, default: Any = _SENTINEL) -> Enum:
121
- member = cls.__members__.get(name)
122
- if member is not None:
123
- return member
154
+ def from_name(
155
+ cls, name: str, default: Any = _SENTINEL, *, case_insensitive: bool = False
156
+ ) -> Enum:
157
+ if case_insensitive:
158
+ upper = name.upper()
159
+ for member in cls:
160
+ if member.name.upper() == upper:
161
+ return member
162
+ else:
163
+ found = cls.__members__.get(name)
164
+ if found is not None:
165
+ return found
124
166
  if default is not _SENTINEL:
125
167
  return cast(Enum, default)
126
168
  raise KeyError(f"{name!r} is not a valid {cls.__name__} name")
@@ -149,6 +191,49 @@ class Enum(enum.Enum, metaclass=EnumMeta):
149
191
  def labels(cls) -> list[str]:
150
192
  return [member.label for member in cls]
151
193
 
194
+ @classmethod
195
+ def keys(cls) -> list[str]:
196
+ return [member.name for member in cls]
197
+
198
+ @classmethod
199
+ def get(cls, value: Any, default: Any = None) -> Enum | None:
200
+ return cls.from_value(value, default=default)
201
+
202
+ @classmethod
203
+ def get_initial(cls) -> Enum:
204
+ members = list(cls)
205
+ if not members:
206
+ raise ValueError(f"{cls.__name__} has no members")
207
+ return members[0]
208
+
209
+ @classmethod
210
+ def get_final(cls) -> Enum:
211
+ members = list(cls)
212
+ if not members:
213
+ raise ValueError(f"{cls.__name__} has no members")
214
+ return members[-1]
215
+
216
+ @classmethod
217
+ def map(cls, mapping: dict[Enum, Any]) -> dict[str, Any]:
218
+ result: dict[str, Any] = {}
219
+ for member in cls:
220
+ result[member.name] = mapping.get(member, None)
221
+ return result
222
+
223
+ @classmethod
224
+ def to_dict(cls) -> dict[str, dict[str, Any]]:
225
+ result: dict[str, dict[str, Any]] = {}
226
+ for member in cls:
227
+ result[member.name] = {
228
+ "value": member.value,
229
+ "label": member.label,
230
+ "metadata": {
231
+ k: (v() if callable(v) else v)
232
+ for k, v in member._metadata_.items()
233
+ },
234
+ }
235
+ return result
236
+
152
237
  @classmethod
153
238
  def filter(cls, **kwargs: Any) -> list[Enum]:
154
239
  if not kwargs:
@@ -14,16 +14,28 @@ def get_pydantic_core_schema(
14
14
  ) -> CoreSchema:
15
15
  from pydantic_core import core_schema
16
16
 
17
+ serialize_by_name = getattr(cls, "serialize_by_name", False)
18
+
17
19
  def validate(value: Any) -> Any:
18
20
  if isinstance(value, cls):
19
21
  return value
22
+ if serialize_by_name and isinstance(value, str):
23
+ try:
24
+ return cls.from_name(value)
25
+ except KeyError:
26
+ raise ValueError(f"{value!r} is not a valid {cls.__name__} name") from None
20
27
  return cls.from_value(value)
21
28
 
29
+ if serialize_by_name:
30
+ serializer = lambda v: v.name
31
+ else:
32
+ serializer = lambda v: v.value
33
+
22
34
  return core_schema.no_info_after_validator_function(
23
35
  validate,
24
36
  core_schema.any_schema(),
25
37
  serialization=core_schema.plain_serializer_function_ser_schema(
26
- lambda v: v.value,
38
+ serializer,
27
39
  return_schema=core_schema.any_schema(),
28
40
  ),
29
41
  )
@@ -6,6 +6,12 @@ from typing import Any, cast
6
6
  from enumplus.enum import Enum
7
7
 
8
8
 
9
+ def _serialize_value(value: Any) -> Any:
10
+ if callable(value):
11
+ return value()
12
+ return value
13
+
14
+
9
15
  def to_json(cls: type[Enum]) -> str:
10
16
  data: dict[str, Any] = {
11
17
  "name": cls.__name__,
@@ -14,7 +20,9 @@ def to_json(cls: type[Enum]) -> str:
14
20
  "name": member.name,
15
21
  "value": member.value,
16
22
  "label": member.label,
17
- "metadata": member._metadata_,
23
+ "metadata": {
24
+ k: _serialize_value(v) for k, v in member._metadata_.items()
25
+ },
18
26
  }
19
27
  for member in cls
20
28
  ],
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "enumplus"
7
- version = "1.0.0"
7
+ version = "1.1.0"
8
8
  description = "Enhanced Python enums with metadata, serialization, and choices"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,304 @@
1
+ from __future__ import annotations
2
+
3
+ from enumplus import Enum
4
+
5
+
6
+ class TestCaseInsensitive:
7
+ def test_from_value_case_insensitive(self) -> None:
8
+ class Color(Enum):
9
+ RED = "red"
10
+ GREEN = "green"
11
+
12
+ assert Color.from_value("Red", case_insensitive=True) is Color.RED
13
+ assert Color.from_value("GREEN", case_insensitive=True) is Color.GREEN
14
+ assert Color.from_value("rEd", case_insensitive=True) is Color.RED
15
+
16
+ def test_from_value_case_insensitive_not_found(self) -> None:
17
+ class Color(Enum):
18
+ RED = "red"
19
+
20
+ try:
21
+ Color.from_value("blue", case_insensitive=True)
22
+ raise AssertionError("Should have raised ValueError")
23
+ except ValueError:
24
+ pass
25
+
26
+ def test_from_value_case_insensitive_with_default(self) -> None:
27
+ class Color(Enum):
28
+ RED = "red"
29
+
30
+ assert Color.from_value("Blue", case_insensitive=True, default=None) is None
31
+
32
+ def test_from_name_case_insensitive(self) -> None:
33
+ class Color(Enum):
34
+ RED = "red"
35
+ GREEN = "green"
36
+
37
+ assert Color.from_name("red", case_insensitive=True) is Color.RED
38
+ assert Color.from_name("green", case_insensitive=True) is Color.GREEN
39
+ assert Color.from_name("ReD", case_insensitive=True) is Color.RED
40
+
41
+ def test_from_name_case_insensitive_not_found(self) -> None:
42
+ class Color(Enum):
43
+ RED = "red"
44
+
45
+ try:
46
+ Color.from_name("blue", case_insensitive=True)
47
+ raise AssertionError("Should have raised KeyError")
48
+ except KeyError:
49
+ pass
50
+
51
+ def test_from_value_case_insensitive_non_string(self) -> None:
52
+ class Number(Enum):
53
+ ONE = 1
54
+ TWO = 2
55
+
56
+ assert Number.from_value(1, case_insensitive=True) is Number.ONE
57
+ assert Number.from_value(2, case_insensitive=True) is Number.TWO
58
+
59
+
60
+ class TestToDict:
61
+ def test_to_dict_basic(self) -> None:
62
+ class Color(Enum):
63
+ RED = "red"
64
+ GREEN = "green"
65
+
66
+ d = Color.to_dict()
67
+ assert "RED" in d
68
+ assert d["RED"]["value"] == "red"
69
+ assert d["RED"]["label"] == "Red"
70
+ assert d["RED"]["metadata"] == {}
71
+ assert d["GREEN"]["value"] == "green"
72
+ assert d["GREEN"]["label"] == "Green"
73
+
74
+ def test_to_dict_with_metadata(self) -> None:
75
+ class Color(Enum):
76
+ RED = ("red", {"hex": "#FF0000"})
77
+ GREEN = "green"
78
+
79
+ d = Color.to_dict()
80
+ assert d["RED"]["metadata"] == {"hex": "#FF0000"}
81
+ assert d["RED"]["label"] == "Red"
82
+ assert d["GREEN"]["metadata"] == {}
83
+
84
+ def test_to_dict_with_custom_label(self) -> None:
85
+ class Status(Enum):
86
+ IN_PROGRESS = ("in_progress", {"label": "In Progress"})
87
+
88
+ d = Status.to_dict()
89
+ assert d["IN_PROGRESS"]["label"] == "In Progress"
90
+
91
+
92
+ class TestGet:
93
+ def test_get_returns_member(self) -> None:
94
+ class Color(Enum):
95
+ RED = "red"
96
+
97
+ assert Color.get("red") is Color.RED
98
+
99
+ def test_get_returns_default(self) -> None:
100
+ class Color(Enum):
101
+ RED = "red"
102
+
103
+ assert Color.get("blue", default=None) is None
104
+ assert Color.get("blue", default=Color.RED) is Color.RED
105
+
106
+ def test_get_default_is_none(self) -> None:
107
+ class Color(Enum):
108
+ RED = "red"
109
+
110
+ assert Color.get("blue") is None
111
+
112
+
113
+ class TestGetInitial:
114
+ def test_get_initial(self) -> None:
115
+ class Color(Enum):
116
+ RED = "red"
117
+ GREEN = "green"
118
+ BLUE = "blue"
119
+
120
+ assert Color.get_initial() is Color.RED
121
+
122
+ def test_get_initial_single_member(self) -> None:
123
+ class Single(Enum):
124
+ ONLY = 1
125
+
126
+ assert Single.get_initial() is Single.ONLY
127
+
128
+ def test_get_initial_empty_raises(self) -> None:
129
+ class Empty(Enum):
130
+ pass
131
+
132
+ try:
133
+ Empty.get_initial()
134
+ raise AssertionError("Should have raised ValueError")
135
+ except ValueError:
136
+ pass
137
+
138
+
139
+ class TestGetFinal:
140
+ def test_get_final(self) -> None:
141
+ class Color(Enum):
142
+ RED = "red"
143
+ GREEN = "green"
144
+ BLUE = "blue"
145
+
146
+ assert Color.get_final() is Color.BLUE
147
+
148
+ def test_get_final_single_member(self) -> None:
149
+ class Single(Enum):
150
+ ONLY = 1
151
+
152
+ assert Single.get_final() is Single.ONLY
153
+
154
+ def test_get_final_empty_raises(self) -> None:
155
+ class Empty(Enum):
156
+ pass
157
+
158
+ try:
159
+ Empty.get_final()
160
+ raise AssertionError("Should have raised ValueError")
161
+ except ValueError:
162
+ pass
163
+
164
+
165
+ class TestKeys:
166
+ def test_keys_returns_names(self) -> None:
167
+ class Color(Enum):
168
+ RED = "red"
169
+ GREEN = "green"
170
+
171
+ assert Color.keys() == ["RED", "GREEN"]
172
+
173
+ def test_keys_empty(self) -> None:
174
+ class Empty(Enum):
175
+ pass
176
+
177
+ assert Empty.keys() == []
178
+
179
+ def test_keys_equals_names(self) -> None:
180
+ class Color(Enum):
181
+ RED = "red"
182
+ GREEN = "green"
183
+
184
+ assert Color.keys() == Color.names()
185
+
186
+
187
+ class TestMap:
188
+ def test_map_basic(self) -> None:
189
+ class Color(Enum):
190
+ RED = "red"
191
+ GREEN = "green"
192
+
193
+ result = Color.map({Color.RED: "#FF0000", Color.GREEN: "#00FF00"})
194
+ assert result == {"RED": "#FF0000", "GREEN": "#00FF00"}
195
+
196
+ def test_map_partial(self) -> None:
197
+ class Color(Enum):
198
+ RED = "red"
199
+ GREEN = "green"
200
+
201
+ result = Color.map({Color.RED: "#FF0000"})
202
+ assert result == {"RED": "#FF0000", "GREEN": None}
203
+
204
+ def test_map_empty(self) -> None:
205
+ class Color(Enum):
206
+ RED = "red"
207
+
208
+ result = Color.map({})
209
+ assert result == {"RED": None}
210
+
211
+
212
+ class TestI18nLabels:
213
+ def test_callable_label(self) -> None:
214
+ translations = {"RED": "Rojo", "GREEN": "Verde"}
215
+
216
+ def translate(key: str) -> str:
217
+ return translations.get(key, key)
218
+
219
+ class Color(Enum):
220
+ RED = ("red", {"label": lambda: translate("RED")})
221
+ GREEN = ("green", {"label": lambda: translate("GREEN")})
222
+
223
+ assert Color.RED.label == "Rojo"
224
+ assert Color.GREEN.label == "Verde"
225
+
226
+ def test_callable_label_changes_with_locale(self) -> None:
227
+ current_locale: dict[str, str] = {"lang": "en"}
228
+ en = {"RED": "Red", "GREEN": "Green"}
229
+ es = {"RED": "Rojo", "GREEN": "Verde"}
230
+
231
+ def translate(key: str) -> str:
232
+ lang = current_locale["lang"]
233
+ table = es if lang == "es" else en
234
+ return table.get(key, key)
235
+
236
+ class Color(Enum):
237
+ RED = ("red", {"label": lambda: translate("RED")})
238
+ GREEN = ("green", {"label": lambda: translate("GREEN")})
239
+
240
+ assert Color.RED.label == "Red"
241
+ current_locale["lang"] = "es"
242
+ assert Color.RED.label == "Rojo"
243
+ assert Color.GREEN.label == "Verde"
244
+ current_locale["lang"] = "en"
245
+ assert Color.RED.label == "Red"
246
+
247
+ def test_non_callable_label_unchanged(self) -> None:
248
+ class Color(Enum):
249
+ RED = ("red", {"label": "Red"})
250
+
251
+ assert Color.RED.label == "Red"
252
+
253
+ def test_callable_label_in_choices(self) -> None:
254
+ class Color(Enum):
255
+ RED = ("red", {"label": lambda: "Rojo"})
256
+
257
+ assert Color.choices() == [("red", "Rojo")]
258
+
259
+ def test_callable_label_in_to_dict(self) -> None:
260
+ class Color(Enum):
261
+ RED = ("red", {"label": lambda: "Rojo"})
262
+
263
+ d = Color.to_dict()
264
+ assert d["RED"]["label"] == "Rojo"
265
+
266
+ def test_callable_label_in_str(self) -> None:
267
+ class Color(Enum):
268
+ RED = ("red", {"label": lambda: "Rojo"})
269
+
270
+ assert str(Color.RED) == "Rojo"
271
+
272
+ def test_callable_label_in_labels(self) -> None:
273
+ class Color(Enum):
274
+ RED = ("red", {"label": lambda: "Rojo"})
275
+ GREEN = ("green", {"label": lambda: "Verde"})
276
+
277
+ assert Color.labels() == ["Rojo", "Verde"]
278
+
279
+ def test_callable_label_in_to_json(self) -> None:
280
+ class Color(Enum):
281
+ RED = ("red", {"label": lambda: "Rojo"})
282
+
283
+ import json
284
+
285
+ data = json.loads(Color.to_json())
286
+ assert data["members"][0]["label"] == "Rojo"
287
+
288
+
289
+ class TestSerializeByName:
290
+ def test_flag_default_false(self) -> None:
291
+ class Color(Enum):
292
+ RED = "red"
293
+ GREEN = "green"
294
+
295
+ assert Color.serialize_by_name is False
296
+
297
+ def test_flag_set_true(self) -> None:
298
+ class Color(Enum):
299
+ RED = "red"
300
+ GREEN = "green"
301
+
302
+ serialize_by_name = True
303
+
304
+ assert Color.serialize_by_name is True
@@ -15,4 +15,4 @@ def test_all_exports() -> None:
15
15
 
16
16
 
17
17
  def test_version() -> None:
18
- assert enumplus.__version__ == "0.1.0"
18
+ assert enumplus.__version__ == "1.1.0"
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+
5
+ pytest.importorskip("pydantic")
6
+
7
+ from pydantic import BaseModel, ValidationError
8
+
9
+ from enumplus import Enum
10
+
11
+
12
+ class Color(Enum):
13
+ RED = "red"
14
+ GREEN = "green"
15
+
16
+
17
+ class MyModel(BaseModel):
18
+ color: Color
19
+
20
+
21
+ def test_pydantic_validate_string() -> None:
22
+ model = MyModel(color="red") # type: ignore[arg-type]
23
+ assert model.color is Color.RED
24
+
25
+
26
+ def test_pydantic_validate_member() -> None:
27
+ model = MyModel(color=Color.RED)
28
+ assert model.color is Color.RED
29
+
30
+
31
+ def test_pydantic_invalid() -> None:
32
+ with pytest.raises(ValidationError):
33
+ MyModel(color="blue") # type: ignore[arg-type]
34
+
35
+
36
+ def test_pydantic_dump() -> None:
37
+ model = MyModel(color=Color.RED)
38
+ assert model.model_dump() == {"color": "red"}
39
+
40
+
41
+ def test_pydantic_dump_json() -> None:
42
+ model = MyModel(color=Color.RED)
43
+ assert model.model_dump_json() == '{"color":"red"}'
44
+
45
+
46
+ class ColorByName(Enum):
47
+ RED = "red"
48
+ GREEN = "green"
49
+
50
+ serialize_by_name = True
51
+
52
+
53
+ class MyModelByName(BaseModel):
54
+ color: ColorByName
55
+
56
+
57
+ def test_pydantic_serialize_by_name_validate() -> None:
58
+ model = MyModelByName(color="RED") # type: ignore[arg-type]
59
+ assert model.color is ColorByName.RED
60
+
61
+
62
+ def test_pydantic_serialize_by_name_validate_member() -> None:
63
+ model = MyModelByName(color=ColorByName.RED)
64
+ assert model.color is ColorByName.RED
65
+
66
+
67
+ def test_pydantic_serialize_by_name_invalid() -> None:
68
+ with pytest.raises(ValidationError):
69
+ MyModelByName(color="blue") # type: ignore[arg-type]
70
+
71
+
72
+ def test_pydantic_serialize_by_name_dump() -> None:
73
+ model = MyModelByName(color=ColorByName.RED)
74
+ assert model.model_dump() == {"color": "RED"}
75
+
76
+
77
+ def test_pydantic_serialize_by_name_dump_json() -> None:
78
+ model = MyModelByName(color=ColorByName.RED)
79
+ assert model.model_dump_json() == '{"color":"RED"}'
@@ -1,43 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import pytest
4
-
5
- pytest.importorskip("pydantic")
6
-
7
- from pydantic import BaseModel, ValidationError
8
-
9
- from enumplus import Enum
10
-
11
-
12
- class Color(Enum):
13
- RED = "red"
14
- GREEN = "green"
15
-
16
-
17
- class MyModel(BaseModel):
18
- color: Color
19
-
20
-
21
- def test_pydantic_validate_string() -> None:
22
- model = MyModel(color="red") # type: ignore[arg-type]
23
- assert model.color is Color.RED
24
-
25
-
26
- def test_pydantic_validate_member() -> None:
27
- model = MyModel(color=Color.RED)
28
- assert model.color is Color.RED
29
-
30
-
31
- def test_pydantic_invalid() -> None:
32
- with pytest.raises(ValidationError):
33
- MyModel(color="blue") # type: ignore[arg-type]
34
-
35
-
36
- def test_pydantic_dump() -> None:
37
- model = MyModel(color=Color.RED)
38
- assert model.model_dump() == {"color": "red"}
39
-
40
-
41
- def test_pydantic_dump_json() -> None:
42
- model = MyModel(color=Color.RED)
43
- assert model.model_dump_json() == '{"color":"red"}'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes