import unittest from typing import Set from osc.util.models import * from osc.util.models import get_origin class TestTyping(unittest.TestCase): def test_get_origin_list(self): typ = get_origin(list) self.assertEqual(typ, None) def test_get_origin_list_str(self): typ = get_origin(List[str]) self.assertEqual(typ, list) class TestNotSet(unittest.TestCase): def test_repr(self): self.assertEqual(repr(NotSet), "NotSet") def test_bool(self): self.assertEqual(bool(NotSet), False) class Test(unittest.TestCase): def test_dict(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): a: str = Field(default="default") b: Optional[str] = Field(default=None) sub: Optional[List[TestSubmodel]] = Field(default=None) m = TestModel() self.assertEqual(m.dict(), {"a": "default", "b": None, "sub": None}) m.b = "B" m.sub = [{"text": "one"}, {"text": "two"}] self.assertEqual(m.dict(), {"a": "default", "b": "B", "sub": [{"text": "one"}, {"text": "two"}]}) def test_unknown_fields(self): class TestModel(BaseModel): pass self.assertRaises(TypeError, TestModel, does_not_exist=None) def test_uninitialized(self): class TestModel(BaseModel): field: str = Field() self.assertRaises(TypeError, TestModel) def test_invalid_type(self): class TestModel(BaseModel): field: Optional[str] = Field() m = TestModel() self.assertRaises(TypeError, setattr, m.field, []) def test_unsupported_type(self): class TestModel(BaseModel): field: Set[str] = Field(default=None) self.assertRaises(TypeError, TestModel) def test_lazy_default(self): class TestModel(BaseModel): field: List[str] = Field(default=lambda: ["string"]) m = TestModel() self.assertEqual(m.field, ["string"]) def test_lazy_default_invalid_type(self): class TestModel(BaseModel): field: List[str] = Field(default=lambda: None) self.assertRaises(TypeError, TestModel) def test_is_set(self): class TestModel(BaseModel): field: Optional[str] = Field() m = TestModel() self.assertNotIn("field", m._values) self.assertEqual(m.field, None) m.field = "text" self.assertIn("field", m._values) self.assertEqual(m.field, "text") def test_str(self): class TestModel(BaseModel): field: str = Field(default="default") m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_optional, False) self.assertEqual(field.origin_type, str) self.assertEqual(m.field, "default") m.field = "text" self.assertEqual(m.field, "text") def test_optional_str(self): class TestModel(BaseModel): field: Optional[str] = Field() m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_optional, True) self.assertEqual(field.origin_type, str) self.assertEqual(m.field, None) m.field = "text" self.assertEqual(m.field, "text") def test_int(self): class TestModel(BaseModel): field: int = Field(default=0) m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_optional, False) self.assertEqual(field.origin_type, int) self.assertEqual(m.field, 0) m.field = 1 self.assertEqual(m.field, 1) def test_optional_int(self): class TestModel(BaseModel): field: Optional[int] = Field() m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_optional, True) self.assertEqual(field.origin_type, int) self.assertEqual(m.field, None) m.field = 1 self.assertEqual(m.field, 1) def test_submodel(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): field: TestSubmodel = Field(default={}) m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, True) self.assertEqual(field.is_optional, False) self.assertEqual(field.origin_type, TestSubmodel) m = TestModel(field=TestSubmodel()) self.assertEqual(m.field.text, "default") m = TestModel(field={"text": "text"}) self.assertEqual(m.field.text, "text") def test_optional_submodel(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): field: Optional[TestSubmodel] = Field(default=None) m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, True) self.assertEqual(field.is_optional, True) self.assertEqual(field.origin_type, TestSubmodel) self.assertEqual(m.field, None) m.dict() m = TestModel(field=TestSubmodel()) self.assertIsInstance(m.field, TestSubmodel) self.assertEqual(m.field.text, "default") m.dict() m = TestModel(field={"text": "text"}) self.assertNotEqual(m.field, None) self.assertEqual(m.field.text, "text") m.dict() def test_list_submodels(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): field: List[TestSubmodel] = Field(default=[]) m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_model_list, True) self.assertEqual(field.is_optional, False) self.assertEqual(field.origin_type, list) m.dict() m = TestModel(field=[TestSubmodel()]) self.assertEqual(m.field[0].text, "default") m.dict() m = TestModel(field=[{"text": "text"}]) self.assertEqual(m.field[0].text, "text") m.dict() self.assertRaises(TypeError, getattr(m, "field")) def test_optional_list_submodels(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): field: Optional[List[TestSubmodel]] = Field(default=[]) m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_model_list, True) self.assertEqual(field.is_optional, True) self.assertEqual(field.origin_type, list) m.dict() m = TestModel(field=[TestSubmodel()]) self.assertEqual(m.field[0].text, "default") m.dict() m = TestModel(field=[{"text": "text"}]) self.assertEqual(m.field[0].text, "text") m.dict() m.field = None self.assertEqual(m.field, None) m.dict() def test_enum(self): class Numbers(Enum): one = "one" two = "two" class TestModel(BaseModel): field: Optional[Numbers] = Field(default=None) m = TestModel() field = m.__fields__["field"] self.assertEqual(field.is_model, False) self.assertEqual(field.is_optional, True) self.assertEqual(field.origin_type, Numbers) self.assertEqual(m.field, None) m.field = "one" self.assertEqual(m.field, "one") self.assertRaises(ValueError, setattr, m, "field", "does-not-exist") def test_parent(self): class ParentModel(BaseModel): field: str = Field(default="text") class ChildModel(BaseModel): field: str = Field(default=FromParent("field")) field2: str = Field(default=FromParent("field")) p = ParentModel() c = ChildModel(_parent=p) self.assertEqual(p.field, "text") self.assertEqual(c.field, "text") self.assertEqual(c.field2, "text") c.field = "new-text" self.assertEqual(p.field, "text") self.assertEqual(c.field, "new-text") self.assertEqual(c.field2, "text") def test_parent_fallback(self): class SubModel(BaseModel): field: str = Field(default=FromParent("field", fallback="submodel-fallback")) class Model(BaseModel): field: str = Field(default=FromParent("field", fallback="model-fallback")) sub: Optional[SubModel] = Field() sub_list: Optional[List[SubModel]] = Field() m = Model() s = SubModel(_parent=m) m.sub = s self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub.field, "model-fallback") m = Model(sub={}) self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub.field, "model-fallback") m = Model(sub=SubModel()) self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub.field, "model-fallback") m = Model() s = SubModel(_parent=m) m.sub_list = [s] self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub_list[0].field, "model-fallback") m = Model(sub_list=[{}]) self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub_list[0].field, "model-fallback") m = Model(sub_list=[SubModel()]) self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub_list[0].field, "model-fallback") m = Model() m.sub_list = [] m.sub_list.append({}) self.assertEqual(m.field, "model-fallback") self.assertEqual(m.sub_list[0].field, "model-fallback") def test_get_callback(self): class Model(BaseModel): quiet: bool = Field( default=False, ) verbose: bool = Field( default=False, # return False if ``quiet`` is True; return the actual value otherwise get_callback=lambda obj, value: False if obj.quiet else value, ) m = Model() self.assertEqual(m.quiet, False) self.assertEqual(m.verbose, False) m.quiet = True m.verbose = True self.assertEqual(m.quiet, True) self.assertEqual(m.verbose, False) m.quiet = False m.verbose = True self.assertEqual(m.quiet, False) self.assertEqual(m.verbose, True) def test_has_changed(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): field: Optional[List[TestSubmodel]] = Field(default=[]) m = TestModel() self.assertFalse(m.has_changed()) # a new instance of empty list m.field = [] self.assertFalse(m.has_changed()) m.field = [{"text": "one"}, {"text": "two"}] self.assertTrue(m.has_changed()) m.do_snapshot() # a new instance of list with new instances of objects with the same data m.field = [{"text": "one"}, {"text": "two"}] self.assertFalse(m.has_changed()) def test_append_dict(self): class TestSubmodel(BaseModel): text: str = Field(default="default") class TestModel(BaseModel): field: Optional[List[TestSubmodel]] = Field(default=[]) m = TestModel() m.field.append({"text": "value"}) # dict is converted to object next time the field is retrieved self.assertIsInstance(m.field[0], BaseModel) self.assertEqual(m.field[0].text, "value") def test_ordering(self): class TestSubmodel(BaseModel): txt: Optional[str] = Field() class TestModel(BaseModel): num: Optional[int] = Field() txt: Optional[str] = Field() sub: Optional[TestSubmodel] = Field() dct: Optional[Dict[str, TestSubmodel]] = Field() m1 = TestModel() m2 = TestModel() self.assertEqual(m1, m2) m1 = TestModel(num=1) m2 = TestModel(num=2) self.assertNotEqual(m1, m2) self.assertLess(m1, m2) self.assertGreater(m2, m1) m1 = TestModel(txt="a") m2 = TestModel(txt="b") self.assertNotEqual(m1, m2) self.assertLess(m1, m2) self.assertGreater(m2, m1) m1 = TestModel(sub={}) m2 = TestModel(sub={}) self.assertEqual(m1, m2) m1 = TestModel(sub={"txt": "a"}) m2 = TestModel(sub={"txt": "b"}) self.assertNotEqual(m1, m2) self.assertLess(m1, m2) self.assertGreater(m2, m1) m1 = TestModel(dct={}) m2 = TestModel(dct={}) self.assertEqual(m1, m2) m1 = TestModel(dct={"a": TestSubmodel()}) m2 = TestModel(dct={"b": TestSubmodel()}) self.assertNotEqual(m1, m2) self.assertLess(m1, m2) self.assertGreater(m2, m1) # dict ordering doesn't matter m1 = TestModel(dct={"a": TestSubmodel(), "b": TestSubmodel()}) m2 = TestModel(dct={"b": TestSubmodel(), "a": TestSubmodel()}) self.assertEqual(m1, m2) if __name__ == "__main__": unittest.main()