def bind_element_children( cls, params: Dict, meta: XmlMeta, position: int, objects: List, ): """Return a dictionary of qualified object names and their values for the given queue item.""" while len(objects) > position: qname, value = objects.pop(position) arg = meta.find_var(qname, FindMode.NOT_WILDCARD) or meta.find_var( qname, FindMode.WILDCARD ) if not arg: raise ParserError("Impossible exception!") if not arg.init: continue if value is None: value = "" if not cls.bind_element_param(params, arg, value): lookup = QName(value.qname) if isinstance(value, AnyElement) else qname wild = cls.find_eligible_wildcard(meta, lookup, params) if not wild: logger.warning("Unassigned parsed object %s", qname) else: cls.bind_element_wildcard_param(params, wild, qname, value)
def process(self, uris: List[str], package: str): """ Run main processes. :param uris: list of uris to process :param package: package name eg foo.bar.xxx """ collections.apply(uris, self.process_schema) classes = [ cls for classes in self.class_map.values() for cls in classes ] class_num, inner_num = self.count_classes(classes) if class_num: logger.info("Analyzer input: %d main and %d inner classes", class_num, inner_num) self.assign_packages(package) classes = self.analyze_classes(classes) class_num, inner_num = self.count_classes(classes) logger.info("Analyzer output: %d main and %d inner classes", class_num, inner_num) writer.designate(classes, self.output) if self.print: writer.print(classes, self.output) else: writer.write(classes, self.output) else: logger.warning("Analyzer returned zero classes!")
def bind_element_children(self, params: Dict, item: ClassQueueItem, element: Element): """Return a dictionary of qualified object names and their values for the given queue item.""" while len(self.objects) > item.position: qname, value = self.objects.pop(item.position) arg = item.meta.vars[qname] if not arg.init: continue if value is None: value = "" if not self.bind_element_param(params, arg, value): lookup = QName(value.qname) if isinstance( value, AnyElement) else qname wild = self.find_eligible_wildcard(item.meta, lookup, params) if not wild: logger.warning("Unassigned parsed object %s", qname) else: self.bind_element_wildcard_param(params, wild, qname, value)
def flatten_extension(self, target: Class, extension: Extension): """ Flatten target class extension based on the extension type. Types: 1. Native primitive type (int, str, float, etc) 2. Simple source type (simpleType, Extension) 3. Complex source type (ComplexType, Element) 4. Unknown type """ if extension.type.native: self.flatten_extension_native(target, extension) else: qname = target.source_qname(extension.type.name) simple_source = self.find_simple_class(qname) complex_source = None if simple_source else self.find_class(qname) if simple_source: self.flatten_extension_simple(simple_source, target, extension) elif complex_source: self.flatten_extension_complex(complex_source, target, extension) else: logger.warning("Missing extension type: %s", extension.type.name) target.extensions.remove(extension)
def process_attribute_default_enum(self, target: Class, attr: Attr): """ Convert string literal default value for enum fields. Loop through all attributes types and search for enum sources. If an enum source exist map the default string literal value to a qualified name. If the source class in inner promote it to root classes. """ source_found = False for attr_type in attr.types: source = self.find_enum(target, attr_type) if not source: continue source_found = True source_attr = next( (x for x in source.attrs if x.default == attr.default), None) if source_attr: if attr_type.forward: self.promote_inner_class(target, source) attr.default = f"@enum@{source.name}::{source_attr.name}" return if source_found: logger.warning( "No enumeration member matched %s.%s default value `%s`", target.name, attr.local_name, attr.default, ) attr.default = None
def build_parts_attributes(cls, parts: List[Part], ns_map: Dict) -> Iterator[Attr]: """ Build attributes for the given list of parts. :param parts: List of parts :param ns_map: Namespace prefix-URI map """ for part in parts: if part.element: prefix, type_name = text.split(part.element) name = type_name elif part.type: prefix, type_name = text.split(part.type) name = part.name else: logger.warning("Skip untyped message part %s", part.name) continue ns_map.update(part.ns_map) namespace = part.ns_map.get(prefix) type_qname = build_qname(namespace, type_name) native = namespace == Namespace.XS.uri namespace = "" if part.type else namespace yield cls.build_attr(name, type_qname, namespace=namespace, native=native)
def process_dependency_type(self, target: Class, attr: Attr, attr_type: AttrType): """ Process an attributes type that depends on any global type. Strategies: 1. Reset absent or dummy attribute types with a warning 2. Copy attribute properties from a simple type 3. Copy format restriction from an enumeration 4. Set circular flag for the rest """ source = self.find_dependency(attr_type, attr.tag) if not source or (not source.attrs and not source.extensions): logger.warning("Reset dummy type: %s", attr_type.name) use_str = not source or not source.is_complex self.reset_attribute_type(attr_type, use_str) elif source.is_simple_type: self.copy_attribute_properties(source, target, attr, attr_type) elif source.is_enumeration: attr.restrictions.format = collections.first( x.restrictions.format for x in source.attrs if x.restrictions.format) else: self.set_circular_flag(source, target, attr_type)
def process_included( self, included: Union[Import, Include, Redefine, Override], package: str, target_namespace: Optional[str], ) -> List[Class]: """Prepare the given included schema location and send it for processing.""" classes = [] if not included.location: logger.warning( "%s: %s unresolved schema location..", included.class_name, included.schema_location, ) elif included.location in self.processed: logger.debug( "%s: %s already included skipping..", included.class_name, included.schema_location, ) else: package = self.adjust_package(package, included.schema_location) classes = self.process_schema(included.location, package, target_namespace) return classes
def process_dependency_extension(self, target: Class, extension: Extension): """User defined type flatten handler.""" source = self.find_dependency(extension.type) if not source: logger.warning("Missing extension type: %s", extension.type.name) target.extensions.remove(extension) elif not source.is_complex or source.is_enumeration: self.process_simple_extension(source, target, extension) else: self.process_complex_extension(source, target, extension)
def load_resource(self, uri: str) -> Optional[bytes]: """Read and return the contents of the given uri.""" if uri not in self.processed: try: self.processed.append(uri) return self.preloaded.pop(uri, None) or urlopen(uri).read() # nosec except OSError: logger.warning("Resource not found %s", uri) else: logger.debug("Skipping already processed: %s", os.path.basename(uri)) return None
def find_class(self, qname: QName, condition: Optional[Callable] = None) -> Optional[Class]: candidates = list(filter(condition, self.class_index.get(qname, []))) if candidates: candidate = candidates.pop(0) if candidates: logger.warning("More than one candidate found for %s", qname) if id(candidate) not in self.processed: self.flatten_class(candidate) return candidate return None
def flatten_attribute_type(self, target: Class, attr: Attr, attr_type: AttrType): """Flatten attribute type if it's a simple type otherwise check for circular reference or missing type.""" simple_source = self.find_attr_simple_type(target, attr_type) if simple_source: self.merge_attribute_type(simple_source, target, attr, attr_type) else: complex_source = self.find_attr_type(target, attr_type) if complex_source: attr_type.self_ref = self.class_depends_on( complex_source, target) else: logger.warning("Missing type: %s", attr_type.name) self.reset_attribute_type(attr_type)
def process(self, schemas: List[Path], package: str): classes = self.process_schemas(schemas, package) classes = self.analyze_classes(classes) class_num, inner_num = self.count_classes(classes) if class_num: logger.info("Analyzer: %d main and %d inner classes", class_num, inner_num) writer.designate(classes, self.output) if self.print: writer.print(classes, self.output) else: writer.write(classes, self.output) else: logger.warning("Analyzer returned zero classes!")
def bind_objects(cls, params: Dict, meta: XmlMeta, position: int, objects: List): """Return a dictionary of qualified object names and their values for the given queue item.""" while len(objects) > position: qname, value = objects.pop(position) arg = meta.find_var(qname, FindMode.ELEMENT) if arg and cls.bind_var(params, arg, value): continue arg = meta.find_var(qname, FindMode.WILDCARD) if arg and cls.bind_wild_var(params, arg, qname, value): continue logger.warning("Unassigned parsed object %s", qname)
def parse_schema(uri: str, namespace: Optional[str] = None) -> Optional[Schema]: """ Parse the given schema uri and return the schema tree object. Optionally add the target namespace if the schema is included and is missing a target namespace. """ try: schema = urlopen(uri).read() except OSError: logger.warning("Schema not found %s", uri) else: parser = SchemaParser(target_namespace=namespace, location=uri) return parser.from_bytes(schema, Schema) return None
def parse_schema(cls, uri: str, namespace: Optional[str]) -> Optional[Schema]: """ Parse the given schema uri and return the schema tree object. Optionally add the target namespace if the schema is included and is missing a target namespace. """ try: input_stream = cls.load_resource(uri) except OSError: logger.warning("Schema not found %s", uri) else: parser = SchemaParser(target_namespace=namespace, location=uri) return parser.from_bytes(input_stream, Schema) return None
def merge_attribute_type(cls, source: Class, target: Class, attr: Attr, attr_type: AttrType): if len(source.attrs) != 1: logger.warning("Missing implementation: %s", source.type.__name__) cls.reset_attribute_type(attr_type) else: source_attr = source.attrs[0] index = attr.types.index(attr_type) attr.types.pop(index) for source_attr_type in source_attr.types: clone_type = source_attr_type.clone() attr.types.insert(index, clone_type) index += 1 restrictions = source_attr.restrictions.clone() restrictions.merge(attr.restrictions) attr.restrictions = restrictions cls.copy_inner_classes(source, target)
def process_dependency_type(self, target: Class, attr: Attr, attr_type: AttrType): """ Process user defined attribute types, split process between complex and simple types. Reset absent attribute types with a warning. Complex Type: xs:Element and xs:ComplexType Simple stype: the rest """ source = self.find_dependency(attr_type) if not source: logger.warning("Missing type: %s", attr_type.name) self.reset_attribute_type(attr_type) elif source.is_complex and not source.is_enumeration: self.process_complex_dependency(source, target, attr_type) else: self.process_simple_dependency(source, target, attr, attr_type)
def flatten_attribute_types(self, target: Class, attr: Attr): """ Flatten attribute types by using the source attribute type. Steps: * Skip xsd native types * Detect circular references if no source is found * Skip enumeration types * Overwrite attribute type from source """ types = [] for attr_type in attr.types: source = None if not attr_type.native: type_qname = target.source_qname(attr_type.name) source = self.find_class(type_qname) if source is None: attr_type.self_ref = self.attr_depends_on(attr_type, target) types.append(attr_type) elif self.is_qname(source): types.append(source.extensions[0].type.clone()) elif source.is_enumeration: types.append(attr_type) elif len(source.attrs) == 1: source_attr = source.attrs[0] types.extend(source_attr.types) restrictions = source_attr.restrictions.clone() restrictions.merge(attr.restrictions) attr.restrictions = restrictions self.copy_inner_classes(source, target) else: types.append(AttrType(name=DataType.STRING.code, native=True)) logger.warning("Missing type implementation: %s", source.type.__name__) attr.types = types