Skip to content

Commit 9d0f23d

Browse files
Revert "Merge pull request #108 from miladmahmoodi/feat/type-safe-field-references"
This reverts commit 747d93b, reversing changes made to d4e4922.
1 parent 7794ab4 commit 9d0f23d

4 files changed

Lines changed: 6 additions & 264 deletions

File tree

archipy/models/dtos/base_dtos.py

Lines changed: 4 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,17 @@
11
from enum import Enum
2-
from typing import Any, Self, TypeVar
2+
from typing import TypeVar
33

44
from pydantic import BaseModel, ConfigDict
5-
from pydantic._internal._model_construction import ModelMetaclass
65

76
# Generic types
87
T = TypeVar("T", bound=Enum)
98

109

11-
class FieldStr(str):
12-
"""Type-safe field name reference string.
13-
14-
Allows referencing Pydantic model field names as class attributes
15-
instead of hardcoded strings, enabling IDE autocompletion and
16-
refactoring support.
17-
18-
Examples:
19-
>>> class UserDTO(BaseDTO):
20-
... name: str
21-
... email: str
22-
>>> UserDTO.name # returns FieldStr("name")
23-
'name'
24-
>>> UserDTO.name == "name"
25-
True
26-
"""
27-
28-
__slots__ = ("name",)
29-
30-
def __new__(cls, value: str) -> Self:
31-
"""Create a new FieldStr instance.
32-
33-
Args:
34-
value: The field name string.
35-
36-
Returns:
37-
FieldStr: A string subclass carrying the field name.
38-
"""
39-
obj = super().__new__(cls, value)
40-
obj.name = value
41-
return obj
42-
43-
44-
class BaseMeta(ModelMetaclass):
45-
"""Metaclass that adds FieldStr class attributes for each Pydantic model field.
46-
47-
After Pydantic's ModelMetaclass constructs the class and populates
48-
``model_fields``, this metaclass overwrites the corresponding class
49-
attributes with :class:`FieldStr` instances so that
50-
``MyDTO.field_name`` returns a type-safe string equal to ``"field_name"``.
51-
52-
Instance attribute access is unaffected because Python resolves
53-
instance ``__dict__`` entries before class attributes.
54-
"""
55-
56-
def __new__(mcs, name: str, bases: tuple[type, ...], namespace: dict[str, Any], **kwargs: Any) -> type: # noqa: ANN401
57-
"""Create a new class with FieldStr attributes for each model field.
58-
59-
Args:
60-
name: The class name.
61-
bases: The base classes.
62-
namespace: The class namespace.
63-
**kwargs: Additional keyword arguments forwarded to ModelMetaclass.
64-
65-
Returns:
66-
The newly created class with FieldStr attributes.
67-
"""
68-
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
69-
for field_name in cls.model_fields: # ty:ignore[unresolved-attribute]
70-
setattr(cls, field_name, FieldStr(field_name))
71-
return cls
72-
73-
74-
class BaseDTO(BaseModel, metaclass=BaseMeta):
10+
class BaseDTO(BaseModel):
7511
"""Base Data Transfer Object class.
7612
77-
This class extends Pydantic's BaseModel with a custom metaclass that
78-
provides type-safe field name references. After class construction,
79-
each field name is accessible as a :class:`FieldStr` class attribute.
80-
81-
Examples:
82-
>>> class ProductDTO(BaseDTO):
83-
... title: str
84-
... price: float
85-
>>> ProductDTO.title # FieldStr("title")
86-
'title'
87-
>>> product = ProductDTO(title="Widget", price=9.99)
88-
>>> product.title # actual value
89-
'Widget'
13+
This class extends Pydantic's BaseModel to provide common configuration
14+
for all DTOs in the application.
9015
"""
9116

9217
model_config = ConfigDict(

archipy/models/dtos/range_dtos.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def validate_range(self) -> Self:
4646
# The protocol ensures both values support comparison
4747
try:
4848
if self.from_ > self.to: # type: ignore[operator]
49-
raise OutOfRangeError(field_name=type(self).from_) # type: ignore[arg-type]
49+
raise OutOfRangeError(field_name="from_")
5050
except TypeError:
5151
# If comparison fails, skip validation (shouldn't happen with proper types)
5252
pass
@@ -175,7 +175,7 @@ def validate_interval_constraints(self) -> Self:
175175
if max_to_age:
176176
age_threshold = current_time - max_to_age
177177
if self.to < age_threshold:
178-
raise OutOfRangeError(field_name=type(self).to) # type: ignore[arg-type]
178+
raise OutOfRangeError(field_name="to")
179179

180180
# Calculate number of intervals
181181
step = self.INTERVAL_TO_TIMEDELTA[self.interval]

features/base_dtos.feature

Lines changed: 0 additions & 37 deletions
This file was deleted.

features/steps/base_dtos_steps.py

Lines changed: 0 additions & 146 deletions
This file was deleted.

0 commit comments

Comments
 (0)