Skip to content

Commit 5e10ea6

Browse files
authored
Merge 5a7f4e8 into 6aca919
2 parents 6aca919 + 5a7f4e8 commit 5e10ea6

File tree

6 files changed

+21
-34
lines changed

6 files changed

+21
-34
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Currently supported types are:
1515
* [`dict`](https://docs.python.org/3/library/stdtypes.html#dict)
1616
* [`dataclass`](https://docs.python.org/3/library/dataclasses.html)-based classes
1717
* [`attrs`](https://www.attrs.org)-based classes
18-
* [`pydantic`](https://pydantic-docs.helpmanual.io/)-based classes (`pydantic>=2` not yet supported)
18+
* [`pydantic`](https://pydantic-docs.helpmanual.io/)-based classes
1919

2020
Additionally, interaction with arbitrary types is supported, by implementing
2121
a pre-defined interface (see [extending `itemadapter`](#extending-itemadapter)).
@@ -28,7 +28,7 @@ a pre-defined interface (see [extending `itemadapter`](#extending-itemadapter)).
2828
* [`scrapy`](https://scrapy.org/): optional, needed to interact with `scrapy` items
2929
* [`attrs`](https://pypi.org/project/attrs/): optional, needed to interact with `attrs`-based items
3030
* [`pydantic`](https://pypi.org/project/pydantic/): optional, needed to interact with
31-
`pydantic`-based items (`pydantic>=2` not yet supported)
31+
`pydantic`-based items
3232

3333
---
3434

@@ -196,7 +196,7 @@ The returned value is taken from the following sources, depending on the item ty
196196
for `dataclass`-based items
197197
* [`attr.Attribute.metadata`](https://www.attrs.org/en/stable/examples.html#metadata)
198198
for `attrs`-based items
199-
* [`pydantic.fields.FieldInfo`](https://pydantic-docs.helpmanual.io/usage/schema/#field-customisation)
199+
* [`pydantic.fields.FieldInfo`](https://docs.pydantic.dev/latest/api/fields/#pydantic.fields.FieldInfo)
200200
for `pydantic`-based items
201201

202202
#### class method `get_field_names_from_class(item_class: type) -> Optional[list[str]]`

itemadapter/adapter.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -179,24 +179,24 @@ def get_field_meta_from_class(cls, item_class: type, field_name: str) -> Mapping
179179

180180
@classmethod
181181
def get_field_names_from_class(cls, item_class: type) -> Optional[List[str]]:
182-
return list(item_class.__fields__.keys()) # type: ignore[attr-defined]
182+
return list(item_class.model_fields.keys()) # type: ignore[attr-defined]
183183

184184
def field_names(self) -> KeysView:
185-
return KeysView(self.item.__fields__)
185+
return KeysView(self.item.model_fields)
186186

187187
def __getitem__(self, field_name: str) -> Any:
188-
if field_name in self.item.__fields__:
188+
if field_name in self.item.model_fields:
189189
return getattr(self.item, field_name)
190190
raise KeyError(field_name)
191191

192192
def __setitem__(self, field_name: str, value: Any) -> None:
193-
if field_name in self.item.__fields__:
193+
if field_name in self.item.model_fields:
194194
setattr(self.item, field_name, value)
195195
else:
196196
raise KeyError(f"{self.item.__class__.__name__} does not support field: {field_name}")
197197

198198
def __delitem__(self, field_name: str) -> None:
199-
if field_name in self.item.__fields__:
199+
if field_name in self.item.model_fields:
200200
try:
201201
delattr(self.item, field_name)
202202
except AttributeError:
@@ -205,7 +205,7 @@ def __delitem__(self, field_name: str) -> None:
205205
raise KeyError(f"{self.item.__class__.__name__} does not support field: {field_name}")
206206

207207
def __iter__(self) -> Iterator:
208-
return iter(attr for attr in self.item.__fields__ if hasattr(self.item, attr))
208+
return iter(attr for attr in self.item.model_fields if hasattr(self.item, attr))
209209

210210
def __len__(self) -> int:
211211
return len(list(iter(self)))

itemadapter/utils.py

+6-15
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,21 @@ def _is_pydantic_model(obj: Any) -> bool:
2323

2424
def _get_pydantic_model_metadata(item_model: Any, field_name: str) -> MappingProxyType:
2525
metadata = {}
26-
field = item_model.__fields__[field_name].field_info
26+
field = item_model.model_fields[field_name]
2727

2828
for attribute in [
2929
"alias",
3030
"title",
3131
"description",
32-
"const",
33-
"gt",
34-
"ge",
35-
"lt",
36-
"le",
37-
"multiple_of",
38-
"min_items",
39-
"max_items",
40-
"min_length",
41-
"max_length",
42-
"regex",
4332
]:
4433
value = getattr(field, attribute)
4534
if value is not None:
4635
metadata[attribute] = value
47-
if not field.allow_mutation:
48-
metadata["allow_mutation"] = field.allow_mutation
49-
metadata.update(field.extra)
36+
if field.frozen is not None:
37+
metadata["frozen"] = field.frozen
38+
39+
if field.json_schema_extra is not None:
40+
metadata.update(field.json_schema_extra)
5041

5142
return MappingProxyType(metadata)
5243

tests/__init__.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class AttrsItemEmpty:
102102

103103

104104
try:
105-
from pydantic import BaseModel, Field as PydanticField
105+
from pydantic import ConfigDict, BaseModel, Field as PydanticField
106106
except ImportError:
107107
PydanticModel = None
108108
PydanticSpecialCasesModel = None
@@ -125,11 +125,9 @@ class PydanticSpecialCasesModel(BaseModel):
125125
special_cases: Optional[int] = PydanticField(
126126
default_factory=lambda: None,
127127
alias="special_cases",
128-
allow_mutation=False,
128+
frozen=False,
129129
)
130-
131-
class Config:
132-
validate_assignment = True
130+
model_config = ConfigDict(validate_assignment=True)
133131

134132
class PydanticModelNested(BaseModel):
135133
nested: PydanticModel
@@ -139,9 +137,7 @@ class PydanticModelNested(BaseModel):
139137
set_: set
140138
tuple_: tuple
141139
int_: int
142-
143-
class Config:
144-
arbitrary_types_allowed = True
140+
model_config = ConfigDict(arbitrary_types_allowed=True)
145141

146142
class PydanticModelSubclassed(PydanticModel):
147143
subclassed: bool = PydanticField(

tests/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
attrs
2-
pydantic<2
2+
pydantic>2
33
pytest-cov>=2.8
44
pytest>=5.4
55
scrapy>=2.0

tests/test_adapter_pydantic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_true(self):
7373
)
7474
self.assertEqual(
7575
get_field_meta_from_class(PydanticSpecialCasesModel, "special_cases"),
76-
MappingProxyType({"alias": "special_cases", "allow_mutation": False}),
76+
MappingProxyType({"alias": "special_cases", "frozen": False}),
7777
)
7878
with self.assertRaises(KeyError, msg="PydanticModel does not support field: non_existent"):
7979
get_field_meta_from_class(PydanticModel, "non_existent")

0 commit comments

Comments
 (0)