def __new__(metacls, clsname, bases, clsdict, **kwargs): if hasattr(clsdict, 'tags'): # ▼ Collect all base class tag dicts + current class tag dict tagDicts = filter(None, (parent.__dict__.get('__tags__') for parent in bases)) tagsItems = (item for tagsDict in attachItem(tagDicts, clsdict.tags) for item in tagsDict.items()) # ▼ Merge all tags by tag name newTags = {} getIndex = itemgetter(0) for k, g in groupby(sorted(tagsItems, key=getIndex), getIndex): newTags[k] = tuple(chain.from_iterable((i[1] for i in g))) # ▼ Check for name collisions for tagname, tags in newTags.items(): if len(set(tags)) < len(tags): raise AttributeError( f"Class {clsname} has attr name conflict with base class in category {tagname}" f" (test. name is: {(tag for tag in tags if tag not in set(tags)).__next__()}" ) clsdict['__tags__'] = newTags return super().__new__(metacls, clsname, bases, dict(clsdict), **kwargs)
def collectDefaults(bases, currentDefaults): dicts = attachItem(iterable=filter(None, (parent.__dict__.get('_defaults_') for parent in reversed(bases))), append=currentDefaults) newDefaults = dicts.__next__().copy() for defaults in dicts: newDefaults.update(defaults) return newDefaults
def mergeTags(parents, currentTags): # Collect all base class tags dicts + current class tags dict tagsDicts = attachItem(filter(None, (parent.__dict__.get('__tags__') for parent in parents)), currentTags) # Take main parent's tags as base tags dict try: newTags = tagsDicts.__next__().copy() # Use current tags if no single parent defines any except StopIteration: return currentTags # Merge all tags by tag name into 'newTags' for tagsDict in tagsDicts: reduceItems = ((tagname, newTags[tagname] | namesSet) for tagname, namesSet in tagsDict.items()) for _ in starmap(partial(setitem, newTags), reduceItems): pass # TODO: Compare performance ▲ ▼, if negligible - replace with code below (more readable IMHO) # for tagname, updatedNamesSet in reduceItems: # setitem(newTags, tagname, updatedNamesSet) return newTags
def mergeParentDicts(parents, dictName, currentDict): dicts = attachItem( filter(None, (parent.__dict__.get(dictName) for parent in reversed(parents))), currentDict) newDict = dicts.__next__().copy() for attrDict in dicts: newDict.update(attrDict) return newDict