Fix incorrect deprecation warnings on defaults (#348)

This change ensures that deprecation warnings are only raised when
either a deprecated field is explicitly set or a deprecated message is
initialised.

Resolves: #347
This commit is contained in:
Arun Babu Neelicattu 2022-03-12 00:36:14 +01:00 committed by GitHub
parent 9c1bf25304
commit 69f4192341
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 39 deletions

View File

@ -1,16 +1,15 @@
import dataclasses
import enum
import inspect
import json
import math
import struct
import sys
import typing
import warnings
from abc import ABC
from base64 import b64decode, b64encode
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from dateutil.parser import isoparse
from typing import (
Any,
Callable,
@ -26,12 +25,13 @@ from typing import (
get_type_hints,
)
from dateutil.parser import isoparse
from ._types import T
from ._version import __version__
from .casing import camel_case, safe_snake_case, snake_case
from .grpc.grpclib_client import ServiceStub
# Proto 3 data types
TYPE_ENUM = "enum"
TYPE_BOOL = "bool"
@ -867,7 +867,10 @@ class Message(ABC):
return field_cls
def _get_field_default(self, field_name: str) -> Any:
return self._betterproto.default_gen[field_name]()
with warnings.catch_warnings():
# ignore warnings when initialising deprecated field defaults
warnings.filterwarnings("ignore", category=DeprecationWarning)
return self._betterproto.default_gen[field_name]()
@classmethod
def _get_field_default_gen(cls, field: dataclasses.Field) -> Any:
@ -1288,6 +1291,22 @@ class Message(ABC):
"""
return self.from_dict(json.loads(value))
def is_set(self, name: str) -> bool:
"""
Check if field with the given name has been set.
Parameters
-----------
name: :class:`str`
The name of the field to check for.
Returns
--------
:class:`bool`
`True` if field has been set, otherwise `False`.
"""
return self.__raw_get(name) is not PLACEHOLDER
def serialized_on_wire(message: Message) -> bool:
"""

View File

@ -63,7 +63,7 @@ class {{ message.py_name }}(betterproto.Message):
{% endif %}
super().__post_init__()
{% for field in message.deprecated_fields %}
if self.{{ field }}:
if self.is_set("{{ field }}"):
warnings.warn("{{ message.py_name }}.{{ field }} is deprecated", DeprecationWarning)
{% endfor %}
{% endif %}

View File

@ -1,4 +1,6 @@
{
"v": 10,
"message": {
"value": "hello"
},
"value": 10
}

View File

@ -4,8 +4,11 @@ package deprecated;
// Some documentation about the Test message.
message Test {
// Some documentation about the value.
option deprecated = true;
int32 v = 1 [deprecated=true];
Message message = 1 [deprecated=true];
int32 value = 2;
}
message Message {
option deprecated = true;
string value = 1;
}

View File

@ -1,4 +0,0 @@
{
"v": 10,
"value": 10
}

View File

@ -1,10 +0,0 @@
syntax = "proto3";
package deprecated_field;
// Some documentation about the Test message.
message Test {
// Some documentation about the value.
int32 v = 1 [deprecated=true];
int32 value = 2;
}

View File

@ -1,26 +1,42 @@
import warnings
import pytest
from tests.output_betterproto.deprecated import Test as DeprecatedMessageTest
from tests.output_betterproto.deprecated_field import Test as DeprecatedFieldTest
from tests.output_betterproto.deprecated import Message, Test
@pytest.fixture
def message():
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
return Message(value="hello")
def test_deprecated_message():
with pytest.deprecated_call():
DeprecatedMessageTest(value=10)
with pytest.warns(DeprecationWarning) as record:
Message(value="hello")
assert len(record) == 1
assert str(record[0].message) == f"{Message.__name__} is deprecated"
def test_deprecated_message_with_deprecated_field():
def test_message_with_deprecated_field(message):
with pytest.warns(DeprecationWarning) as record:
Test(message=message, value=10)
assert len(record) == 1
assert str(record[0].message) == f"{Test.__name__}.message is deprecated"
def test_message_with_deprecated_field_not_set(message):
with pytest.warns(None) as record:
DeprecatedMessageTest(v=10, value=10)
assert len(record) == 2
Test(value=10)
assert not record
def test_message_with_deprecated_field_not_set_default(message):
with pytest.warns(None) as record:
_ = Test(value=10).message
def test_deprecated_field_warning():
with pytest.deprecated_call():
DeprecatedFieldTest(v=10, value=10)
def test_deprecated_field_no_warning():
with pytest.warns(None) as record:
DeprecatedFieldTest(value=10)
assert not record