stdl 0.6.5__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.5 → stdl-0.6.6}/PKG-INFO +2 -1
  2. {stdl-0.6.5 → stdl-0.6.6}/pyproject.toml +11 -3
  3. {stdl-0.6.5 → stdl-0.6.6}/stdl/__init__.py +1 -0
  4. {stdl-0.6.5 → stdl-0.6.6}/stdl/color.py +248 -73
  5. {stdl-0.6.5 → stdl-0.6.6}/uv.lock +5 -3
  6. {stdl-0.6.5 → stdl-0.6.6}/.editorconfig +0 -0
  7. {stdl-0.6.5 → stdl-0.6.6}/.github/workflows/lint.yml +0 -0
  8. {stdl-0.6.5 → stdl-0.6.6}/.github/workflows/publish.yml +0 -0
  9. {stdl-0.6.5 → stdl-0.6.6}/.github/workflows/test.yml +0 -0
  10. {stdl-0.6.5 → stdl-0.6.6}/.gitignore +0 -0
  11. {stdl-0.6.5 → stdl-0.6.6}/.pre-commit-config.yaml +0 -0
  12. {stdl-0.6.5 → stdl-0.6.6}/.python-version +0 -0
  13. {stdl-0.6.5 → stdl-0.6.6}/.readthedocs.yml +0 -0
  14. {stdl-0.6.5 → stdl-0.6.6}/CONTRIBUTING.md +0 -0
  15. {stdl-0.6.5 → stdl-0.6.6}/Justfile +0 -0
  16. {stdl-0.6.5 → stdl-0.6.6}/LICENSE +0 -0
  17. {stdl-0.6.5 → stdl-0.6.6}/README.md +0 -0
  18. {stdl-0.6.5 → stdl-0.6.6}/docs/index.md +0 -0
  19. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/color.md +0 -0
  20. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/decorators.md +0 -0
  21. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/dt.md +0 -0
  22. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/fs.md +0 -0
  23. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/import_lazy.md +0 -0
  24. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/log.md +0 -0
  25. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/net.md +0 -0
  26. {stdl-0.6.5 → stdl-0.6.6}/docs/reference/st.md +0 -0
  27. {stdl-0.6.5 → stdl-0.6.6}/docs/requirements.txt +0 -0
  28. {stdl-0.6.5 → stdl-0.6.6}/mkdocs.yml +0 -0
  29. {stdl-0.6.5 → stdl-0.6.6}/stdl/decorators.py +0 -0
  30. {stdl-0.6.5 → stdl-0.6.6}/stdl/dt.py +0 -0
  31. {stdl-0.6.5 → stdl-0.6.6}/stdl/fs.py +0 -0
  32. {stdl-0.6.5 → stdl-0.6.6}/stdl/import_lazy.py +0 -0
  33. {stdl-0.6.5 → stdl-0.6.6}/stdl/log.py +0 -0
  34. {stdl-0.6.5 → stdl-0.6.6}/stdl/lst.py +0 -0
  35. {stdl-0.6.5 → stdl-0.6.6}/stdl/net.py +0 -0
  36. {stdl-0.6.5 → stdl-0.6.6}/stdl/st.py +0 -0
  37. {stdl-0.6.5 → stdl-0.6.6}/tests/test_color.py +0 -0
  38. {stdl-0.6.5 → stdl-0.6.6}/tests/test_decorators.py +0 -0
  39. {stdl-0.6.5 → stdl-0.6.6}/tests/test_dt.py +0 -0
  40. {stdl-0.6.5 → stdl-0.6.6}/tests/test_fs.py +0 -0
  41. {stdl-0.6.5 → stdl-0.6.6}/tests/test_lst.py +0 -0
  42. {stdl-0.6.5 → stdl-0.6.6}/tests/test_pydantic_integration.py +0 -0
  43. {stdl-0.6.5 → 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.5
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,6 +52,7 @@ 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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stdl"
3
- version = "0.6.5"
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", "pydantic>=2"]
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"
@@ -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",
@@ -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):
@@ -1175,7 +1161,7 @@ else:
1175
1161
  )
1176
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)
1255
+ return parser(v)
1214
1256
  raise ValueError(
1215
- f"Expected {_cls.__name__} instance, repr string, or mapping, got {type(v).__name__}"
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
+ ]
@@ -552,7 +552,7 @@ wheels = [
552
552
 
553
553
  [[package]]
554
554
  name = "stdl"
555
- version = "0.6.4"
555
+ version = "0.6.5"
556
556
  source = { editable = "." }
557
557
  dependencies = [
558
558
  { name = "python-dateutil" },
@@ -567,6 +567,7 @@ dev = [
567
567
  { name = "coverage" },
568
568
  { name = "mypy" },
569
569
  { name = "pre-commit" },
570
+ { name = "pydantic" },
570
571
  { name = "pytest" },
571
572
  { name = "ruff" },
572
573
  ]
@@ -583,7 +584,8 @@ requires-dist = [
583
584
  { name = "coverage", marker = "extra == 'test'" },
584
585
  { name = "mypy", marker = "extra == 'dev'" },
585
586
  { name = "pre-commit", marker = "extra == 'dev'" },
586
- { name = "pydantic", marker = "extra == 'test'" },
587
+ { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2" },
588
+ { name = "pydantic", marker = "extra == 'test'", specifier = ">=2" },
587
589
  { name = "pytest", marker = "extra == 'dev'" },
588
590
  { name = "pytest", marker = "extra == 'test'" },
589
591
  { name = "python-dateutil", specifier = ">=2.8.2" },
@@ -592,7 +594,7 @@ requires-dist = [
592
594
  { name = "toml", specifier = ">=0.10.2" },
593
595
  { name = "tqdm", specifier = ">=4.67.1" },
594
596
  ]
595
- provides-extras = ["test", "dev"]
597
+ provides-extras = ["dev", "test"]
596
598
 
597
599
  [[package]]
598
600
  name = "toml"
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
File without changes