clevis 0.3.2__tar.gz → 0.3.3__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 (52) hide show
  1. {clevis-0.3.2 → clevis-0.3.3}/CHANGELOG.md +18 -0
  2. {clevis-0.3.2 → clevis-0.3.3}/PKG-INFO +1 -1
  3. {clevis-0.3.2 → clevis-0.3.3}/pyproject.toml +1 -1
  4. {clevis-0.3.2 → clevis-0.3.3}/src/clevis/__init__.py +27 -7
  5. clevis-0.3.3/tests/test_advanced_types.py +752 -0
  6. clevis-0.3.3/tests/test_yoker_schema.py +579 -0
  7. {clevis-0.3.2 → clevis-0.3.3}/uv.lock +1 -1
  8. {clevis-0.3.2 → clevis-0.3.3}/.claude/settings.local.json +0 -0
  9. {clevis-0.3.2 → clevis-0.3.3}/.github/workflows/test.yml +0 -0
  10. {clevis-0.3.2 → clevis-0.3.3}/.gitignore +0 -0
  11. {clevis-0.3.2 → clevis-0.3.3}/.python-version +0 -0
  12. {clevis-0.3.2 → clevis-0.3.3}/.readthedocs.yaml +0 -0
  13. {clevis-0.3.2 → clevis-0.3.3}/HISTORY.md +0 -0
  14. {clevis-0.3.2 → clevis-0.3.3}/LICENSE +0 -0
  15. {clevis-0.3.2 → clevis-0.3.3}/Makefile +0 -0
  16. {clevis-0.3.2 → clevis-0.3.3}/PACKAGE.md +0 -0
  17. {clevis-0.3.2 → clevis-0.3.3}/README.md +0 -0
  18. {clevis-0.3.2 → clevis-0.3.3}/REQUIREMENTS.md +0 -0
  19. {clevis-0.3.2 → clevis-0.3.3}/TODO.md +0 -0
  20. {clevis-0.3.2 → clevis-0.3.3}/analysis/2026-05-30-api-review-p1-003.md +0 -0
  21. {clevis-0.3.2 → clevis-0.3.3}/analysis/2026-06-05-api-review-p2-001-factory-pattern.md +0 -0
  22. {clevis-0.3.2 → clevis-0.3.3}/analysis/2026-06-05-api-review-subcommand-enhancements.md +0 -0
  23. {clevis-0.3.2 → clevis-0.3.3}/analysis/P2-002-security-parameter.md +0 -0
  24. {clevis-0.3.2 → clevis-0.3.3}/analysis/api-cli-optional.md +0 -0
  25. {clevis-0.3.2 → clevis-0.3.3}/analysis/edge-case-analysis.md +0 -0
  26. {clevis-0.3.2 → clevis-0.3.3}/analysis/functional.md +0 -0
  27. {clevis-0.3.2 → clevis-0.3.3}/analysis/security-P2-002.md +0 -0
  28. {clevis-0.3.2 → clevis-0.3.3}/docs/api.rst +0 -0
  29. {clevis-0.3.2 → clevis-0.3.3}/docs/conf.py +0 -0
  30. {clevis-0.3.2 → clevis-0.3.3}/docs/index.rst +0 -0
  31. {clevis-0.3.2 → clevis-0.3.3}/docs/installation.rst +0 -0
  32. {clevis-0.3.2 → clevis-0.3.3}/docs/usage.rst +0 -0
  33. {clevis-0.3.2 → clevis-0.3.3}/example_config_param.py +0 -0
  34. {clevis-0.3.2 → clevis-0.3.3}/examples/app1.toml +0 -0
  35. {clevis-0.3.2 → clevis-0.3.3}/examples/commands.py +0 -0
  36. {clevis-0.3.2 → clevis-0.3.3}/examples/factory.py +0 -0
  37. {clevis-0.3.2 → clevis-0.3.3}/examples/main.py +0 -0
  38. {clevis-0.3.2 → clevis-0.3.3}/examples/nested-commands.py +0 -0
  39. {clevis-0.3.2 → clevis-0.3.3}/examples/nested.py +0 -0
  40. {clevis-0.3.2 → clevis-0.3.3}/examples/nested.toml +0 -0
  41. {clevis-0.3.2 → clevis-0.3.3}/examples/project.toml +0 -0
  42. {clevis-0.3.2 → clevis-0.3.3}/nested-commands.toml +0 -0
  43. {clevis-0.3.2 → clevis-0.3.3}/src/clevis/__init__.pyi +0 -0
  44. {clevis-0.3.2 → clevis-0.3.3}/src/clevis/py.typed +0 -0
  45. {clevis-0.3.2 → clevis-0.3.3}/tests/test_bug_regression.py +0 -0
  46. {clevis-0.3.2 → clevis-0.3.3}/tests/test_clevis.py +0 -0
  47. {clevis-0.3.2 → clevis-0.3.3}/tests/test_config_key.py +0 -0
  48. {clevis-0.3.2 → clevis-0.3.3}/tests/test_edge_cases.py +0 -0
  49. {clevis-0.3.2 → clevis-0.3.3}/tests/test_parser.py +0 -0
  50. {clevis-0.3.2 → clevis-0.3.3}/tests/test_security.py +0 -0
  51. {clevis-0.3.2 → clevis-0.3.3}/tests/test_subcommand_config.py +0 -0
  52. {clevis-0.3.2 → clevis-0.3.3}/tests/test_toctou_fix.py +0 -0
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.3.3 (2026-06-07)
9
+
10
+ ### Added
11
+ - Support for `Literal["A", "B", "C"]` types with validation
12
+ - Support for container types: `list[T]`, `dict[K, V]`, `set[T]`, `tuple[T, ...]`
13
+ - Automatic conversion of TOML arrays to `tuple` and `set` types
14
+ - Support for nested dataclasses as dict values: `dict[str, DataclassType]`
15
+ - Compatibility with `frozen=True` dataclasses
16
+
17
+ ### Changed
18
+ - Enhanced `unpack_type()` to properly handle container types and Literal types
19
+ - Updated `from_dict()` to use `Config(cast=[tuple, set])` for automatic type casting
20
+ - Improved type hints with `Union` and `get_origin` imports
21
+
22
+ ### Fixed
23
+ - `unpack_type()` now correctly returns container types as-is instead of treating them as unions
24
+ - `unpack_type()` now correctly returns Literal types as-is for dacite validation
25
+
8
26
  ## 0.3.2 (2026-06-07)
9
27
 
10
28
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clevis
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Configuration management for Python projects with dataclass-based schemas
5
5
  Project-URL: Homepage, https://github.com/christophevg/clevis
6
6
  Project-URL: Documentation, https://clevis.readthedocs.io
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "clevis"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "Configuration management for Python projects with dataclass-based schemas"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -21,12 +21,12 @@ from collections.abc import Callable
21
21
  from dataclasses import Field, dataclass, field, fields, is_dataclass
22
22
  from enum import Enum
23
23
  from pathlib import Path
24
- from typing import Any, Protocol, TypedDict, TypeVar, get_args
24
+ from typing import Any, Literal, Protocol, TypedDict, TypeVar, Union, get_args, get_origin
25
25
 
26
- from dacite import from_dict
26
+ from dacite import Config, from_dict
27
27
  from dacite.exceptions import DaciteError, MissingValueError, WrongTypeError
28
28
 
29
- __version__ = "0.3.2"
29
+ __version__ = "0.3.3"
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
@@ -629,6 +629,8 @@ def unpack_type(type_def: type) -> type:
629
629
  Given a type, if a union type, return the not-None type (dataclass).
630
630
 
631
631
  For Optional[T] or T | None, returns T.
632
+ For container types (list, dict, set, tuple), returns as-is.
633
+ For Literal types, returns as-is.
632
634
  For non-union types, returns the type as-is.
633
635
 
634
636
  Args:
@@ -640,13 +642,30 @@ def unpack_type(type_def: type) -> type:
640
642
  Raises:
641
643
  ValueError: If union has more than 2 types (not supported yet)
642
644
  """
643
- types = get_args(type_def)
644
- # not a union type
645
- if len(types) == 0:
645
+ from types import UnionType
646
+
647
+ origin = get_origin(type_def)
648
+
649
+ # Handle container types (list, dict, set, tuple) - return as-is
650
+ if origin in (list, dict, set, tuple):
651
+ return type_def
652
+
653
+ # Handle Literal types - return as-is (dacite validates)
654
+ if origin is Literal:
646
655
  return type_def
656
+
657
+ # Handle Union types (Optional[T] / T | None)
658
+ # Not a union type - return as-is
659
+ if origin is not Union and origin is not UnionType:
660
+ return type_def
661
+
662
+ types = get_args(type_def)
647
663
  # <type> | None is only supported combination
664
+ if len(types) == 0:
665
+ return type_def # Not a generic/union
648
666
  if len(types) > 2:
649
667
  raise ValueError("Complex unions not supported")
668
+ # T | None or None | T
650
669
  return types[0] if types[1] is type(None) else types[1] # type: ignore[no-any-return]
651
670
 
652
671
 
@@ -807,8 +826,9 @@ def get_config(
807
826
  apply_to_dict(get_factory(clz).get_args(args), cfg)
808
827
 
809
828
  # Convert dict to dataclass
829
+ # Use cast=[tuple, set] to convert TOML lists to tuples and sets
810
830
  try:
811
- return from_dict(data_class=clz, data=cfg)
831
+ return from_dict(data_class=clz, data=cfg, config=Config(cast=[tuple, set]))
812
832
  except MissingValueError as e:
813
833
  # Extract field path from dacite error message
814
834
  # Format: 'missing value for field "database.host"'