stdl 0.6.4__tar.gz → 0.6.6__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 (43) hide show
  1. {stdl-0.6.4 → stdl-0.6.6}/PKG-INFO +3 -1
  2. {stdl-0.6.4 → stdl-0.6.6}/pyproject.toml +12 -3
  3. {stdl-0.6.4 → stdl-0.6.6}/stdl/__init__.py +1 -0
  4. {stdl-0.6.4 → stdl-0.6.6}/stdl/color.py +252 -77
  5. {stdl-0.6.4 → stdl-0.6.6}/stdl/fs.py +30 -15
  6. {stdl-0.6.4 → stdl-0.6.6}/tests/test_pydantic_integration.py +56 -4
  7. {stdl-0.6.4 → stdl-0.6.6}/uv.lock +160 -2
  8. {stdl-0.6.4 → stdl-0.6.6}/.editorconfig +0 -0
  9. {stdl-0.6.4 → stdl-0.6.6}/.github/workflows/lint.yml +0 -0
  10. {stdl-0.6.4 → stdl-0.6.6}/.github/workflows/publish.yml +0 -0
  11. {stdl-0.6.4 → stdl-0.6.6}/.github/workflows/test.yml +0 -0
  12. {stdl-0.6.4 → stdl-0.6.6}/.gitignore +0 -0
  13. {stdl-0.6.4 → stdl-0.6.6}/.pre-commit-config.yaml +0 -0
  14. {stdl-0.6.4 → stdl-0.6.6}/.python-version +0 -0
  15. {stdl-0.6.4 → stdl-0.6.6}/.readthedocs.yml +0 -0
  16. {stdl-0.6.4 → stdl-0.6.6}/CONTRIBUTING.md +0 -0
  17. {stdl-0.6.4 → stdl-0.6.6}/Justfile +0 -0
  18. {stdl-0.6.4 → stdl-0.6.6}/LICENSE +0 -0
  19. {stdl-0.6.4 → stdl-0.6.6}/README.md +0 -0
  20. {stdl-0.6.4 → stdl-0.6.6}/docs/index.md +0 -0
  21. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/color.md +0 -0
  22. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/decorators.md +0 -0
  23. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/dt.md +0 -0
  24. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/fs.md +0 -0
  25. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/import_lazy.md +0 -0
  26. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/log.md +0 -0
  27. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/net.md +0 -0
  28. {stdl-0.6.4 → stdl-0.6.6}/docs/reference/st.md +0 -0
  29. {stdl-0.6.4 → stdl-0.6.6}/docs/requirements.txt +0 -0
  30. {stdl-0.6.4 → stdl-0.6.6}/mkdocs.yml +0 -0
  31. {stdl-0.6.4 → stdl-0.6.6}/stdl/decorators.py +0 -0
  32. {stdl-0.6.4 → stdl-0.6.6}/stdl/dt.py +0 -0
  33. {stdl-0.6.4 → stdl-0.6.6}/stdl/import_lazy.py +0 -0
  34. {stdl-0.6.4 → stdl-0.6.6}/stdl/log.py +0 -0
  35. {stdl-0.6.4 → stdl-0.6.6}/stdl/lst.py +0 -0
  36. {stdl-0.6.4 → stdl-0.6.6}/stdl/net.py +0 -0
  37. {stdl-0.6.4 → stdl-0.6.6}/stdl/st.py +0 -0
  38. {stdl-0.6.4 → stdl-0.6.6}/tests/test_color.py +0 -0
  39. {stdl-0.6.4 → stdl-0.6.6}/tests/test_decorators.py +0 -0
  40. {stdl-0.6.4 → stdl-0.6.6}/tests/test_dt.py +0 -0
  41. {stdl-0.6.4 → stdl-0.6.6}/tests/test_fs.py +0 -0
  42. {stdl-0.6.4 → stdl-0.6.6}/tests/test_lst.py +0 -0
  43. {stdl-0.6.4 → stdl-0.6.6}/tests/test_st.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stdl
3
- Version: 0.6.4
3
+ Version: 0.6.6
4
4
  Summary: Extended Python Standard Library
5
5
  Project-URL: Repository, https://github.com/zigai/stdl
6
6
  Project-URL: Issues, https://github.com/zigai/stdl/issues
@@ -52,10 +52,12 @@ Requires-Dist: codespell; extra == 'dev'
52
52
  Requires-Dist: coverage; extra == 'dev'
53
53
  Requires-Dist: mypy; extra == 'dev'
54
54
  Requires-Dist: pre-commit; extra == 'dev'
55
+ Requires-Dist: pydantic>=2; extra == 'dev'
55
56
  Requires-Dist: pytest; extra == 'dev'
56
57
  Requires-Dist: ruff; extra == 'dev'
57
58
  Provides-Extra: test
58
59
  Requires-Dist: coverage; extra == 'test'
60
+ Requires-Dist: pydantic>=2; extra == 'test'
59
61
  Requires-Dist: pytest; extra == 'test'
60
62
  Description-Content-Type: text/markdown
61
63
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stdl"
3
- version = "0.6.4"
3
+ version = "0.6.6"
4
4
  description = "Extended Python Standard Library"
5
5
  authors = [{ name = "Žiga Ivanšek", email = "ziga.ivansek@gmail.com" }]
6
6
  license = { file = "LICENSE" }
@@ -47,9 +47,17 @@ keywords = [
47
47
 
48
48
 
49
49
  [project.optional-dependencies]
50
- test = ["pytest", "coverage"]
51
- dev = ["pytest", "coverage", "ruff", "pre-commit", "mypy", "codespell"]
50
+ dev = [
51
+ "pytest",
52
+ "coverage",
53
+ "ruff",
54
+ "pre-commit",
55
+ "mypy",
56
+ "codespell",
57
+ "pydantic>=2",
58
+ ]
52
59
 
60
+ test = ["pytest", "coverage", "pydantic>=2"]
53
61
  [project.urls]
54
62
  Repository = "https://github.com/zigai/stdl"
55
63
  Issues = "https://github.com/zigai/stdl/issues"
@@ -71,6 +79,7 @@ select = [
71
79
  "E", # Pycodestyle (Error)
72
80
  'I', # isort
73
81
  'D', # pydocstyle
82
+ 'D213', # pydocstyle
74
83
  'UP', # pyupgrade
75
84
  'YTT', # flake8-2020
76
85
  'B', # flake8-bugbear
@@ -1 +1,2 @@
1
1
  from stdl import color, decorators, dt, fs, import_lazy, log, lst, net, st
2
+ from stdl.fs import Directory, File
@@ -90,7 +90,7 @@ class RGB(Color):
90
90
  return (self.red, self.green, self.blue)
91
91
 
92
92
  def RGB(self) -> RGB:
93
- return self.copy()
93
+ return self.copy() # type:ignore
94
94
 
95
95
  def HSV(self) -> HSV:
96
96
  r, g, b = self.red / 255, self.green / 255, self.blue / 255
@@ -197,7 +197,7 @@ class RGBA(Color):
197
197
  return RGB(self.red, self.green, self.blue)
198
198
 
199
199
  def RGBA(self) -> RGBA:
200
- return self.copy()
200
+ return self.copy() # type:ignore
201
201
 
202
202
  def HSV(self) -> HSV:
203
203
  return self.RGB().HSV()
@@ -266,7 +266,7 @@ class HEX(Color):
266
266
  return self.RGB().CMYK()
267
267
 
268
268
  def HEX(self) -> HEX:
269
- return self.copy()
269
+ return self.copy() # type:ignore
270
270
 
271
271
  def ASSA(self) -> ASSA:
272
272
  return self.RGB().ASSA()
@@ -329,7 +329,7 @@ class HSV(Color):
329
329
  return RGB(round((r + m) * 255), round((g + m) * 255), round((b + m) * 255))
330
330
 
331
331
  def HSV(self) -> HSV:
332
- return self.copy()
332
+ return self.copy() # type:ignore
333
333
 
334
334
  def HSL(self) -> HSL:
335
335
  return self.RGB().HSL()
@@ -415,7 +415,7 @@ class HSL(Color):
415
415
  return self.RGB().HSV()
416
416
 
417
417
  def HSL(self) -> HSL:
418
- return self.copy()
418
+ return self.copy() # type:ignore
419
419
 
420
420
  def CMYK(self) -> CMYK:
421
421
  return self.RGB().CMYK()
@@ -477,7 +477,7 @@ class CMYK(Color):
477
477
  return self.RGB().HSL()
478
478
 
479
479
  def CMYK(self) -> CMYK:
480
- return self.copy()
480
+ return self.copy() # type:ignore
481
481
 
482
482
  def HEX(self) -> HEX:
483
483
  return self.RGB().HEX()
@@ -587,7 +587,7 @@ class ASSA(Color):
587
587
  return self.RGB().HEX()
588
588
 
589
589
  def ASSA(self) -> ASSA:
590
- return self.copy()
590
+ return self.copy() # type:ignore
591
591
 
592
592
  def embed_text(self, text: str) -> str:
593
593
  """Embed text with this color in ASS format"""
@@ -637,7 +637,7 @@ class ASSA(Color):
637
637
  return "".join(c for c in value.upper() if c in "0123456789ABCDEF")
638
638
 
639
639
 
640
- CSS_COLOR_NAME = T.Literal[
640
+ CssColorName = T.Literal[
641
641
  "aliceblue",
642
642
  "antiquewhite",
643
643
  "aqua",
@@ -795,7 +795,7 @@ CSS_COLOR_TO_HEX = {
795
795
  "aquamarine": "#7FFFD4",
796
796
  "azure": "#F0FFFF",
797
797
  "beige": "#F5F5DC",
798
- "bisque": "#FFE4C4x",
798
+ "bisque": "#FFE4C4",
799
799
  "black": "#000000",
800
800
  "blanchedalmond": "#FFEBCD",
801
801
  "blue": "#0000FF",
@@ -943,7 +943,7 @@ CSS_COLOR_TO_HEX = {
943
943
  class webcolor(Color):
944
944
  COLORS = CSS_COLOR_TO_HEX
945
945
 
946
- def __init__(self, name: CSS_COLOR_NAME):
946
+ def __init__(self, name: CssColorName):
947
947
  name = name.lower() # type:ignore
948
948
  self.name = name
949
949
  self.validate()
@@ -983,7 +983,7 @@ class webcolor(Color):
983
983
  return self.hex_value
984
984
 
985
985
  def webcolor(self) -> webcolor:
986
- return self.copy()
986
+ return self.copy() # type:ignore
987
987
 
988
988
  def ASSA(self) -> ASSA:
989
989
  return self.hex_value.ASSA()
@@ -1010,7 +1010,7 @@ def hex_to_rgb(color: str) -> tuple[int, int, int]:
1010
1010
  def normalize_color(
1011
1011
  color: (
1012
1012
  str
1013
- | CSS_COLOR_NAME
1013
+ | CssColorName
1014
1014
  | tuple[int, int, int]
1015
1015
  | tuple[int, int, int, float]
1016
1016
  | Sequence[int]
@@ -1034,30 +1034,16 @@ def normalize_color(
1034
1034
  raise ColorValueError(f"Unsupported color format: {color}")
1035
1035
 
1036
1036
 
1037
- __all__ = [
1038
- "Color",
1039
- "RGB",
1040
- "RGBA",
1041
- "HEX",
1042
- "HSV",
1043
- "HSL",
1044
- "CMYK",
1045
- "ASSA",
1046
- "webcolor",
1047
- "CSS_COLOR_TO_HEX",
1048
- "CSS_COLOR_NAME",
1049
- "ColorValueError",
1050
- "normalize_color",
1051
- ]
1052
-
1053
1037
  try:
1054
1038
  from pydantic import GetCoreSchemaHandler as _GetCoreSchemaHandler
1039
+ from pydantic import GetJsonSchemaHandler as _GetJsonSchemaHandler
1040
+ from pydantic.json_schema import JsonSchemaValue as _JsonSchemaValue
1055
1041
  from pydantic_core import core_schema as _core_schema
1056
1042
  except Exception: # pragma: no cover
1057
1043
  pass # Pydantic not installed
1058
1044
  else:
1059
1045
 
1060
- def _parse_rgb(value: str | Mapping[str, T.Any]) -> RGB:
1046
+ def parse_rgb(value: str | Mapping[str, T.Any]) -> RGB:
1061
1047
  if isinstance(value, Mapping):
1062
1048
  data = dict(value)
1063
1049
  if {"red", "green", "blue"}.issubset(data):
@@ -1075,7 +1061,7 @@ else:
1075
1061
  r, g, b = (int(match.group(1)), int(match.group(2)), int(match.group(3)))
1076
1062
  return RGB(r, g, b)
1077
1063
 
1078
- def _parse_rgba(value: str | Mapping[str, T.Any]) -> RGBA:
1064
+ def parse_rgba(value: str | Mapping[str, T.Any]) -> RGBA:
1079
1065
  if isinstance(value, Mapping):
1080
1066
  data = dict(value)
1081
1067
  if {"red", "green", "blue", "alpha"}.issubset(data):
@@ -1095,7 +1081,7 @@ else:
1095
1081
  a = float(match.group(4))
1096
1082
  return RGBA(r, g, b, a)
1097
1083
 
1098
- def _parse_hex(value: str | Mapping[str, T.Any]) -> HEX:
1084
+ def parse_hex(value: str | Mapping[str, T.Any]) -> HEX:
1099
1085
  if isinstance(value, Mapping):
1100
1086
  data = dict(value)
1101
1087
  if "value" in data:
@@ -1113,7 +1099,7 @@ else:
1113
1099
 
1114
1100
  return HEX(v)
1115
1101
 
1116
- def _parse_hsv(value: str | Mapping[str, T.Any]) -> HSV:
1102
+ def parse_hsv(value: str | Mapping[str, T.Any]) -> HSV:
1117
1103
  if isinstance(value, Mapping):
1118
1104
  data = dict(value)
1119
1105
  if {"hue", "saturation", "value"}.issubset(data):
@@ -1132,7 +1118,7 @@ else:
1132
1118
  h, s, v = float(match.group(1)), float(match.group(2)), float(match.group(3))
1133
1119
  return HSV(h, s, v)
1134
1120
 
1135
- def _parse_hsl(value: str | Mapping[str, T.Any]) -> HSL:
1121
+ def parse_hsl(value: str | Mapping[str, T.Any]) -> HSL:
1136
1122
  if isinstance(value, Mapping):
1137
1123
  data = dict(value)
1138
1124
  if {"hue", "saturation", "lightness"}.issubset(data):
@@ -1151,7 +1137,7 @@ else:
1151
1137
  h, s, l = float(match.group(1)), float(match.group(2)), float(match.group(3))
1152
1138
  return HSL(h, s, l)
1153
1139
 
1154
- def _parse_cmyk(value: str | Mapping[str, T.Any]) -> CMYK:
1140
+ def parse_cmyk(value: str | Mapping[str, T.Any]) -> CMYK:
1155
1141
  if isinstance(value, Mapping):
1156
1142
  data = dict(value)
1157
1143
  if {"cyan", "magenta", "yellow", "key"}.issubset(data):
@@ -1167,15 +1153,15 @@ else:
1167
1153
  if not match:
1168
1154
  raise ValueError(f"Invalid CMYK repr: {value!r}")
1169
1155
 
1170
- c, m_, y, k = (
1156
+ c, m, y, k = (
1171
1157
  float(match.group(1)),
1172
1158
  float(match.group(2)),
1173
1159
  float(match.group(3)),
1174
1160
  float(match.group(4)),
1175
1161
  )
1176
- return CMYK(c, m_, y, k)
1162
+ return CMYK(c, m, y, k)
1177
1163
 
1178
- def _parse_assa(value: str | Mapping[str, T.Any]) -> ASSA:
1164
+ def parse_assa(value: str | Mapping[str, T.Any]) -> ASSA:
1179
1165
  if isinstance(value, Mapping):
1180
1166
  data = dict(value)
1181
1167
  if "value" in data:
@@ -1190,7 +1176,7 @@ else:
1190
1176
 
1191
1177
  return ASSA(match.group(1))
1192
1178
 
1193
- def _parse_webcolor(value: str | Mapping[str, T.Any]) -> webcolor:
1179
+ def parse_webcolor(value: str | Mapping[str, T.Any]) -> webcolor:
1194
1180
  if isinstance(value, Mapping):
1195
1181
  data = dict(value)
1196
1182
  if "name" in data:
@@ -1203,54 +1189,243 @@ else:
1203
1189
 
1204
1190
  return webcolor(match.group(2))
1205
1191
 
1206
- def _make_schema(_cls, _parser):
1192
+ def _string_schema(
1193
+ *,
1194
+ title: str,
1195
+ description: str | None = None,
1196
+ pattern: str | None = None,
1197
+ examples: list[str] | None = None,
1198
+ ) -> _JsonSchemaValue:
1199
+ schema: dict[str, T.Any] = {"type": "string", "title": title}
1200
+ if description:
1201
+ schema["description"] = description
1202
+ if pattern:
1203
+ schema["pattern"] = pattern
1204
+ if examples:
1205
+ schema["examples"] = examples
1206
+ return schema
1207
+
1208
+ def _object_schema(
1209
+ *,
1210
+ title: str,
1211
+ description: str | None,
1212
+ properties: dict[str, T.Any],
1213
+ required: list[str],
1214
+ ) -> _JsonSchemaValue:
1215
+ return {
1216
+ "type": "object",
1217
+ "title": title,
1218
+ "description": description,
1219
+ "properties": properties,
1220
+ "required": required,
1221
+ "additionalProperties": False,
1222
+ }
1223
+
1224
+ def _register_color(
1225
+ cls: type[Color],
1226
+ parser: T.Callable[[str | Mapping[str, T.Any]], Color],
1227
+ *,
1228
+ title: str,
1229
+ string_description: str,
1230
+ string_pattern: str,
1231
+ string_examples: list[str],
1232
+ object_description: str,
1233
+ object_properties: dict[str, T.Any],
1234
+ object_required: list[str],
1235
+ ) -> None:
1236
+ string_schema = _string_schema(
1237
+ title=title,
1238
+ description=string_description,
1239
+ pattern=string_pattern,
1240
+ examples=string_examples,
1241
+ )
1242
+ object_schema = _object_schema(
1243
+ title=title,
1244
+ description=object_description,
1245
+ properties=object_properties,
1246
+ required=object_required,
1247
+ )
1248
+
1207
1249
  def _validator(v):
1208
- if isinstance(v, _cls):
1250
+ if isinstance(v, cls):
1209
1251
  return v
1210
1252
  if isinstance(v, str):
1211
- return _parser(v)
1253
+ return parser(v)
1212
1254
  if isinstance(v, Mapping):
1213
- return _parser(v)
1214
- raise TypeError(
1215
- f"Expected {_cls.__name__} instance, repr string, or mapping, got {type(v).__name__}"
1255
+ return parser(v)
1256
+ raise ValueError(
1257
+ f"Expected {cls.__name__} instance, repr string, or mapping, got {type(v).__name__}"
1216
1258
  )
1217
1259
 
1218
- return _core_schema.no_info_plain_validator_function(
1260
+ core_schema = _core_schema.no_info_plain_validator_function(
1219
1261
  _validator,
1220
1262
  serialization=_core_schema.plain_serializer_function_ser_schema(
1221
1263
  lambda v: repr(v), when_used="json"
1222
1264
  ),
1223
1265
  )
1224
1266
 
1225
- def _rgb_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1226
- return _make_schema(RGB, _parse_rgb)
1227
-
1228
- def _rgba_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1229
- return _make_schema(RGBA, _parse_rgba)
1230
-
1231
- def _hex_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1232
- return _make_schema(HEX, _parse_hex)
1233
-
1234
- def _hsv_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1235
- return _make_schema(HSV, _parse_hsv)
1236
-
1237
- def _hsl_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1238
- return _make_schema(HSL, _parse_hsl)
1239
-
1240
- def _cmyk_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1241
- return _make_schema(CMYK, _parse_cmyk)
1242
-
1243
- def _assa_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1244
- return _make_schema(ASSA, _parse_assa)
1245
-
1246
- def _webcolor_schema(cls, _source, _handler: _GetCoreSchemaHandler):
1247
- return _make_schema(webcolor, _parse_webcolor)
1248
-
1249
- RGB.__get_pydantic_core_schema__ = classmethod(_rgb_schema) # type: ignore[attr-defined]
1250
- RGBA.__get_pydantic_core_schema__ = classmethod(_rgba_schema) # type: ignore[attr-defined]
1251
- HEX.__get_pydantic_core_schema__ = classmethod(_hex_schema) # type: ignore[attr-defined]
1252
- HSV.__get_pydantic_core_schema__ = classmethod(_hsv_schema) # type: ignore[attr-defined]
1253
- HSL.__get_pydantic_core_schema__ = classmethod(_hsl_schema) # type: ignore[attr-defined]
1254
- CMYK.__get_pydantic_core_schema__ = classmethod(_cmyk_schema) # type: ignore[attr-defined]
1255
- ASSA.__get_pydantic_core_schema__ = classmethod(_assa_schema) # type: ignore[attr-defined]
1256
- webcolor.__get_pydantic_core_schema__ = classmethod(_webcolor_schema) # type: ignore[attr-defined]
1267
+ def _core_schema_factory(_cls, _source, _handler: _GetCoreSchemaHandler):
1268
+ return core_schema
1269
+
1270
+ def _json_schema_factory(
1271
+ _cls,
1272
+ _core_schema_obj: _core_schema.CoreSchema,
1273
+ handler: _GetJsonSchemaHandler,
1274
+ ) -> _JsonSchemaValue:
1275
+ return {
1276
+ "title": title,
1277
+ "anyOf": [
1278
+ handler.resolve_ref_schema(string_schema),
1279
+ handler.resolve_ref_schema(object_schema),
1280
+ ],
1281
+ }
1282
+
1283
+ cls.__get_pydantic_core_schema__ = classmethod(_core_schema_factory) # type: ignore[attr-defined]
1284
+ cls.__get_pydantic_json_schema__ = classmethod(_json_schema_factory) # type: ignore[attr-defined]
1285
+
1286
+ _register_color(
1287
+ RGB,
1288
+ parse_rgb,
1289
+ title="RGB",
1290
+ string_description="String representation rgb(r, g, b) with 0-255 integer channels",
1291
+ string_pattern=r"^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$",
1292
+ string_examples=["rgb(255, 0, 0)", "rgb(0, 0, 0)"],
1293
+ object_description="Object with integer channels 0-255",
1294
+ object_properties={
1295
+ "red": {"type": "integer", "minimum": 0, "maximum": 255},
1296
+ "green": {"type": "integer", "minimum": 0, "maximum": 255},
1297
+ "blue": {"type": "integer", "minimum": 0, "maximum": 255},
1298
+ },
1299
+ object_required=["red", "green", "blue"],
1300
+ )
1301
+
1302
+ _register_color(
1303
+ RGBA,
1304
+ parse_rgba,
1305
+ title="RGBA",
1306
+ string_description="String representation rgba(r, g, b, a) with 0-255 integer channels and alpha 0-1",
1307
+ string_pattern=r"^rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(?:0|1|0?\.\d+)\s*\)$",
1308
+ string_examples=["rgba(255, 0, 0, 1)", "rgba(0, 128, 255, 0.5)"],
1309
+ object_description="Object with integer channels 0-255 and alpha 0-1",
1310
+ object_properties={
1311
+ "red": {"type": "integer", "minimum": 0, "maximum": 255},
1312
+ "green": {"type": "integer", "minimum": 0, "maximum": 255},
1313
+ "blue": {"type": "integer", "minimum": 0, "maximum": 255},
1314
+ "alpha": {"type": "number", "minimum": 0, "maximum": 1},
1315
+ },
1316
+ object_required=["red", "green", "blue", "alpha"],
1317
+ )
1318
+
1319
+ _register_color(
1320
+ HEX,
1321
+ parse_hex,
1322
+ title="HEX",
1323
+ string_description="String representation hex(#RRGGBB)",
1324
+ string_pattern=r"^hex\(\s*#?[0-9A-Fa-f]{6}\s*\)$",
1325
+ string_examples=["hex(#ffffff)", "hex(#0a1b2c)"],
1326
+ object_description="Object with hex string value #RRGGBB",
1327
+ object_properties={
1328
+ "value": {
1329
+ "type": "string",
1330
+ "pattern": r"^#?[0-9A-Fa-f]{6}$",
1331
+ "examples": ["#ffffff", "0A1B2C"],
1332
+ }
1333
+ },
1334
+ object_required=["value"],
1335
+ )
1336
+
1337
+ _register_color(
1338
+ HSV,
1339
+ parse_hsv,
1340
+ title="HSV",
1341
+ string_description="String representation hsv(h, s, v)",
1342
+ string_pattern=r"^hsv\(\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*\)$",
1343
+ string_examples=["hsv(120.0, 100.0, 100.0)"],
1344
+ object_description="Object with hue 0-360, saturation/value 0-100",
1345
+ object_properties={
1346
+ "hue": {"type": "number", "minimum": 0, "maximum": 360},
1347
+ "saturation": {"type": "number", "minimum": 0, "maximum": 100},
1348
+ "value": {"type": "number", "minimum": 0, "maximum": 100},
1349
+ },
1350
+ object_required=["hue", "saturation", "value"],
1351
+ )
1352
+
1353
+ _register_color(
1354
+ HSL,
1355
+ parse_hsl,
1356
+ title="HSL",
1357
+ string_description="String representation hsl(h, s, l)",
1358
+ string_pattern=r"^hsl\(\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*\)$",
1359
+ string_examples=["hsl(240.0, 100.0, 50.0)"],
1360
+ object_description="Object with hue 0-360, saturation/lightness 0-100",
1361
+ object_properties={
1362
+ "hue": {"type": "number", "minimum": 0, "maximum": 360},
1363
+ "saturation": {"type": "number", "minimum": 0, "maximum": 100},
1364
+ "lightness": {"type": "number", "minimum": 0, "maximum": 100},
1365
+ },
1366
+ object_required=["hue", "saturation", "lightness"],
1367
+ )
1368
+
1369
+ _register_color(
1370
+ CMYK,
1371
+ parse_cmyk,
1372
+ title="CMYK",
1373
+ string_description="String representation cmyk(c, m, y, k)",
1374
+ string_pattern=r"^cmyk\(\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*,\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*\)$",
1375
+ string_examples=["cmyk(0, 100, 100, 0)", "cmyk(10.5, 0, 25, 5)"],
1376
+ object_description="Object with CMYK percentages 0-100",
1377
+ object_properties={
1378
+ "cyan": {"type": "number", "minimum": 0, "maximum": 100},
1379
+ "magenta": {"type": "number", "minimum": 0, "maximum": 100},
1380
+ "yellow": {"type": "number", "minimum": 0, "maximum": 100},
1381
+ "key": {"type": "number", "minimum": 0, "maximum": 100},
1382
+ },
1383
+ object_required=["cyan", "magenta", "yellow", "key"],
1384
+ )
1385
+
1386
+ _register_color(
1387
+ ASSA,
1388
+ parse_assa,
1389
+ title="ASSA",
1390
+ string_description="String representation assa(BBGGRR) or assa(AABBGGRR)",
1391
+ string_pattern=r"^assa\(\s*[0-9A-Fa-f]{6}(?:[0-9A-Fa-f]{2})?\s*\)$",
1392
+ string_examples=["assa(00ff00)", "assa(ff00ff00)"],
1393
+ object_description="Object with ASSA hex value (BBGGRR or AABBGGRR)",
1394
+ object_properties={
1395
+ "value": {
1396
+ "type": "string",
1397
+ "pattern": r"^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$",
1398
+ "examples": ["00ff00", "ff00ff00"],
1399
+ }
1400
+ },
1401
+ object_required=["value"],
1402
+ )
1403
+
1404
+ _register_color(
1405
+ webcolor,
1406
+ parse_webcolor,
1407
+ title="Web Color",
1408
+ string_description="Named CSS color via webcolor('name')",
1409
+ string_pattern=r"^webcolor\(\s*['\"]?[A-Za-z]+['\"]?\s*\)$",
1410
+ string_examples=["webcolor('red')", 'webcolor("aliceblue")'],
1411
+ object_description="Object with CSS color name",
1412
+ object_properties={
1413
+ "name": {"type": "string", "enum": list(CSS_COLOR_TO_HEX.keys())},
1414
+ },
1415
+ object_required=["name"],
1416
+ )
1417
+ __all__ = [
1418
+ "Color",
1419
+ "RGB",
1420
+ "RGBA",
1421
+ "HEX",
1422
+ "HSV",
1423
+ "HSL",
1424
+ "CMYK",
1425
+ "ASSA",
1426
+ "webcolor",
1427
+ "CSS_COLOR_TO_HEX",
1428
+ "CssColorName",
1429
+ "ColorValueError",
1430
+ "normalize_color",
1431
+ ]
@@ -256,7 +256,8 @@ def rand_filename(prefix: str = "file", ext: str = "", include_datetime: bool =
256
256
 
257
257
 
258
258
  def bytes_readable(size_bytes: int) -> str:
259
- """Convert bytes to a human-readable string.
259
+ """
260
+ Convert bytes to a human-readable string.
260
261
 
261
262
  Args:
262
263
  size_bytes (int): The number of bytes
@@ -275,7 +276,8 @@ def bytes_readable(size_bytes: int) -> str:
275
276
 
276
277
 
277
278
  def readable_size_to_bytes(size: str, kb_size: Literal[1000, 1024] = 1024) -> int:
278
- """Convert human-readable string to bytes.
279
+ """
280
+ Convert human-readable string to bytes.
279
281
 
280
282
  Args:
281
283
  size (str): The number of bytes in human-readable format
@@ -772,7 +774,8 @@ class PathBase(PathLike):
772
774
  return self
773
775
 
774
776
  def chmod(self, mode: int):
775
- """Change the path's permissions.
777
+ """
778
+ Change the path's permissions.
776
779
 
777
780
  Args:
778
781
  mode (int): The new permissions mode.
@@ -784,7 +787,8 @@ class PathBase(PathLike):
784
787
  return self
785
788
 
786
789
  def chown(self, user: str, group: str):
787
- """Change the path's owner and group.
790
+ """
791
+ Change the path's owner and group.
788
792
 
789
793
  Args:
790
794
  user (str): The new owner.
@@ -797,7 +801,8 @@ class PathBase(PathLike):
797
801
  return self
798
802
 
799
803
  def should_exist(self):
800
- """Raise FileNotFoundError if the path does not exist.
804
+ """
805
+ Raise FileNotFoundError if the path does not exist.
801
806
 
802
807
  Returns:
803
808
  PathBase: The object.
@@ -807,7 +812,8 @@ class PathBase(PathLike):
807
812
  return self
808
813
 
809
814
  def should_not_exist(self):
810
- """Raise FileExistsError if the path exists.
815
+ """
816
+ Raise FileExistsError if the path exists.
811
817
 
812
818
  Returns:
813
819
  PathBase: The object.
@@ -900,7 +906,8 @@ class PathBase(PathLike):
900
906
  if sys.platform != "win32":
901
907
 
902
908
  def get_xattr(self, name: str, group: str = "user") -> str:
903
- """Retrieve the value of an extended attribute.
909
+ """
910
+ Retrieve the value of an extended attribute.
904
911
 
905
912
  Args:
906
913
  name: The name of the extended attribute.
@@ -912,7 +919,8 @@ class PathBase(PathLike):
912
919
  return os.getxattr(self.path, f"{group}.{name}").decode()
913
920
 
914
921
  def set_xattr(self, value: str | bytes, name: str, group: str = "user") -> PathBase:
915
- """Set an extended attribute.
922
+ """
923
+ Set an extended attribute.
916
924
 
917
925
  Args:
918
926
  value: The value of the extended attribute.
@@ -925,7 +933,8 @@ class PathBase(PathLike):
925
933
  return self
926
934
 
927
935
  def remove_xattr(self, name: str, group: str = "user") -> PathBase:
928
- """Remove an extended attribute.
936
+ """
937
+ Remove an extended attribute.
929
938
 
930
939
  Args:
931
940
  name: The name of the extended attribute.
@@ -943,7 +952,8 @@ class File(PathBase):
943
952
  *,
944
953
  abs: bool = False,
945
954
  ) -> None:
946
- """Initialize a File object.
955
+ """
956
+ Initialize a File object.
947
957
 
948
958
  Args:
949
959
  path (str | PathLike): File path.
@@ -969,7 +979,8 @@ class File(PathBase):
969
979
 
970
980
  @property
971
981
  def ext(self) -> str:
972
- """The file's extension (without the dot).
982
+ """
983
+ The file's extension (without the dot).
973
984
  Returns empty string if the file has no extension.
974
985
  """
975
986
  if "." in self.basename:
@@ -1052,7 +1063,8 @@ class File(PathBase):
1052
1063
  self._write(data, "a", newline=newline)
1053
1064
 
1054
1065
  def write_iter(self, data: Iterable, sep="\n"):
1055
- """Write data from an iterable to a file, overwriting any existing data.
1066
+ """
1067
+ Write data from an iterable to a file, overwriting any existing data.
1056
1068
 
1057
1069
  Args:
1058
1070
  data (Iterable): The data to write.
@@ -1061,7 +1073,8 @@ class File(PathBase):
1061
1073
  self._write_iter(data, "w", sep=sep)
1062
1074
 
1063
1075
  def append_iter(self, data: Iterable, sep="\n"):
1064
- """Append data from an iterable to a file.
1076
+ """
1077
+ Append data from an iterable to a file.
1065
1078
 
1066
1079
  Args:
1067
1080
  data (Iterable): The data to append.
@@ -1144,7 +1157,8 @@ class File(PathBase):
1144
1157
  return self
1145
1158
 
1146
1159
  def with_ext(self, ext: str):
1147
- """Change the extension of the file and return the new File object
1160
+ """
1161
+ Change the extension of the file and return the new File object
1148
1162
 
1149
1163
  Args:
1150
1164
  ext (str): The new extension of the file.
@@ -1214,7 +1228,8 @@ class Directory(PathBase):
1214
1228
  *,
1215
1229
  abs: bool = False,
1216
1230
  ) -> None:
1217
- """Initialize a Directory object.
1231
+ """
1232
+ Initialize a Directory object.
1218
1233
 
1219
1234
  Args:
1220
1235
  path (str | PathLike): Directory path.
@@ -1,7 +1,7 @@
1
1
  import pytest
2
2
 
3
3
  pydantic = pytest.importorskip("pydantic")
4
- from pydantic import BaseModel
4
+ from pydantic import BaseModel, ValidationError
5
5
 
6
6
  from stdl.color import ASSA, CMYK, HEX, HSL, HSV, RGB, RGBA
7
7
 
@@ -34,9 +34,7 @@ def test_model_dump_preserves_objects_and_json_serializes():
34
34
  assert isinstance(d["hsl"], HSL)
35
35
  assert isinstance(d["cmyk"], CMYK)
36
36
  assert isinstance(d["assa"], ASSA)
37
- # Ensure JSON dump still uses repr strings
38
- js = c.model_dump_json()
39
- assert isinstance(js, str)
37
+ assert isinstance(c.model_dump_json(), str)
40
38
 
41
39
 
42
40
  def test_validate_from_repr_strings():
@@ -77,3 +75,57 @@ def test_validate_from_dicts():
77
75
  assert isinstance(c.hsl, HSL)
78
76
  assert isinstance(c.cmyk, CMYK)
79
77
  assert isinstance(c.assa, ASSA)
78
+
79
+
80
+ def test_union_validation_accepts_hex_instance():
81
+ class NumberColorModel(BaseModel):
82
+ color: RGB | RGBA | HEX
83
+
84
+ payload = {"color": HEX("#2055b9")}
85
+
86
+ model = NumberColorModel.model_validate(payload)
87
+
88
+ assert isinstance(model.color, HEX)
89
+
90
+
91
+ def test_union_validation_accepts_repr_for_late_branch():
92
+ class ColorUnionModel(BaseModel):
93
+ color: RGB | HEX
94
+
95
+ payload = {"color": "hex(#123456)"}
96
+
97
+ model = ColorUnionModel.model_validate(payload)
98
+
99
+ assert isinstance(model.color, HEX)
100
+ assert model.color.value == "#123456"
101
+
102
+
103
+ def test_union_validation_accepts_mapping_for_late_branch():
104
+ class ColorUnionModel(BaseModel):
105
+ color: RGBA | HEX
106
+
107
+ payload = {"color": {"value": "#abcdef"}}
108
+
109
+ model = ColorUnionModel.model_validate(payload)
110
+
111
+ assert isinstance(model.color, HEX)
112
+ assert model.color.value == "#abcdef"
113
+
114
+
115
+ def test_union_validation_keeps_first_branch_instance():
116
+ class ColorUnionModel(BaseModel):
117
+ color: RGB | HEX
118
+
119
+ rgb = RGB(1, 2, 3)
120
+
121
+ model = ColorUnionModel.model_validate({"color": rgb})
122
+
123
+ assert model.color is rgb
124
+
125
+
126
+ def test_union_validation_reports_invalid_values():
127
+ class ColorUnionModel(BaseModel):
128
+ color: RGB | HEX
129
+
130
+ with pytest.raises(ValidationError):
131
+ ColorUnionModel.model_validate({"color": 42})
@@ -2,6 +2,15 @@ version = 1
2
2
  revision = 3
3
3
  requires-python = ">=3.10"
4
4
 
5
+ [[package]]
6
+ name = "annotated-types"
7
+ version = "0.7.0"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
12
+ ]
13
+
5
14
  [[package]]
6
15
  name = "cfgv"
7
16
  version = "3.4.0"
@@ -291,6 +300,139 @@ wheels = [
291
300
  { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" },
292
301
  ]
293
302
 
303
+ [[package]]
304
+ name = "pydantic"
305
+ version = "2.12.4"
306
+ source = { registry = "https://pypi.org/simple" }
307
+ dependencies = [
308
+ { name = "annotated-types" },
309
+ { name = "pydantic-core" },
310
+ { name = "typing-extensions" },
311
+ { name = "typing-inspection" },
312
+ ]
313
+ sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" }
314
+ wheels = [
315
+ { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" },
316
+ ]
317
+
318
+ [[package]]
319
+ name = "pydantic-core"
320
+ version = "2.41.5"
321
+ source = { registry = "https://pypi.org/simple" }
322
+ dependencies = [
323
+ { name = "typing-extensions" },
324
+ ]
325
+ sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
326
+ wheels = [
327
+ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" },
328
+ { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" },
329
+ { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" },
330
+ { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" },
331
+ { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" },
332
+ { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" },
333
+ { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" },
334
+ { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" },
335
+ { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" },
336
+ { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" },
337
+ { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" },
338
+ { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" },
339
+ { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" },
340
+ { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
341
+ { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
342
+ { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
343
+ { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
344
+ { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
345
+ { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
346
+ { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
347
+ { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
348
+ { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
349
+ { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
350
+ { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
351
+ { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
352
+ { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
353
+ { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
354
+ { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
355
+ { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
356
+ { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
357
+ { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
358
+ { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
359
+ { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
360
+ { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
361
+ { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
362
+ { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
363
+ { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
364
+ { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
365
+ { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
366
+ { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
367
+ { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
368
+ { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
369
+ { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
370
+ { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
371
+ { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
372
+ { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
373
+ { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
374
+ { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
375
+ { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
376
+ { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
377
+ { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
378
+ { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
379
+ { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
380
+ { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
381
+ { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
382
+ { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
383
+ { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
384
+ { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
385
+ { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
386
+ { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
387
+ { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
388
+ { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
389
+ { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
390
+ { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
391
+ { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
392
+ { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
393
+ { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
394
+ { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
395
+ { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
396
+ { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
397
+ { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
398
+ { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
399
+ { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
400
+ { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
401
+ { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
402
+ { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
403
+ { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
404
+ { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
405
+ { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
406
+ { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
407
+ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
408
+ { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
409
+ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
410
+ { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
411
+ { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
412
+ { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
413
+ { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
414
+ { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
415
+ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
416
+ { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
417
+ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
418
+ { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" },
419
+ { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" },
420
+ { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" },
421
+ { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" },
422
+ { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" },
423
+ { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" },
424
+ { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" },
425
+ { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" },
426
+ { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
427
+ { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
428
+ { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
429
+ { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
430
+ { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
431
+ { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
432
+ { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
433
+ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
434
+ ]
435
+
294
436
  [[package]]
295
437
  name = "pygments"
296
438
  version = "2.19.2"
@@ -410,7 +552,7 @@ wheels = [
410
552
 
411
553
  [[package]]
412
554
  name = "stdl"
413
- version = "0.6.4"
555
+ version = "0.6.5"
414
556
  source = { editable = "." }
415
557
  dependencies = [
416
558
  { name = "python-dateutil" },
@@ -425,11 +567,13 @@ dev = [
425
567
  { name = "coverage" },
426
568
  { name = "mypy" },
427
569
  { name = "pre-commit" },
570
+ { name = "pydantic" },
428
571
  { name = "pytest" },
429
572
  { name = "ruff" },
430
573
  ]
431
574
  test = [
432
575
  { name = "coverage" },
576
+ { name = "pydantic" },
433
577
  { name = "pytest" },
434
578
  ]
435
579
 
@@ -440,6 +584,8 @@ requires-dist = [
440
584
  { name = "coverage", marker = "extra == 'test'" },
441
585
  { name = "mypy", marker = "extra == 'dev'" },
442
586
  { name = "pre-commit", marker = "extra == 'dev'" },
587
+ { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2" },
588
+ { name = "pydantic", marker = "extra == 'test'", specifier = ">=2" },
443
589
  { name = "pytest", marker = "extra == 'dev'" },
444
590
  { name = "pytest", marker = "extra == 'test'" },
445
591
  { name = "python-dateutil", specifier = ">=2.8.2" },
@@ -448,7 +594,7 @@ requires-dist = [
448
594
  { name = "toml", specifier = ">=0.10.2" },
449
595
  { name = "tqdm", specifier = ">=4.67.1" },
450
596
  ]
451
- provides-extras = ["test", "dev"]
597
+ provides-extras = ["dev", "test"]
452
598
 
453
599
  [[package]]
454
600
  name = "toml"
@@ -519,6 +665,18 @@ wheels = [
519
665
  { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
520
666
  ]
521
667
 
668
+ [[package]]
669
+ name = "typing-inspection"
670
+ version = "0.4.2"
671
+ source = { registry = "https://pypi.org/simple" }
672
+ dependencies = [
673
+ { name = "typing-extensions" },
674
+ ]
675
+ sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
676
+ wheels = [
677
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
678
+ ]
679
+
522
680
  [[package]]
523
681
  name = "virtualenv"
524
682
  version = "20.31.2"
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
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
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
File without changes
File without changes
File without changes