From 7fb31529124f51cceb32d1d7b8ae9b58c0606b17 Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Thu, 30 May 2024 14:14:04 +1000 Subject: [PATCH] Ignore other warnings when checking warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building on Michał's amazing work in PR#9040, extend the pattern to also ignore other warnings that are emitted by pytest.warns() when running under Pytest 8.x. This also continues to support Pytest 7.x. Co-authored-by: Michał Górny Fixes: #9025 --- tests/conftest.py | 2 +- tests/test_config.py | 84 +++++++++++--------- tests/test_deprecated.py | 156 ++++++++++++++++++++++---------------- tests/test_json_schema.py | 9 ++- tests/test_validators.py | 1 + 5 files changed, 146 insertions(+), 106 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e0c0dee018..421afade2e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -49,7 +49,7 @@ def _create_module_file(code, tmp_path, name): def disable_error_urls(): # Don't add URLs during docs tests when printing # Otherwise we'll get version numbers in the URLs that will update frequently - os.environ['PYDANTIC_ERRORS_OMIT_URL'] = 'true' + os.environ['PYDANTIC_ERRORS_INCLUDE_URL'] = 'false' @pytest.fixture diff --git a/tests/test_config.py b/tests/test_config.py index 1a5e31628a..31b2320326 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,6 +1,7 @@ import json import re import sys +import warnings from contextlib import nullcontext as does_not_raise from decimal import Decimal from inspect import signature @@ -349,45 +350,54 @@ class Model(BaseModel): assert m == m.model_copy() def test_config_class_is_deprecated(self): - with pytest.warns( - PydanticDeprecatedSince20, match='Support for class-based `config` is deprecated, use ConfigDict instead.' - ): - - class Config(BaseConfig): - pass + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns( + PydanticDeprecatedSince20, + match='Support for class-based `config` is deprecated, use ConfigDict instead.', + ): + + class Config(BaseConfig): + pass def test_config_class_attributes_are_deprecated(self): - with pytest.warns( - PydanticDeprecatedSince20, - match='Support for class-based `config` is deprecated, use ConfigDict instead.', - ): - assert BaseConfig.validate_assignment is False - - with pytest.warns( - PydanticDeprecatedSince20, - match='Support for class-based `config` is deprecated, use ConfigDict instead.', - ): - assert BaseConfig().validate_assignment is False - - with pytest.warns( - PydanticDeprecatedSince20, - match='Support for class-based `config` is deprecated, use ConfigDict instead.', - ): - - class Config(BaseConfig): - pass - - with pytest.warns( - PydanticDeprecatedSince20, - match='Support for class-based `config` is deprecated, use ConfigDict instead.', - ): - assert Config.validate_assignment is False - - with pytest.warns( - PydanticDeprecatedSince20, - match='Support for class-based `config` is deprecated, use ConfigDict instead.', - ): - assert Config().validate_assignment is False + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns( + PydanticDeprecatedSince20, + match='Support for class-based `config` is deprecated, use ConfigDict instead.', + ): + assert BaseConfig.validate_assignment is False + + with pytest.warns( + PydanticDeprecatedSince20, + match='Support for class-based `config` is deprecated, use ConfigDict instead.', + ): + assert BaseConfig().validate_assignment is False + + with pytest.warns( + PydanticDeprecatedSince20, + match='Support for class-based `config` is deprecated, use ConfigDict instead.', + ): + + class Config(BaseConfig): + pass + + with pytest.warns( + PydanticDeprecatedSince20, + match='Support for class-based `config` is deprecated, use ConfigDict instead.', + ): + assert Config.validate_assignment is False + + with pytest.warns( + PydanticDeprecatedSince20, + match='Support for class-based `config` is deprecated, use ConfigDict instead.', + ): + assert Config().validate_assignment is False @pytest.mark.filterwarnings('ignore:.* is deprecated.*:DeprecationWarning') def test_config_class_missing_attributes(self): diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py index fda6879597..2e58e8b433 100644 --- a/tests/test_deprecated.py +++ b/tests/test_deprecated.py @@ -1,5 +1,6 @@ import platform import re +import warnings from datetime import date, timedelta from pathlib import Path from types import SimpleNamespace @@ -273,8 +274,12 @@ class Model(BaseModel): x: int y: int - with pytest.warns(PydanticDeprecatedSince20, match='The `parse_raw` method is deprecated'): - model = Model.parse_raw('{"x": 1, "y": 2}') + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns(PydanticDeprecatedSince20, match='The `parse_raw` method is deprecated'): + model = Model.parse_raw('{"x": 1, "y": 2}') assert model.model_dump() == {'x': 1, 'y': 2} @@ -284,9 +289,13 @@ class Model(BaseModel): x: int y: int - with pytest.warns(PydanticDeprecatedSince20, match='The `parse_raw` method is deprecated'): - with pytest.raises(ValidationError, match='1 validation error for Model') as exc_info: - Model.parse_raw('invalid') + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns(PydanticDeprecatedSince20, match='The `parse_raw` method is deprecated'): + with pytest.raises(ValidationError, match='1 validation error for Model') as exc_info: + Model.parse_raw('invalid') # insert_assert(exc_info.value.errors(include_url=False)) assert exc_info.value.errors(include_url=False) == [ @@ -435,10 +444,14 @@ class Model(BaseModel): def test_field_include_deprecation(): m = '`include` is deprecated and does nothing. It will be removed, use `exclude` instead' - with pytest.warns(PydanticDeprecatedSince20, match=m): + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns(PydanticDeprecatedSince20, match=m): - class Model(BaseModel): - x: int = Field(include=True) + class Model(BaseModel): + x: int = Field(include=True) def test_unique_items_items(): @@ -664,10 +677,14 @@ def test_parse_obj(): def test_parse_file(tmp_path): path = tmp_path / 'test.json' path.write_text('{"x": 12}') - with pytest.warns( - PydanticDeprecatedSince20, match='^The `parse_file` method is deprecated; load the data from file,' - ): - assert SimpleModel.parse_file(str(path)).model_dump() == {'x': 12} + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns( + PydanticDeprecatedSince20, match='^The `parse_file` method is deprecated; load the data from file,' + ): + assert SimpleModel.parse_file(str(path)).model_dump() == {'x': 12} def test_construct(): @@ -736,61 +753,68 @@ def test_deprecated_module(tmp_path: Path) -> None: class Model(BaseModel): x: int - assert hasattr(parse_obj_as, '__deprecated__') - with pytest.warns( - PydanticDeprecatedSince20, - match='`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.', - ): - parse_obj_as(Model, {'x': 1}) - - assert hasattr(schema_json_of, '__deprecated__') - with pytest.warns( - PydanticDeprecatedSince20, - match='`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', - ): - schema_json_of(Model) - - assert hasattr(schema_of, '__deprecated__') - with pytest.warns( - PydanticDeprecatedSince20, match='`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.' - ): - schema_of(Model) - - assert hasattr(load_str_bytes, '__deprecated__') - with pytest.warns(PydanticDeprecatedSince20, match='`load_str_bytes` is deprecated.'): - load_str_bytes('{"x": 1}') - - assert hasattr(load_file, '__deprecated__') - file = tmp_path / 'main.py' - file.write_text('{"x": 1}') - with pytest.warns(PydanticDeprecatedSince20, match='`load_file` is deprecated.'): - load_file(file) - - assert hasattr(pydantic_encoder, '__deprecated__') - with pytest.warns( - PydanticDeprecatedSince20, - match='`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.', - ): - pydantic_encoder(Model(x=1)) - - assert hasattr(custom_pydantic_encoder, '__deprecated__') - with pytest.warns( - PydanticDeprecatedSince20, match='`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.' - ): - custom_pydantic_encoder({int: lambda x: str(x)}, Model(x=1)) - - assert hasattr(timedelta_isoformat, '__deprecated__') - with pytest.warns(PydanticDeprecatedSince20, match='`timedelta_isoformat` is deprecated.'): - timedelta_isoformat(timedelta(seconds=1)) - - with pytest.warns( - PydanticDeprecatedSince20, match='The `validate_arguments` method is deprecated; use `validate_call` instead.' - ): - - def test(a: int, b: int): - pass + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + assert hasattr(parse_obj_as, '__deprecated__') + with pytest.warns( + PydanticDeprecatedSince20, + match='`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.', + ): + parse_obj_as(Model, {'x': 1}) + + assert hasattr(schema_json_of, '__deprecated__') + with pytest.warns( + PydanticDeprecatedSince20, + match='`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', + ): + schema_json_of(Model) + + assert hasattr(schema_of, '__deprecated__') + with pytest.warns( + PydanticDeprecatedSince20, + match='`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', + ): + schema_of(Model) + + assert hasattr(load_str_bytes, '__deprecated__') + with pytest.warns(PydanticDeprecatedSince20, match='`load_str_bytes` is deprecated.'): + load_str_bytes('{"x": 1}') + + assert hasattr(load_file, '__deprecated__') + file = tmp_path / 'main.py' + file.write_text('{"x": 1}') + with pytest.warns(PydanticDeprecatedSince20, match='`load_file` is deprecated.'): + load_file(file) + + assert hasattr(pydantic_encoder, '__deprecated__') + with pytest.warns( + PydanticDeprecatedSince20, + match='`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.', + ): + pydantic_encoder(Model(x=1)) + + assert hasattr(custom_pydantic_encoder, '__deprecated__') + with pytest.warns( + PydanticDeprecatedSince20, + match='`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.', + ): + custom_pydantic_encoder({int: lambda x: str(x)}, Model(x=1)) + + assert hasattr(timedelta_isoformat, '__deprecated__') + with pytest.warns(PydanticDeprecatedSince20, match='`timedelta_isoformat` is deprecated.'): + timedelta_isoformat(timedelta(seconds=1)) + + with pytest.warns( + PydanticDeprecatedSince20, + match='The `validate_arguments` method is deprecated; use `validate_call` instead.', + ): + + def test(a: int, b: int): + pass - validate_arguments()(test) + validate_arguments()(test) def test_deprecated_color(): diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index e6c60557a5..85d833c15c 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -5,6 +5,7 @@ import re import sys import typing +import warnings from datetime import date, datetime, time, timedelta from decimal import Decimal from enum import Enum, IntEnum @@ -1385,8 +1386,12 @@ class Model(BaseModel): class MyGenerator(GenerateJsonSchema): ignored_warning_kinds = () - with pytest.warns(PydanticJsonSchemaWarning, match=warning_match): - model_schema = Model.model_json_schema(schema_generator=MyGenerator) + with warnings.catch_warnings(): + # we need to explicitly ignore the other warning in pytest-8 + # TODO: rewrite it to use two nested pytest.warns() when pytest-7 is no longer supported + warnings.simplefilter('ignore') + with pytest.warns(PydanticJsonSchemaWarning, match=warning_match): + model_schema = Model.model_json_schema(schema_generator=MyGenerator) assert model_schema == { 'properties': {'callback': {'title': 'Callback', 'type': 'integer'}}, 'title': 'Model', diff --git a/tests/test_validators.py b/tests/test_validators.py index 52a8bda88f..b286c49b1b 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -2554,6 +2554,7 @@ class Model(BaseModel): assert Model(x=1, y=2).model_dump() == {'x': 2, 'y': 3} +@pytest.mark.filterwarnings('ignore:Pydantic V1 style `@root_validator` validators are deprecated.*:pydantic.warnings.PydanticDeprecatedSince20') def test_root_validator_allow_reuse_same_field(): with pytest.warns(UserWarning, match='`root_val` overrides an existing Pydantic `@root_validator` decorator'):