def unique_operations(self) -> Iterator[BindingOperation]: name_attr = operator.attrgetter("name") grouped_operations = collections.group_by(self.operations, key=name_attr) for operations in grouped_operations.values(): yield operations[-1]
def group_compound_fields(self, target: Class): """Group and process target attributes by the choice group.""" groups = group_by(target.attrs, lambda x: x.restrictions.choice) for choice, attrs in groups.items(): if choice and len(attrs) > 1 and any(attr.is_list for attr in attrs): self.group_fields(target, attrs)
def resolve_conflicts(self): """Find classes with the same case insensitive qualified name and rename them.""" groups = group_by(self.container.iterate(), lambda x: alnum(x.qname)) for classes in groups.values(): if len(classes) > 1: self.rename_classes(classes)
def filter_classes(self): """If there is any class derived from complexType or element then filter classes that should be generated, otherwise leave the container as it is.""" candidates = list(filter(lambda x: x.should_generate, self.iterate())) if candidates: self.data = group_by(candidates, attrgetter("qname"))
def process_duplicate_attribute_names(cls, attrs: List[Attr]) -> None: """Sanitize duplicate attribute names that might exist by applying rename strategies.""" grouped = group_by(attrs, lambda attr: alnum(attr.name)) for items in grouped.values(): total = len(items) if total == 2 and not items[0].is_enumeration: cls.rename_attribute_by_preference(*items) elif total > 1: cls.rename_attributes_with_index(attrs, items)
def reduce(cls, classes: List[Class]) -> List[Class]: result = [] indexed = collections.group_by(classes, key=lambda x: x.qname) for group in indexed.values(): group.sort(key=lambda x: len(x.attrs)) target = group.pop() for source in group: target.mixed = target.mixed or source.mixed cls.merge_attributes(target, source) result.append(target) return result
def handle_duplicate_types(cls, classes: List[Class]): """Handle classes with same namespace, name that are derived from the same xs type.""" grouped = group_by(classes, lambda x: f"{x.type.__name__}{x.qname}") for items in grouped.values(): if len(items) == 1: continue index = cls.select_winner(list(items)) winner = items.pop(index) for item in items: classes.remove(item) if winner.container == Tag.REDEFINE: cls.merge_redefined_type(item, winner)
def render_package(self, classes: List[Class]) -> str: """Render the source code for the __init__.py with all the imports of the generated class names.""" imports = [ Import(name=obj.name, source=obj.target_module) for obj in sorted(classes, key=lambda x: x.name) ] for group in group_by(imports, key=lambda x: x.name).values(): if len(group) == 1: continue for index, cur in enumerate(group): cmp = group[index + 1] if index == 0 else group[index - 1] parts = re.split("[_.]", cur.source) diff = set(parts) - set(re.split("[_.]", cmp.source)) add = "_".join(part for part in parts if part in diff) cur.alias = f"{add}:{cur.name}" return self.template("imports").render(imports=imports)
def process_duplicate_attribute_names(cls, attrs: List[Attr]) -> None: """ Sanitize duplicate attribute names that might exist by applying rename strategies. Steps: 1. If more than two attributes share the same name or if they are enumerations append a numerical index to the attribute names. 2. If one of the two fields has a specific namespace prepend it to the name. If possible rename the second field. 3. Append the xml type to the name of one of the two attributes. if possible rename the second field or the field with xml type `attribute`. """ grouped = group_by(attrs, lambda attr: attr.name.lower()) for items in grouped.values(): if len(items) == 1: continue if len(items) > 2 or items[0].is_enumeration: for index in range(1, len(items)): num = 1 name = items[index].name.lower() while any(attr for attr in attrs if attr.name.lower() == f"{name}_{num}"): num += 1 items[index].name = f"{name}_{num}" else: first, second = items if first.tag == second.tag and any( (first.namespace, second.namespace)): change = second if second.namespace else first change.name = f"{text.clean_uri(change.namespace)}_{change.name}" else: change = second if second.is_attribute else first change.name = f"{change.name}_{change.tag}"
def group_imports(cls, imports: List[Package]) -> Dict[str, List[Package]]: """Group the given list of packages by the source path.""" return group_by(imports, lambda x: x.source)
def from_list(cls, items: List[Class]) -> "ClassContainer": """Static constructor from a list of classes.""" return cls(group_by(items, attrgetter("qname")))
def from_list(cls, items: List[Class]) -> "ClassContainer": """Static constructor from a list of classes.""" return cls(group_by(items, methodcaller("source_qname")))
output = [ ".. list-table::", " :widths: auto", " :header-rows: 1", " :align: left", "", " * - Python", " - XML Type", " -", " -", " -", " -", ] groups = group_by(list(DataType), key=lambda x: x.type.__name__.lower()) for key in sorted(groups): tp = groups[key][0].type if tp.__module__ != "builtins": output.append(f" * - :class:`~{tp.__module__}.{tp.__name__}`") else: output.append(f" * - :class:`{tp.__name__}`") count = 0 for dt in groups[key]: output.append(f" - {dt.code}") count += 1 if count == 5: output.append(" * -") count = 0
def group_by_module(cls, classes: List[Class]) -> Dict[Path, List[Class]]: """Group the given list of classes by the target module directory.""" return group_by(classes, lambda x: module_path(x.target_module))
" :align: left", "", " * - Python", " - XML Type", " -", " -", " -", " -", ] custom_classes = { "Decimal": "decimal.Decimal", "QName": "xml.etree.ElementTree.QName", } for tp, data_types in group_by(list(DataType), key=lambda x: x.type.__name__).items(): output.append(f" * - :class:`{custom_classes.get(tp, tp)}`") count = 0 for dt in data_types: output.append(f" - {dt.code}") count += 1 if count == 5: output.append(" * -") count = 0 output.extend([" -" for _ in range(5 - count)]) output.append(" * - :class:`enum.Enum`") output.append(" - enumeration") output.append(" -")