Skip to content

fix incorrect deprecation warnings on defaults #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions src/betterproto/__init__.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -26,12 +25,13 @@
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"
Expand Down Expand Up @@ -867,7 +867,10 @@ def _cls_for(cls, field: dataclasses.Field, index: int = 0) -> Type:
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:
Expand Down Expand Up @@ -1288,6 +1291,22 @@ def from_json(self: T, value: Union[str, bytes]) -> T:
"""
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:
"""
Expand Down
2 changes: 1 addition & 1 deletion src/betterproto/templates/template.py.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand Down
4 changes: 3 additions & 1 deletion tests/inputs/deprecated/deprecated.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"v": 10,
"message": {
"value": "hello"
},
"value": 10
}
9 changes: 6 additions & 3 deletions tests/inputs/deprecated/deprecated.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 0 additions & 4 deletions tests/inputs/deprecated_field/deprecated_field.json

This file was deleted.

10 changes: 0 additions & 10 deletions tests/inputs/deprecated_field/deprecated_field.proto

This file was deleted.

42 changes: 29 additions & 13 deletions tests/test_deprecated.py
Original file line number Diff line number Diff line change
@@ -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():
with pytest.warns(None) as record:
DeprecatedMessageTest(v=10, value=10)
assert len(record) == 2

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_deprecated_field_warning():
with pytest.deprecated_call():
DeprecatedFieldTest(v=10, value=10)

def test_message_with_deprecated_field_not_set(message):
with pytest.warns(None) as record:
Test(value=10)

assert not record

def test_deprecated_field_no_warning():

def test_message_with_deprecated_field_not_set_default(message):
with pytest.warns(None) as record:
DeprecatedFieldTest(value=10)
_ = Test(value=10).message

assert not record