def reset_unsupported_types(cls, target: Class): for attr in target.attrs: if not cls.validate_default_value(attr, target.ns_map): attr.types.clear() attr.types.append(AttrType(qname=str(DataType.STRING), native=True)) attr.restrictions.format = None attr.types = collections.unique_sequence(attr.types, key="qname")
def process(self, target: Class): """ Process the given class attributes and their types. Ensure all types are unique. """ for attr in list(target.attrs): for attr_type in list(attr.types): self.process_type(target, attr, attr_type) attr.types = unique_sequence(attr.types, key="name")
def build_class_extensions(cls, obj: ElementBase, target: Class): """Build the item class extensions from the given ElementBase children.""" restrictions = obj.get_restrictions() extensions = [ cls.build_class_extension(target, base, restrictions) for base in obj.bases ] extensions.extend(cls.children_extensions(obj, target)) target.extensions = collections.unique_sequence(extensions)
def flatten(cls, target: Class, module: str) -> Iterator[Class]: target.module = module while target.inner: yield from cls.flatten(target.inner.pop(), module) for attr in target.attrs: attr.types = collections.unique_sequence(attr.types, key="qname") for tp in attr.types: tp.forward = False yield target
def flatten_attribute_types(self, target: Class, attr: Attr): """ Loop over the the given attribute types to flatten simple definitions. Notes: * xs:pattern is not yet supported reset all native types to xs:string. * skip over forward references aka inner classes """ for current_type in list(attr.types): if current_type.native: if attr.restrictions.pattern: self.reset_attribute_type(current_type) elif not current_type.forward_ref: self.flatten_attribute_type(target, attr, current_type) attr.types = unique_sequence(attr.types, key="name")
def build_class_attribute_types(cls, target: Class, obj: ElementBase) -> List[AttrType]: """Convert real type and anonymous inner types to an attribute type list.""" types = [cls.build_data_type(target, tp) for tp in obj.attr_types] module = target.module namespace = target.target_namespace for inner in cls.build_inner_classes(obj, module, namespace): target.inner.append(inner) types.append(AttrType(qname=inner.qname, forward=True)) if len(types) == 0: types.append(cls.build_data_type(target, name=obj.default_type)) return collections.unique_sequence(types)
def field_type(self, attr: Attr, parents: List[str]) -> str: """Generate type hints for the given attribute.""" type_names = unique_sequence( self.field_type_name(x, parents) for x in attr.types) result = ", ".join(type_names) if len(type_names) > 1: result = f"Union[{result}]" if attr.is_tokens: result = f"List[{result}]" if attr.is_list: result = f"List[{result}]" elif attr.is_dict: result = "Dict" elif attr.default is None and not attr.is_factory: result = f"Optional[{result}]" return result
def choice_type(self, choice: Attr, parents: List[str]) -> str: """ Generate type hints for the given choice. Choices support a subset of features from normal attributes. First of all we don't have a proper type hint but a type metadata key. That's why we always need to wrap as Type[xxx]. The second big difference is that our choice belongs to a compound field that might be a list, that's why list restriction is also ignored. """ type_names = unique_sequence( self.field_type_name(x, parents) for x in choice.types) result = ", ".join(type_names) if len(type_names) > 1: result = f"Union[{result}]" if choice.is_tokens: result = f"List[{result}]" return f"Type[{result}]"
def filter_types(cls, types: List[AttrType]) -> List[AttrType]: """ Remove duplicate and invalid types. Invalid: 1. xs:error 2. xs:anyType and xs:anySimpleType when there are other types present """ types = collections.unique_sequence(types, key="qname") types = collections.remove(types, lambda x: x.datatype == DataType.ERROR) if len(types) > 1: types = collections.remove( types, lambda x: x.datatype in (DataType.ANY_TYPE, DataType.ANY_SIMPLE_TYPE), ) if not types: types.append(AttrType(qname=str(DataType.STRING), native=True)) return types
def collapse_whitespace(string: str) -> str: """Remove excess whitespace and duplicate words.""" return " ".join( unique_sequence([part for part in string.split(" ") if part.strip()]))
def __post_init__(self): self.namespace = " ".join(unique_sequence(self.namespace.split()))
def collapse_whitespace(string: str) -> str: """Remove excess whitespace and duplicate words.""" return " ".join(unique_sequence(string.split()))