def test_next_node_when_given_qname_matches_dataclass_var( self, mock_ctx_fetch, mock_element_xsi_type): ele = Element("a") ctx = XmlContext() cfg = ParserConfig() var = XmlElement(name="a", qname=QName("a"), types=[Foo], dataclass=True) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, vars=[var], ) xsi_type = QName("foo") namespace = meta.qname.namespace mock_ctx_fetch.return_value = replace(meta) mock_element_xsi_type.return_value = xsi_type node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertIsInstance(actual, ElementNode) self.assertEqual(10, actual.position) self.assertIs(mock_ctx_fetch.return_value, actual.meta) mock_ctx_fetch.assert_called_once_with(var.clazz, namespace, xsi_type)
def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta: """Fetch from cache or build the metadata object for the given class and parent namespace.""" if clazz not in self.cache: # Ensure the given type is a dataclass. if not is_dataclass(clazz): raise XmlContextError(f"Object {clazz} is not a dataclass.") # Fetch the dataclass meta settings and make sure we don't inherit # the parent class meta. meta = getattr(clazz, "Meta", None) if meta and meta.__qualname__ != f"{clazz.__name__}.Meta": meta = None name = getattr(meta, "name", self.name_generator(clazz.__name__)) nillable = getattr(meta, "nillable", False) namespace = getattr(meta, "namespace", parent_ns) module = sys.modules[clazz.__module__] source_namespace = getattr(module, "__NAMESPACE__", None) self.cache[clazz] = XmlMeta( name=name, clazz=clazz, qname=QName(namespace, name), source_qname=QName(source_namespace, name), nillable=nillable, vars=list(self.get_type_hints(clazz, namespace)), ) return self.cache[clazz]
def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta: """ Fetch from cache or build the binding metadata for the given class and parent namespace. :param clazz: A dataclass type :param parent_ns: The inherited parent namespace """ if clazz not in self.cache: # Ensure the given type is a dataclass. if not is_dataclass(clazz): raise XmlContextError(f"Object {clazz} is not a dataclass.") # Fetch the dataclass meta settings and make sure we don't inherit # the parent class meta. meta = clazz.Meta if "Meta" in clazz.__dict__ else None name = getattr(meta, "name", None) or self.local_name( clazz.__name__) nillable = getattr(meta, "nillable", False) namespace = getattr(meta, "namespace", parent_ns) module = sys.modules[clazz.__module__] source_namespace = getattr(module, "__NAMESPACE__", None) self.cache[clazz] = XmlMeta( clazz=clazz, qname=build_qname(namespace, name), source_qname=build_qname(source_namespace, name), nillable=nillable, vars=list(self.get_type_hints(clazz, namespace)), ) return self.cache[clazz]
def test_fetch(self, mock_build, mock_find_subclass): meta = XmlMeta( clazz=ItemsType, qname="ItemsType", source_qname="ItemsType", nillable=False, ) mock_build.return_value = meta actual = self.ctx.fetch(ItemsType, "foo") self.assertEqual(meta, actual) self.assertEqual(0, mock_find_subclass.call_count) mock_build.assert_called_once_with(ItemsType, "foo")
def setUp(self) -> None: super().setUp() self.context = XmlContext() self.meta = XmlMeta(clazz=Foo, qname="foo", source_qname="foo", nillable=False) self.node = ElementNode( position=0, meta=self.meta, context=self.context, config=ParserConfig(), attrs={}, ns_map={}, )
def test_fetch_with_xsi_type_and_subclass_not_found( self, mock_build, mock_find_subclass): meta = XmlMeta( clazz=ItemsType, qname="ItemsType", source_qname="ItemsType", nillable=False, ) mock_build.return_value = meta mock_find_subclass.return_value = None actual = self.ctx.fetch(ItemsType, xsi_type="foo") self.assertEqual(meta, actual) mock_find_subclass.assert_called_once_with(ItemsType, "foo")
def test_next_node_when_config_fail_on_unknown_properties_is_false(self): ele = Element("nope") ctx = XmlContext() cfg = ParserConfig(fail_on_unknown_properties=False) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, ) node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertEqual(SkipNode(position=10), actual)
def test_fetch_with_xsi_type_and_subclass_found(self, mock_build, mock_find_subclass): meta = XmlMeta( clazz=ItemsType, qname="ItemsType", source_qname="ItemsType", nillable=False, ) xsi_meta = replace(meta, qname="XsiType") mock_build.side_effect = [meta, xsi_meta] mock_find_subclass.return_value = xsi_meta actual = self.ctx.fetch(ItemsType, xsi_type="foo") self.assertEqual(xsi_meta, actual) mock_find_subclass.assert_called_once_with(ItemsType, "foo")
def test_build_build_vars(self, mock_get_type_hints): var = XmlVar(element=True, name="foo", qname="{foo}bar", types=[int]) mock_get_type_hints.return_value = [var] result = self.ctx.build(ItemsType, None) expected = XmlMeta( clazz=ItemsType, qname="ItemsType", source_qname="ItemsType", nillable=False, vars=[var], ) self.assertEqual(expected, result) mock_get_type_hints.assert_called_once_with(ItemsType, None)
def test_next_node_when_given_qname_does_not_match_any_var(self): ele = Element("nope") ctx = XmlContext() cfg = ParserConfig() meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, ) node = ElementNode(position=0, meta=meta, config=cfg) with self.assertRaises(XmlContextError) as cm: node.next_node(ele, 10, ctx) self.assertEqual("foo does not support mixed content: nope", str(cm.exception))
def test_next_node_when_given_qname_matches_primitive_var(self): ele = Element("a") ctx = XmlContext() cfg = ParserConfig() var = XmlText(name="a", qname=QName("a"), types=[int], default=100) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, vars=[var], ) node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertIsInstance(actual, PrimitiveNode) self.assertEqual(10, actual.position) self.assertEqual(var, actual.var)
def test_next_node_when_given_qname_matches_any_element_var(self): ele = Element("a") ctx = XmlContext() cfg = ParserConfig() var = XmlWildcard(name="a", qname=QName("a"), types=[], dataclass=False) meta = XmlMeta( name="foo", clazz=None, qname=QName("foo"), source_qname=QName("foo"), nillable=False, vars=[var], ) node = ElementNode(position=0, meta=meta, config=cfg) actual = node.next_node(ele, 10, ctx) self.assertIsInstance(actual, WildcardNode) self.assertEqual(10, actual.position) self.assertEqual(var.qname, actual.qname)
def test_property_element_form(self): meta = XmlMeta( name="foo", clazz=BookForm, qname=QName("foo"), source_qname=QName("foo"), nillable=False, ) self.assertEqual(FormType.UNQUALIFIED, meta.element_form) meta = replace(meta, qname=QName("bar", "foo")) self.assertEqual(FormType.QUALIFIED, meta.element_form) meta.vars.append(XmlElement(name="a", qname=QName("a"))) self.assertEqual(FormType.UNQUALIFIED, meta.element_form) meta.vars.append( XmlElement(name="b", qname=QName("b"), namespaces=["bar"])) self.assertEqual(FormType.UNQUALIFIED, meta.element_form) meta.vars.pop(0) self.assertEqual(FormType.QUALIFIED, meta.element_form)
def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta: if clazz not in self.cache: if not is_dataclass(clazz): raise XmlContextError(f"Object {clazz} is not a dataclass.") meta = getattr(clazz, "Meta", None) if meta and meta.__qualname__ != f"{clazz.__name__}.Meta": meta = None name = getattr(meta, "name", self.name_generator(clazz.__name__)) nillable = getattr(meta, "nillable", False) namespace = getattr(meta, "namespace", parent_ns) module = sys.modules[clazz.__module__] source_namespace = getattr(module, "__NAMESPACE__", None) self.cache[clazz] = XmlMeta( name=name, clazz=clazz, qname=QName(namespace, name), source_qname=QName(source_namespace, name), nillable=nillable, vars=list(self.get_type_hints(clazz, namespace)), ) return self.cache[clazz]