Raise AttributeError on attempts to access unset oneof fields (#510)

This commit is contained in:
Alexander Khabarov
2023-07-21 13:26:30 +01:00
committed by GitHub
parent 098989e9e9
commit 6faac1d1ca
11 changed files with 116 additions and 29 deletions

View File

@@ -50,8 +50,10 @@ def test_bytes_are_the_same_for_oneof():
# None of these fields were explicitly set BUT they should not actually be null
# themselves
assert isinstance(message.foo, Foo)
assert isinstance(message2.foo, Foo)
assert not hasattr(message, "foo")
assert object.__getattribute__(message, "foo") == betterproto.PLACEHOLDER
assert not hasattr(message2, "foo")
assert object.__getattribute__(message2, "foo") == betterproto.PLACEHOLDER
assert isinstance(message_reference.foo, ReferenceFoo)
assert isinstance(message_reference2.foo, ReferenceFoo)

View File

@@ -18,9 +18,8 @@ def test_which_one_of_returns_enum_with_default_value():
get_test_case_json_data("oneof_enum", "oneof_enum-enum-0.json")[0].json
)
assert message.move == Move(
x=0, y=0
) # Proto3 will default this as there is no null
assert not hasattr(message, "move")
assert object.__getattribute__(message, "move") == betterproto.PLACEHOLDER
assert message.signal == Signal.PASS
assert betterproto.which_one_of(message, "action") == ("signal", Signal.PASS)
@@ -33,9 +32,8 @@ def test_which_one_of_returns_enum_with_non_default_value():
message.from_json(
get_test_case_json_data("oneof_enum", "oneof_enum-enum-1.json")[0].json
)
assert message.move == Move(
x=0, y=0
) # Proto3 will default this as there is no null
assert not hasattr(message, "move")
assert object.__getattribute__(message, "move") == betterproto.PLACEHOLDER
assert message.signal == Signal.RESIGN
assert betterproto.which_one_of(message, "action") == ("signal", Signal.RESIGN)
@@ -44,5 +42,6 @@ def test_which_one_of_returns_second_field_when_set():
message = Test()
message.from_json(get_test_case_json_data("oneof_enum")[0].json)
assert message.move == Move(x=2, y=3)
assert message.signal == Signal.PASS
assert not hasattr(message, "signal")
assert object.__getattribute__(message, "signal") == betterproto.PLACEHOLDER
assert betterproto.which_one_of(message, "action") == ("move", Move(x=2, y=3))

View File

@@ -0,0 +1,46 @@
from dataclasses import dataclass
import pytest
import betterproto
def test_oneof_pattern_matching():
@dataclass
class Sub(betterproto.Message):
val: int = betterproto.int32_field(1)
@dataclass
class Foo(betterproto.Message):
bar: int = betterproto.int32_field(1, group="group1")
baz: str = betterproto.string_field(2, group="group1")
sub: Sub = betterproto.message_field(3, group="group2")
abc: str = betterproto.string_field(4, group="group2")
foo = Foo(baz="test1", abc="test2")
match foo:
case Foo(bar=_):
pytest.fail("Matched 'bar' instead of 'baz'")
case Foo(baz=v):
assert v == "test1"
case _:
pytest.fail("Matched neither 'bar' nor 'baz'")
match foo:
case Foo(sub=_):
pytest.fail("Matched 'sub' instead of 'abc'")
case Foo(abc=v):
assert v == "test2"
case _:
pytest.fail("Matched neither 'sub' nor 'abc'")
foo.sub = Sub(val=1)
match foo:
case Foo(sub=Sub(val=v)):
assert v == 1
case Foo(abc=v):
pytest.fail("Matched 'abc' instead of 'sub'")
case _:
pytest.fail("Matched neither 'sub' nor 'abc'")

View File

@@ -1,4 +1,5 @@
import json
import sys
from copy import (
copy,
deepcopy,
@@ -18,6 +19,8 @@ from typing import (
Optional,
)
import pytest
import betterproto
@@ -151,17 +154,18 @@ def test_oneof_support():
foo.baz = "test"
# Other oneof fields should now be unset
assert foo.bar == 0
assert not hasattr(foo, "bar")
assert object.__getattribute__(foo, "bar") == betterproto.PLACEHOLDER
assert betterproto.which_one_of(foo, "group1")[0] == "baz"
foo.sub.val = 1
foo.sub = Sub(val=1)
assert betterproto.serialized_on_wire(foo.sub)
foo.abc = "test"
# Group 1 shouldn't be touched, group 2 should have reset
assert foo.sub.val == 0
assert betterproto.serialized_on_wire(foo.sub) is False
assert not hasattr(foo, "sub")
assert object.__getattribute__(foo, "sub") == betterproto.PLACEHOLDER
assert betterproto.which_one_of(foo, "group2")[0] == "abc"
# Zero value should always serialize for one-of
@@ -176,6 +180,16 @@ def test_oneof_support():
assert betterproto.which_one_of(foo2, "group2")[0] == ""
@pytest.mark.skipif(
sys.version_info < (3, 10),
reason="pattern matching is only supported in python3.10+",
)
def test_oneof_pattern_matching():
from .oneof_pattern_matching import test_oneof_pattern_matching
test_oneof_pattern_matching()
def test_json_casing():
@dataclass
class CasingTest(betterproto.Message):