statica 1.1.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statica
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: A minimalistic data validation library
5
5
  Author-email: Marcel Kröker <kroeker.marcel@gmail.com>
6
6
  License: MIT License
@@ -31,7 +31,7 @@ Classifier: Operating System :: OS Independent
31
31
  Classifier: Intended Audience :: Developers
32
32
  Classifier: Programming Language :: Python
33
33
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
34
- Requires-Python: <3.14,>=3.11
34
+ Requires-Python: <3.14,>=3.10
35
35
  Description-Content-Type: text/markdown
36
36
 
37
37
  # Statica
@@ -44,13 +44,13 @@ Statica is a Python library for defining and validating structured data with typ
44
44
 
45
45
  ## Why Statica?
46
46
 
47
- Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to libraries like pydantic.
47
+ Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to libraries like pydantic.
48
48
  While pydantic is a powerful tool for data validation and parsing, Statica offers some distinct advantages in specific situations:
49
49
 
50
50
  1. **Lightweight**: Statica does not rely on any third-party dependencies, making it ideal for projects where minimizing external dependencies is a priority.
51
- 2. **Customizable Validation**: Statica allows fine-grained control over type and constraint validation through customizable field descriptors (`Field`) and error classes.
51
+ 2. **Performance**: For use cases where performance is critical. Pydantic needs `3x` more memory than Statica for the same models.
52
52
  3. **Ease of Use**: With its simple, Pythonic design, Statica is intuitive for developers already familiar with Python's `dataclasses` and type hinting. It avoids much of the magic that pydantic employs.
53
- 4. **Performance**: For use cases where performance, especially memory usage, is critical, Statica avoids some of the overhead introduced by the advanced features of pydantic.
53
+ 4. **Customizable Validation**: Statica allows fine-grained control over type and constraint validation through customizable field descriptors (`Field`) and error classes.
54
54
 
55
55
  ## Features
56
56
 
@@ -8,13 +8,13 @@ Statica is a Python library for defining and validating structured data with typ
8
8
 
9
9
  ## Why Statica?
10
10
 
11
- Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to libraries like pydantic.
11
+ Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to libraries like pydantic.
12
12
  While pydantic is a powerful tool for data validation and parsing, Statica offers some distinct advantages in specific situations:
13
13
 
14
14
  1. **Lightweight**: Statica does not rely on any third-party dependencies, making it ideal for projects where minimizing external dependencies is a priority.
15
- 2. **Customizable Validation**: Statica allows fine-grained control over type and constraint validation through customizable field descriptors (`Field`) and error classes.
15
+ 2. **Performance**: For use cases where performance is critical. Pydantic needs `3x` more memory than Statica for the same models.
16
16
  3. **Ease of Use**: With its simple, Pythonic design, Statica is intuitive for developers already familiar with Python's `dataclasses` and type hinting. It avoids much of the magic that pydantic employs.
17
- 4. **Performance**: For use cases where performance, especially memory usage, is critical, Statica avoids some of the overhead introduced by the advanced features of pydantic.
17
+ 4. **Customizable Validation**: Statica allows fine-grained control over type and constraint validation through customizable field descriptors (`Field`) and error classes.
18
18
 
19
19
  ## Features
20
20
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "statica"
3
- version = "1.1.0"
3
+ version = "1.2.0"
4
4
  description = "A minimalistic data validation library"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Marcel Kröker", email = "kroeker.marcel@gmail.com" }]
@@ -13,7 +13,7 @@ classifiers=[
13
13
  "Programming Language :: Python",
14
14
  "Topic :: Software Development :: Libraries :: Python Modules"
15
15
  ]
16
- requires-python = ">=3.11, <3.14"
16
+ requires-python = ">=3.10, <3.14"
17
17
 
18
18
 
19
19
  ########################################################################################
@@ -26,6 +26,7 @@ dependencies = []
26
26
  [dependency-groups]
27
27
  dev = [
28
28
  "mypy ~= 1.16.0",
29
+ "pyinstrument>=5.0.2",
29
30
  "pytest-cov ~= 6.1.0",
30
31
  "ruff ~= 0.11.0",
31
32
  ]
@@ -77,6 +78,9 @@ quote-style = "double"
77
78
  #### MARK: Testing
78
79
 
79
80
  [tool.coverage.report]
81
+ omit = [
82
+ "benchmark_*.py",
83
+ ]
80
84
  exclude_also = [
81
85
  "if TYPE_CHECKING:",
82
86
  'if __name__ == "__main__":',
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from dataclasses import field as dataclass_field
5
- from types import UnionType
6
5
  from typing import (
7
6
  TYPE_CHECKING,
8
7
  Any,
@@ -11,14 +10,11 @@ from typing import (
11
10
  TypeVar,
12
11
  cast,
13
12
  dataclass_transform,
14
- get_args,
15
- get_origin,
16
13
  get_type_hints,
17
14
  overload,
18
15
  )
19
16
 
20
17
  from statica.exceptions import ConstraintValidationError, TypeValidationError
21
- from statica.types_utils import get_expected_type
22
18
  from statica.validators import validate_constraints, validate_type
23
19
 
24
20
  if TYPE_CHECKING:
@@ -55,7 +51,7 @@ class FieldDescriptor(Generic[T]):
55
51
  def __set_name__(self, owner: Any, name: str) -> None:
56
52
  self.name = name
57
53
  self.owner = owner
58
- self.expected_type = get_expected_type(owner, name)
54
+ self.expected_type = owner.__annotations__.get(name)
59
55
 
60
56
  @overload
61
57
  def __get__(self, instance: None, owner: Any) -> FieldDescriptor[T]: ...
@@ -78,7 +74,13 @@ class FieldDescriptor(Generic[T]):
78
74
 
79
75
  validate_type(value, self.expected_type)
80
76
 
81
- if value is not None:
77
+ if (
78
+ self.min_length is not None
79
+ or self.max_length is not None
80
+ or self.min_value is not None
81
+ or self.max_value is not None
82
+ or self.strip_whitespace is not None
83
+ ):
82
84
  value = validate_constraints(
83
85
  value,
84
86
  min_length=self.min_length,
@@ -182,20 +184,15 @@ class StaticaMeta(type):
182
184
 
183
185
  # Generate custom __init__ method
184
186
 
185
- def custom_init(self: Statica, **kwargs: Any) -> None:
187
+ def statica_init(self: Statica, **kwargs: Any) -> None:
186
188
  for field_name, field_type in annotations.items():
187
- # If it is union type with none continuation, skip it
188
- if get_origin(field_type) is UnionType and type(None) in get_args(field_type):
189
- if field_name not in kwargs:
190
- setattr(self, field_name, None)
191
- continue
192
-
193
- if field_name not in kwargs:
189
+ if field_name not in kwargs and not isinstance(None, field_type):
194
190
  msg = f"Missing required field: {field_name}"
195
191
  raise TypeValidationError(msg)
196
- setattr(self, field_name, kwargs[field_name])
197
192
 
198
- namespace["__init__"] = custom_init
193
+ setattr(self, field_name, kwargs.get(field_name))
194
+
195
+ namespace["__init__"] = statica_init
199
196
 
200
197
  # Set up Field descriptors for type-hinted attributes
201
198
 
@@ -4,7 +4,6 @@ from types import UnionType
4
4
  from typing import Any
5
5
 
6
6
  from statica.exceptions import ConstraintValidationError, TypeValidationError
7
- from statica.types_utils import value_matches_type
8
7
 
9
8
 
10
9
  def validate_type(value: Any, expected_type: type | UnionType) -> None:
@@ -20,11 +19,12 @@ def validate_type(value: Any, expected_type: type | UnionType) -> None:
20
19
  _validate_type(None, int | None) # No exception
21
20
  _validate_type(None, int) # Raises TypeValidationError
22
21
  """
23
- if not value_matches_type(value, expected_type):
24
- expected_type_str = str(expected_type) if type(expected_type) is UnionType else expected_type.__name__
22
+ if isinstance(value, expected_type):
23
+ return
25
24
 
26
- msg = f"expected type '{expected_type_str}', got '{type(value).__name__}'"
27
- raise TypeValidationError(msg)
25
+ expected_type_str = str(expected_type) if type(expected_type) is UnionType else expected_type.__name__
26
+ msg = f"expected type '{expected_type_str}', got '{type(value).__name__}'"
27
+ raise TypeValidationError(msg)
28
28
 
29
29
 
30
30
  def validate_constraints(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statica
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: A minimalistic data validation library
5
5
  Author-email: Marcel Kröker <kroeker.marcel@gmail.com>
6
6
  License: MIT License
@@ -31,7 +31,7 @@ Classifier: Operating System :: OS Independent
31
31
  Classifier: Intended Audience :: Developers
32
32
  Classifier: Programming Language :: Python
33
33
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
34
- Requires-Python: <3.14,>=3.11
34
+ Requires-Python: <3.14,>=3.10
35
35
  Description-Content-Type: text/markdown
36
36
 
37
37
  # Statica
@@ -44,13 +44,13 @@ Statica is a Python library for defining and validating structured data with typ
44
44
 
45
45
  ## Why Statica?
46
46
 
47
- Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to libraries like pydantic.
47
+ Statica was created to address the need for a lightweight, flexible, and dependency-free alternative to libraries like pydantic.
48
48
  While pydantic is a powerful tool for data validation and parsing, Statica offers some distinct advantages in specific situations:
49
49
 
50
50
  1. **Lightweight**: Statica does not rely on any third-party dependencies, making it ideal for projects where minimizing external dependencies is a priority.
51
- 2. **Customizable Validation**: Statica allows fine-grained control over type and constraint validation through customizable field descriptors (`Field`) and error classes.
51
+ 2. **Performance**: For use cases where performance is critical. Pydantic needs `3x` more memory than Statica for the same models.
52
52
  3. **Ease of Use**: With its simple, Pythonic design, Statica is intuitive for developers already familiar with Python's `dataclasses` and type hinting. It avoids much of the magic that pydantic employs.
53
- 4. **Performance**: For use cases where performance, especially memory usage, is critical, Statica avoids some of the overhead introduced by the advanced features of pydantic.
53
+ 4. **Customizable Validation**: Statica allows fine-grained control over type and constraint validation through customizable field descriptors (`Field`) and error classes.
54
54
 
55
55
  ## Features
56
56
 
@@ -4,7 +4,6 @@ pyproject.toml
4
4
  statica/__init__.py
5
5
  statica/core.py
6
6
  statica/exceptions.py
7
- statica/types_utils.py
8
7
  statica/validators.py
9
8
  statica.egg-info/PKG-INFO
10
9
  statica.egg-info/SOURCES.txt
@@ -12,5 +11,4 @@ statica.egg-info/dependency_links.txt
12
11
  statica.egg-info/top_level.txt
13
12
  tests/test_aliasing.py
14
13
  tests/test_statica.py
15
- tests/test_types_utils.py
16
14
  tests/test_validators.py
@@ -1,19 +1,6 @@
1
1
  import pytest
2
2
 
3
- from statica.core import ConstraintValidationError, Field, Statica, TypeValidationError, get_expected_type
4
-
5
-
6
- def test_get_expected_type() -> None:
7
- class TestClass(Statica):
8
- required_field: str
9
- optional_field: str | None
10
- required_with_field: str = Field()
11
- optional_with_field: str | None = Field()
12
-
13
- assert get_expected_type(TestClass, "required_field") is str
14
- assert get_expected_type(TestClass, "optional_field") == str | None
15
- assert get_expected_type(TestClass, "required_with_field") is str
16
- assert get_expected_type(TestClass, "optional_with_field") == str | None
3
+ from statica.core import ConstraintValidationError, Field, Statica, TypeValidationError
17
4
 
18
5
 
19
6
  def test_basic_syntax() -> None:
@@ -1,5 +1,3 @@
1
- from typing import Generic, TypeVar
2
-
3
1
  import pytest
4
2
 
5
3
  from statica.core import TypeValidationError
@@ -24,14 +22,3 @@ def test_validate_type() -> None:
24
22
  validate_type(None, int)
25
23
  with pytest.raises(TypeValidationError):
26
24
  validate_type("abc", int | None)
27
-
28
- # Test with generics
29
-
30
- T = TypeVar("T")
31
-
32
- class GenericTest(Generic[T]):
33
- value: T
34
-
35
- validate_type(GenericTest[int](), GenericTest[int])
36
-
37
- validate_type(GenericTest[str](), GenericTest[str]) # Is this correct?
@@ -1,64 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from types import UnionType
4
- from typing import Any, get_args, get_origin, get_type_hints
5
-
6
-
7
- def type_allows_none(expected_type: Any) -> bool:
8
- """
9
- Check if the expected type allows None.
10
-
11
- Examples:
12
- .. code-block:: python
13
- _allows_none(int | None) # True
14
- _allows_none(int) # False
15
- """
16
- if isinstance(expected_type, UnionType):
17
- return type(None) in get_args(expected_type)
18
- return expected_type is type(None) or expected_type is Any
19
-
20
-
21
- def value_matches_type(value: Any | None, expected_type: Any) -> bool:
22
- """
23
- Check if the value matches the expected type.
24
- Handles basic types, Union types, and generic types.
25
-
26
- Examples:
27
- .. code-block:: python
28
- _value_matches_type(1, int) # True
29
- _value_matches_type(None, int | None) # True
30
- _value_matches_type(None, int) # False
31
- """
32
- # If expected_type is e.g. int | None, pass if value is None
33
- if type_allows_none(expected_type) and value is None:
34
- return True
35
-
36
- # Basic types like int, str, etc.
37
- if (origin := get_origin(expected_type)) is None:
38
- return isinstance(value, expected_type)
39
-
40
- # Handle Union types
41
- if origin is UnionType or origin is type(None) or origin is Any:
42
- types = get_args(expected_type)
43
- return any(value_matches_type(value, t) for t in types if t is not type(None))
44
-
45
- # Handle generic types
46
- return isinstance(value, origin)
47
-
48
-
49
- def get_expected_type(cls: type, attr_name: str) -> Any:
50
- """
51
- Get the expected type for a class attribute.
52
- Handles type hints and Field descriptors.
53
-
54
- Examples:
55
- .. code-block:: python
56
- class MyClass(Statica):
57
- age: int | None
58
- name: str = Field()
59
-
60
- _get_expected_type(MyClass, "age") # int | None
61
- _get_expected_type(MyClass, "name") # str
62
- """
63
-
64
- return get_type_hints(cls).get(attr_name, Any)
@@ -1,33 +0,0 @@
1
- from typing import Generic, TypeVar
2
-
3
- from statica.types_utils import type_allows_none, value_matches_type
4
-
5
-
6
- def test_allows_none() -> None:
7
- assert type_allows_none(type(None))
8
- assert type_allows_none(int | None)
9
- assert not type_allows_none(int)
10
-
11
-
12
- def test_value_matches_type() -> None:
13
- assert value_matches_type(1, int)
14
- assert value_matches_type("abc", str)
15
- assert not value_matches_type(1, str)
16
- assert value_matches_type(None, int | None)
17
- assert value_matches_type(1, int | None)
18
- assert not value_matches_type(None, int)
19
- assert value_matches_type(1, int | None)
20
- assert not value_matches_type("abc", int | None)
21
-
22
- # Test with generics
23
-
24
- T = TypeVar("T")
25
-
26
- class GenericTest(Generic[T]):
27
- value: T
28
-
29
- def __init__(self, value: T) -> None:
30
- self.value = value
31
-
32
- assert value_matches_type(GenericTest[int](1), GenericTest[int])
33
- assert value_matches_type(GenericTest[int](1), GenericTest[str]) # Is this correct?
File without changes
File without changes
File without changes
File without changes
File without changes