python-introspect 0.1.6__tar.gz → 0.1.7__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.
- {python_introspect-0.1.6/src/python_introspect.egg-info → python_introspect-0.1.7}/PKG-INFO +1 -1
- {python_introspect-0.1.6 → python_introspect-0.1.7}/pyproject.toml +1 -1
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/__init__.py +3 -1
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/validation.py +13 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7/src/python_introspect.egg-info}/PKG-INFO +1 -1
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_init.py +1 -1
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_validation.py +16 -5
- {python_introspect-0.1.6 → python_introspect-0.1.7}/LICENSE +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/README.md +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/setup.cfg +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/annotation_types.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/dataclass_projection.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/enableable.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/environment_projection.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/exceptions.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/signature_analyzer.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/unified_parameter_analyzer.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/SOURCES.txt +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/dependency_links.txt +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/requires.txt +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/top_level.txt +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_annotation_types.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_dataclass_projection.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_environment_projection.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_exceptions.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_signature_analyzer.py +0 -0
- {python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_unified_parameter_analyzer.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-introspect"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.7"
|
|
8
8
|
description = "Pure Python introspection toolkit for function signatures, dataclasses, and type hints"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -9,7 +9,7 @@ Extensibility:
|
|
|
9
9
|
type resolution for framework-specific types (lazy configs, proxies, etc.)
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
__version__ = "0.1.
|
|
12
|
+
__version__ = "0.1.7"
|
|
13
13
|
|
|
14
14
|
from .signature_analyzer import (
|
|
15
15
|
SignatureAnalyzer,
|
|
@@ -41,6 +41,7 @@ from .enableable import (
|
|
|
41
41
|
mark_enableable,
|
|
42
42
|
)
|
|
43
43
|
from .validation import (
|
|
44
|
+
AnnotatedDataclassValidationMixin,
|
|
44
45
|
AnnotationValidationError,
|
|
45
46
|
overlay_non_none_dataclass,
|
|
46
47
|
validate_annotated_dataclass,
|
|
@@ -95,6 +96,7 @@ __all__ = [
|
|
|
95
96
|
"is_enableable",
|
|
96
97
|
"mark_enableable",
|
|
97
98
|
# Runtime annotation validation
|
|
99
|
+
"AnnotatedDataclassValidationMixin",
|
|
98
100
|
"AnnotationValidationError",
|
|
99
101
|
"overlay_non_none_dataclass",
|
|
100
102
|
"validate_annotated_dataclass",
|
|
@@ -27,6 +27,19 @@ class AnnotationValidationError(ValueError):
|
|
|
27
27
|
DataclassT = TypeVar("DataclassT")
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
class AnnotatedDataclassValidationMixin:
|
|
31
|
+
"""Validate a dataclass instance from its resolved annotations after construction.
|
|
32
|
+
|
|
33
|
+
Dataclass declarations inherit this mixin when their annotations are the
|
|
34
|
+
authoritative runtime schema. Keeping the lifecycle hook on a nominal base
|
|
35
|
+
also preserves validation when another package recreates the dataclass with
|
|
36
|
+
the same bases.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __post_init__(self) -> None:
|
|
40
|
+
validate_annotated_dataclass(self)
|
|
41
|
+
|
|
42
|
+
|
|
30
43
|
def overlay_non_none_dataclass(
|
|
31
44
|
base: DataclassT,
|
|
32
45
|
overlay: object,
|
|
@@ -11,7 +11,7 @@ class TestPackageImports:
|
|
|
11
11
|
"""Test that __version__ is available."""
|
|
12
12
|
assert hasattr(python_introspect, "__version__")
|
|
13
13
|
assert isinstance(python_introspect.__version__, str)
|
|
14
|
-
assert python_introspect.__version__ == "0.1.
|
|
14
|
+
assert python_introspect.__version__ == "0.1.7"
|
|
15
15
|
|
|
16
16
|
def test_signature_analyzer_import(self):
|
|
17
17
|
"""Test SignatureAnalyzer is importable."""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, make_dataclass
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import Annotated, Literal
|
|
4
4
|
|
|
@@ -6,6 +6,7 @@ import pytest
|
|
|
6
6
|
from annotated_types import Ge, Gt, Le, MinLen, Predicate
|
|
7
7
|
|
|
8
8
|
from python_introspect import (
|
|
9
|
+
AnnotatedDataclassValidationMixin,
|
|
9
10
|
overlay_non_none_dataclass,
|
|
10
11
|
validate_annotated_dataclass,
|
|
11
12
|
)
|
|
@@ -21,13 +22,10 @@ NonBlankText = Annotated[str, MinLen(1), Predicate(str.strip)]
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
@dataclass(frozen=True)
|
|
24
|
-
class BaseConfig:
|
|
25
|
+
class BaseConfig(AnnotatedDataclassValidationMixin):
|
|
25
26
|
port: Port = 7000
|
|
26
27
|
mode: Mode = Mode.FIRST
|
|
27
28
|
|
|
28
|
-
def __post_init__(self) -> None:
|
|
29
|
-
validate_annotated_dataclass(self)
|
|
30
|
-
|
|
31
29
|
|
|
32
30
|
@dataclass(frozen=True)
|
|
33
31
|
class ChildConfig(BaseConfig):
|
|
@@ -53,6 +51,19 @@ def test_validation_rejects_wrong_nominal_types_without_coercion() -> None:
|
|
|
53
51
|
ChildConfig(mode="first")
|
|
54
52
|
|
|
55
53
|
|
|
54
|
+
def test_inherited_validation_survives_dataclass_recreation() -> None:
|
|
55
|
+
RecreatedConfig = make_dataclass(
|
|
56
|
+
"RecreatedConfig",
|
|
57
|
+
(("port", Port, 7000),),
|
|
58
|
+
bases=(AnnotatedDataclassValidationMixin,),
|
|
59
|
+
frozen=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert RecreatedConfig(port=8000).port == 8000
|
|
63
|
+
with pytest.raises(ValueError, match="port must be at least"):
|
|
64
|
+
RecreatedConfig(port=0)
|
|
65
|
+
|
|
66
|
+
|
|
56
67
|
@dataclass(frozen=True)
|
|
57
68
|
class OptionalConfig:
|
|
58
69
|
port: Port | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/annotation_types.py
RENAMED
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/dataclass_projection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/environment_projection.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect/signature_analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/requires.txt
RENAMED
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/src/python_introspect.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.1.6 → python_introspect-0.1.7}/tests/test_unified_parameter_analyzer.py
RENAMED
|
File without changes
|