From f9b09bef24cf71c2850f30ad34f1bf05743dd56b58f6081218dea9dd7cf02bec Mon Sep 17 00:00:00 2001 From: Daniel Garcia Date: Wed, 20 Mar 2024 13:32:14 +0000 Subject: [PATCH] - Add pydantic2.patch to make tests compatible with pydantic2 gh#scrapy/itemadapter#76 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-itemadapter?expand=0&rev=10 --- pydantic2.patch | 148 +++++++++++++++++++++++++++++++++++++ python-itemadapter.changes | 6 ++ python-itemadapter.spec | 9 ++- 3 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 pydantic2.patch diff --git a/pydantic2.patch b/pydantic2.patch new file mode 100644 index 0000000..b0e6c41 --- /dev/null +++ b/pydantic2.patch @@ -0,0 +1,148 @@ +Index: itemadapter-0.8.0/README.md +=================================================================== +--- itemadapter-0.8.0.orig/README.md ++++ itemadapter-0.8.0/README.md +@@ -193,7 +193,7 @@ The returned value is taken from the fol + for `dataclass`-based items + * [`attr.Attribute.metadata`](https://www.attrs.org/en/stable/examples.html#metadata) + for `attrs`-based items +- * [`pydantic.fields.FieldInfo`](https://pydantic-docs.helpmanual.io/usage/schema/#field-customisation) ++ * [`pydantic.fields.FieldInfo`](https://docs.pydantic.dev/latest/api/fields/#pydantic.fields.FieldInfo) + for `pydantic`-based items + + #### class method `get_field_names_from_class(item_class: type) -> Optional[list[str]]` +Index: itemadapter-0.8.0/itemadapter/adapter.py +=================================================================== +--- itemadapter-0.8.0.orig/itemadapter/adapter.py ++++ itemadapter-0.8.0/itemadapter/adapter.py +@@ -179,24 +179,24 @@ class PydanticAdapter(AdapterInterface): + + @classmethod + def get_field_names_from_class(cls, item_class: type) -> Optional[List[str]]: +- return list(item_class.__fields__.keys()) # type: ignore[attr-defined] ++ return list(item_class.model_fields.keys()) # type: ignore[attr-defined] + + def field_names(self) -> KeysView: +- return KeysView(self.item.__fields__) ++ return KeysView(self.item.model_fields) + + def __getitem__(self, field_name: str) -> Any: +- if field_name in self.item.__fields__: ++ if field_name in self.item.model_fields: + return getattr(self.item, field_name) + raise KeyError(field_name) + + def __setitem__(self, field_name: str, value: Any) -> None: +- if field_name in self.item.__fields__: ++ if field_name in self.item.model_fields: + setattr(self.item, field_name, value) + else: + raise KeyError(f"{self.item.__class__.__name__} does not support field: {field_name}") + + def __delitem__(self, field_name: str) -> None: +- if field_name in self.item.__fields__: ++ if field_name in self.item.model_fields: + try: + delattr(self.item, field_name) + except AttributeError: +@@ -205,7 +205,7 @@ class PydanticAdapter(AdapterInterface): + raise KeyError(f"{self.item.__class__.__name__} does not support field: {field_name}") + + def __iter__(self) -> Iterator: +- return iter(attr for attr in self.item.__fields__ if hasattr(self.item, attr)) ++ return iter(attr for attr in self.item.model_fields if hasattr(self.item, attr)) + + def __len__(self) -> int: + return len(list(iter(self))) +Index: itemadapter-0.8.0/itemadapter/utils.py +=================================================================== +--- itemadapter-0.8.0.orig/itemadapter/utils.py ++++ itemadapter-0.8.0/itemadapter/utils.py +@@ -23,30 +23,21 @@ def _is_pydantic_model(obj: Any) -> bool + + def _get_pydantic_model_metadata(item_model: Any, field_name: str) -> MappingProxyType: + metadata = {} +- field = item_model.__fields__[field_name].field_info ++ field = item_model.model_fields[field_name] + + for attribute in [ + "alias", + "title", + "description", +- "const", +- "gt", +- "ge", +- "lt", +- "le", +- "multiple_of", +- "min_items", +- "max_items", +- "min_length", +- "max_length", +- "regex", + ]: + value = getattr(field, attribute) + if value is not None: + metadata[attribute] = value +- if not field.allow_mutation: +- metadata["allow_mutation"] = field.allow_mutation +- metadata.update(field.extra) ++ if field.frozen is not None: ++ metadata["frozen"] = field.frozen ++ ++ if field.json_schema_extra is not None: ++ metadata.update(field.json_schema_extra) + + return MappingProxyType(metadata) + +Index: itemadapter-0.8.0/tests/__init__.py +=================================================================== +--- itemadapter-0.8.0.orig/tests/__init__.py ++++ itemadapter-0.8.0/tests/__init__.py +@@ -102,7 +102,7 @@ else: + + + try: +- from pydantic import BaseModel, Field as PydanticField ++ from pydantic import ConfigDict, BaseModel, Field as PydanticField + except ImportError: + PydanticModel = None + PydanticSpecialCasesModel = None +@@ -125,11 +125,9 @@ else: + special_cases: Optional[int] = PydanticField( + default_factory=lambda: None, + alias="special_cases", +- allow_mutation=False, ++ frozen=False, + ) +- +- class Config: +- validate_assignment = True ++ model_config = ConfigDict(validate_assignment=True) + + class PydanticModelNested(BaseModel): + nested: PydanticModel +@@ -139,9 +137,7 @@ else: + set_: set + tuple_: tuple + int_: int +- +- class Config: +- arbitrary_types_allowed = True ++ model_config = ConfigDict(arbitrary_types_allowed=True) + + class PydanticModelSubclassed(PydanticModel): + subclassed: bool = PydanticField( +Index: itemadapter-0.8.0/tests/test_adapter_pydantic.py +=================================================================== +--- itemadapter-0.8.0.orig/tests/test_adapter_pydantic.py ++++ itemadapter-0.8.0/tests/test_adapter_pydantic.py +@@ -73,7 +73,7 @@ class DataclassTestCase(unittest.TestCas + ) + self.assertEqual( + get_field_meta_from_class(PydanticSpecialCasesModel, "special_cases"), +- MappingProxyType({"alias": "special_cases", "allow_mutation": False}), ++ MappingProxyType({"alias": "special_cases", "frozen": False}), + ) + with self.assertRaises(KeyError, msg="PydanticModel does not support field: non_existent"): + get_field_meta_from_class(PydanticModel, "non_existent") diff --git a/python-itemadapter.changes b/python-itemadapter.changes index 6264089..e613403 100644 --- a/python-itemadapter.changes +++ b/python-itemadapter.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Mar 20 13:31:31 UTC 2024 - Daniel Garcia + +- Add pydantic2.patch to make tests compatible with pydantic2 + gh#scrapy/itemadapter#76 + ------------------------------------------------------------------- Thu Dec 7 22:47:54 UTC 2023 - Dirk Müller diff --git a/python-itemadapter.spec b/python-itemadapter.spec index 5a2983b..f0a748c 100644 --- a/python-itemadapter.spec +++ b/python-itemadapter.spec @@ -1,7 +1,7 @@ # -# spec file +# spec file for package python-itemadapter # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # Copyright (c) 2016, Martin Hauke # # All modifications and additions to the file contributed by third parties @@ -17,7 +17,6 @@ # -%{?!python_module:%define python_module() python-%{**} python3-%{**}} %global flavor @BUILD_FLAVOR@%{nil} %if "%{flavor}" == "test" %define psuffix -test @@ -36,6 +35,8 @@ Summary: Wrapper for data container objects License: BSD-3-Clause URL: https://github.com/scrapy/itemadapter Source: https://github.com/scrapy/itemadapter/archive/v%{version}.tar.gz#/itemadapter-%{version}.tar.gz +# PATCH-FIX-UPSTREAM pydantic2.patch gh#scrapy/itemadapter#76 +Patch0: pydantic2.patch BuildRequires: %{python_module setuptools >= 40.5.0} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -53,7 +54,7 @@ a common interface to handle objects of different types in an uniform manner, regardless of their underlying implementation. %prep -%setup -q -n itemadapter-%{version} +%autosetup -p1 -n itemadapter-%{version} %build %python_build