Skip to content

Commit 7299cd4

Browse files
authored
Merge pull request openSUSE#1726 from dmach/improve-models
Improve models
2 parents 636c906 + a83c67b commit 7299cd4

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

osc/util/models.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ def get_origin(typ):
3939
else:
4040
from typing import get_origin
4141

42+
43+
# types.UnionType was added in Python 3.10
44+
if sys.version_info < (3, 10):
45+
class UnionType:
46+
pass
47+
else:
48+
from types import UnionType
49+
50+
4251
import urllib3.response
4352

4453
from . import xml
@@ -79,8 +88,9 @@ def __init__(self, field_name, *, fallback=NotSet):
7988
def __repr__(self):
8089
return f"FromParent(field_name={self.field_name})"
8190

82-
83-
class Field(property):
91+
# HACK: inheriting from Any fixes the following mypy error:
92+
# Incompatible types in assignment (expression has type "Field", variable has type "X | None") [assignment]
93+
class Field(property, *([Any] if typing.TYPE_CHECKING else [])):
8494
def __init__(
8595
self,
8696
default: Any = NotSet,
@@ -165,7 +175,7 @@ def inner_type(self):
165175
@property
166176
def is_optional(self):
167177
origin_type = get_origin(self.type) or self.type
168-
return origin_type == Union and len(self.type.__args__) == 2 and type(None) in self.type.__args__
178+
return origin_type in (Union, UnionType) and type(None) in self.type.__args__
169179

170180
@property
171181
def is_model(self):
@@ -193,7 +203,7 @@ def validate_type(self, value, expected_types=None):
193203
origin_type = get_origin(expected_type) or expected_type
194204

195205
# unwrap Union
196-
if origin_type == Union:
206+
if origin_type in (Union, UnionType):
197207
if value is None and type(None) in expected_type.__args__:
198208
valid_type = True
199209
continue
@@ -464,7 +474,7 @@ def has_changed(self):
464474

465475

466476
class XmlModel(BaseModel):
467-
XML_TAG = None
477+
XML_TAG: Optional[str] = None
468478

469479
_apiurl: Optional[str] = Field(
470480
exclude=True,

tests/test_models.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import unittest
23
from typing import Set
34

@@ -24,6 +25,16 @@ def test_bool(self):
2425

2526

2627
class Test(unittest.TestCase):
28+
@unittest.skipIf(sys.version_info[:2] < (3, 10), "added in python 3.10")
29+
def test_union_or(self):
30+
class TestModel(BaseModel):
31+
text: str | None = Field()
32+
33+
m = TestModel()
34+
self.assertEqual(m.dict(), {"text": None})
35+
36+
self.assertRaises(TypeError, setattr, m.text, 123)
37+
2738
def test_dict(self):
2839
class TestSubmodel(BaseModel):
2940
text: str = Field(default="default")

0 commit comments

Comments
 (0)