def resolve_imports(self): """Walk the import qualified names, check for naming collisions and add the necessary code generator import instance.""" local_names = { split_qname(qname)[1] for qname in self.class_map.keys() } for qname in self.import_classes(): package = self.find_package(qname) local_name = split_qname(qname)[1] exists = local_name in local_names local_names.add(local_name) self.add_import(qname=qname, package=package, exists=exists)
def serialize(self, value: QName, ns_map: Optional[Dict] = None, **kwargs: Any) -> str: """ Convert a QName instance to string either with a namespace prefix if a prefix-URI namespaces mapping is provided or to a fully qualified name with the namespace. examples: - QName("http://www.w3.org/2001/XMLSchema", "int") & ns_map -> xs:int - QName("foo, "bar") -> {foo}bar """ if ns_map is None: return value.text namespace, tag = namespaces.split_qname(value.text) if not namespace: return tag prefix = namespaces.load_prefix(namespace, ns_map) return f"{prefix}:{tag}" if prefix else tag
def copy_inner_class(cls, source: Class, target: Class, attr: Attr, attr_type: AttrType): """ Check if the given attr type is a forward reference and copy its inner class from the source to the target class. Checks: 1. Update type if inner class in a circular reference 2. Copy inner class, rename it if source is a simple type. """ if not attr_type.forward: return # This will fail if no inner class is found, too strict??? inner = ClassUtils.find_inner(source, attr_type.qname) if inner is target: attr_type.circular = True else: clone = inner.clone() clone.package = target.package clone.module = target.module # Simple type, update the name if clone.name == "@value": namespace, _ = split_qname(clone.qname) clone.qname = attr_type.qname = build_qname( namespace, attr.name) target.inner.append(clone)
def field_default_enum(self, attr: Attr) -> str: assert attr.default is not None qname, enumeration = attr.default[6:].split("::", 1) qname = next(x.alias or qname for x in attr.types if x.qname == qname) _, name = split_qname(qname) return f"{self.class_name(name)}.{self.constant_name(enumeration, name)}"
def write_dataclass( self, obj: Any, namespace: NoneStr = None, qname: NoneStr = None, nillable: bool = False, xsi_type: Optional[QName] = None, ) -> Generator: """ Produce an events stream from a dataclass. Optionally override the qualified name and the xsi properties type and nil. """ meta = self.context.build(obj.__class__, namespace) qname = qname or meta.qname nillable = nillable or meta.nillable namespace, tag = split_qname(qname) yield XmlWriterEvent.START, qname for key, value in self.next_attribute(obj, meta, nillable, xsi_type): yield XmlWriterEvent.ATTR, key, value for var, value in self.next_value(obj, meta): if value is not None or var.nillable: yield from self.write_value(value, var, namespace) yield XmlWriterEvent.END, qname
def attributes(cls, elements: Iterator[AnyElement]) -> Dict: """Return all attributes from all extended elements as a dictionary.""" return { split_qname(qname)[1]: value for element in elements if isinstance(element, AnyElement) for qname, value in element.attributes.items() }
def map(cls, element: AnyElement) -> List[Class]: """Map schema children elements to classes.""" assert element.qname is not None target_namespace, module = split_qname(element.qname) target = cls.build_class(element, target_namespace) return list(ClassUtils.flatten(target, module))
def read_root_name(path: Path) -> str: try: recovering_parser = etree.XMLParser( recover=True, resolve_entities=False, no_network=True ) tree = etree.parse(str(path), parser=recovering_parser) # nosec _, local_name = split_qname(tree.getroot().tag) return text.pascal_case(utils.safe_snake(local_name, "Type")) except Exception: return ""
def matches_wildcard(self, qname: str) -> bool: """Match the given qname to the wildcard allowed namespaces.""" if qname == "*": return True namespace, tag = split_qname(qname) if not self.namespaces and namespace is None: return True return any( self.match_namespace(ns, namespace) for ns in self.namespaces)
def add_import(self, qname: str, package: str, exists: bool = False): """Append an import package to the list of imports with any if necessary aliases if the import name exists in the local module.""" alias = None local_name = split_qname(qname)[1] if exists: module = package.split(".")[-1] alias = f"{module}:{local_name}" self.aliases[qname] = alias self.imports.append( Import(name=local_name, source=package, alias=alias))
def rename_class(self, target: Class): """Find the next available class identifier, save the original name in the class metadata and update the class qualified name and all classes that depend on the target class.""" qname = target.qname namespace, name = split_qname(target.qname) target.qname = self.next_qname(namespace, name) target.meta_name = name self.container.reset(target, qname) for item in self.container.iterate(): self.rename_class_dependencies(item, qname, target.qname)
def test_split_qname(self): self.assertEqual(("a", "b"), split_qname("{a}b")) self.assertEqual((None, "b"), split_qname("b")) self.assertEqual((None, "{"), split_qname("{")) self.assertEqual((None, "{foobar"), split_qname("{foobar")) with self.assertRaises(IndexError): split_qname("")
def build_class(cls, element: AnyElement, target_namespace: Optional[str]) -> Class: assert element.qname is not None namespace, name = split_qname(element.qname) target = Class( qname=build_qname(target_namespace, name), namespace=cls.select_namespace(namespace, target_namespace), tag=Tag.ELEMENT, module="", ) children = [c for c in element.children if isinstance(c, AnyElement)] sequential_set = cls.sequential_names(children) for key, value in element.attributes.items(): attr_type = cls.build_attribute_type(key, value) cls.build_attribute(target, key, attr_type, target_namespace, Tag.ATTRIBUTE) for child in children: assert child.qname is not None if child.tail: target.mixed = True if child.attributes or child.children: inner = cls.build_class(child, target_namespace) attr_type = AttrType(qname=inner.qname, forward=True) target.inner.append(inner) else: attr_type = cls.build_attribute_type(child.qname, child.text) cls.build_attribute( target, child.qname, attr_type, target_namespace, Tag.ELEMENT, child.qname in sequential_set, ) if element.text: attr_type = cls.build_attribute_type("value", element.text) cls.build_attribute(target, "value", attr_type, None, Tag.SIMPLE_TYPE) return target
def start_tag(self, qname: str): """ Start tag notification receiver. The receiver will flush the start of any pending element, create new namespaces context and queue the current tag for generation. :param qname: Tag qualified name """ self.flush_start(False) self.ns_context.append(self.ns_map.copy()) self.ns_map = self.ns_context[-1] self.pending_tag = split_qname(qname) self.add_namespace(self.pending_tag[0])
def build_attribute( cls, target: Class, qname: str, attr_type: AttrType, target_namespace: Optional[str] = None, tag: str = Tag.ELEMENT, sequential: bool = False, ): namespace, name = split_qname(qname) namespace = cls.select_namespace(namespace, target_namespace, tag) index = len(target.attrs) attr = Attr(index=index, name=name, tag=tag, namespace=namespace) attr.types.append(attr_type) attr.restrictions.sequential = sequential or None cls.add_attribute(target, attr)
def __deserialize_complementos(obj: object) -> list: complementos = [] if not obj or not hasattr(obj, 'complemento'): return complementos obj.complemento = obj.complemento[0] if isinstance( obj.complemento, list) else obj.complemento for complemento in getattr(obj.complemento, 'any_element', []): ns, tag = namespaces.split_qname(getattr(complemento, 'qname', '')) complemento_type = COMPLEMENTO_TYPES_MAP.get(ns) if not complemento_type: continue delattr(complemento, 'qname') complemento = deserialize(serialize(complemento), complemento_type) complementos.append(complemento) return complementos
def write_wildcard(self, value: AnyElement, var: XmlVar, namespace: NoneStr) -> Generator: """Produce an element events stream for the given generic object.""" if value.qname: namespace, tag = split_qname(value.qname) yield XmlWriterEvent.START, value.qname for key, val in value.attributes.items(): yield XmlWriterEvent.ATTR, key, val yield XmlWriterEvent.DATA, value.text for child in value.children: yield from self.write_any_type(child, var, namespace) if value.qname: yield XmlWriterEvent.END, value.qname if value.tail: yield XmlWriterEvent.DATA, value.tail
def add_attribute(self, key: str, value: Any, check_pending: bool = True): """ Add attribute notification receiver. The receiver will convert the key to a namespace, name tuple and convert the value to string. Internally the converter will also generate any missing namespace prefixes. :param key: Attribute name :param value: Attribute value :param check_pending: Raise exception if not no element is pending start """ if not self.pending_tag and check_pending: raise XmlWriterError("Empty pending tag.") if self.is_xsi_type(key, value): value = QName(value) name = split_qname(key) self.attrs[name] = self.encode_data(value)
def build_envelope_class( cls, definitions: Definitions, binding_message: BindingMessage, port_type_message: PortTypeMessage, name: str, style: str, namespace: Optional[str], ) -> Class: """Step 6.1: Build Envelope class for the given binding message with attributes from the port type message.""" target = Class( qname=build_qname(definitions.target_namespace, name), meta_name="Envelope", type=type(binding_message), module=definitions.module, ns_map=binding_message.ns_map, namespace=namespace, ) message = port_type_message.message for ext in binding_message.extended_elements: assert ext.qname is not None local_name = split_qname(ext.qname)[1].title() inner = cls.build_inner_class(target, local_name) if style == "rpc" and local_name == "Body": namespace = ext.attributes.get("namespace") attrs = cls.map_port_type_message(port_type_message, namespace) else: attrs = cls.map_binding_message_parts( definitions, message, ext, inner.ns_map ) inner.attrs.extend(attrs) return target
def map_binding_message_parts( cls, definitions: Definitions, message: str, extended: AnyElement, ns_map: Dict ) -> Iterator[Attr]: """Find a Message instance and map its parts to attributes according to the the extensible element..""" parts = [] if "part" in extended.attributes: parts.append(extended.attributes["part"]) elif "parts" in extended.attributes: parts.extend(extended.attributes["parts"].split()) if "message" in extended.attributes: message_name = split_qname(extended.attributes["message"])[1] else: message_name = text.suffix(message) definition_message = definitions.find_message(message_name) message_parts = definition_message.parts if parts: message_parts = [part for part in message_parts if part.name in parts] yield from cls.build_parts_attributes(message_parts, ns_map)
def end_tag(self, qname: str): """ End tag notification receiver. The receiver will flush if pending the start of the element, end the element, its tail content and its namespaces prefix mapping and current context. :param qname: Tag qualified name """ self.flush_start(True) self.handler.endElementNS(split_qname(qname), None) if self.tail: self.handler.characters(self.tail) self.tail = None self.in_tail = False self.ns_context.pop() if self.ns_context: self.ns_map = self.ns_context[-1] for prefix in self.pending_prefixes.pop(): self.handler.endPrefixMapping(prefix)
def lname(self) -> str: """Local name.""" _, name = split_qname(self.qname) return name
def name(self) -> str: """Shortcut for qname local name.""" return split_qname(self.qname)[1]
def target_namespace(self) -> Optional[str]: return split_qname(self.qname)[0]