def uml_class_tree_graph(class_doc, linker, context=None, **options):
    """
    Return a `DotGraph` that graphically displays the class hierarchy
    for the given class, using UML notation.  Options:

      - max_attributes
      - max_operations
      - show_private_vars
      - show_magic_vars
      - link_attributes
    """
    nodes = {} # ClassDoc -> DotGraphUmlClassNode
    exclude = options.get('exclude', ())
        
    # Create nodes for class_doc and all its bases.
    for cls in class_doc.mro():
        if cls.pyval is object: continue # don't include `object`.
        if cls in exclude: break # stop if we get to an excluded class.
        if cls == class_doc: color = SELECTED_BG
        else: color = BASECLASS_BG
        nodes[cls] = DotGraphUmlClassNode(cls, linker, context,
                                          show_inherited_vars=False,
                                          collapsed=False, bgcolor=color)

    # Create nodes for all class_doc's subclasses.
    queue = [class_doc]
    for cls in queue:
        if (isinstance(cls, ClassDoc) and
            cls.subclasses not in (None, UNKNOWN)):
            for subcls in cls.subclasses:
                subcls_name = subcls.canonical_name[-1]
                if subcls not in nodes and subcls not in exclude:
                    queue.append(subcls)
                    nodes[subcls] = DotGraphUmlClassNode(
                        subcls, linker, context, collapsed=True,
                        bgcolor=SUBCLASS_BG)
                    
    # Only show variables in the class where they're defined for
    # *class_doc*.
    mro = class_doc.mro()
    for name, var in class_doc.variables.items():
        i = mro.index(var.container)
        for base in mro[i+1:]:
            if base.pyval is object: continue # don't include `object`.
            overridden_var = base.variables.get(name)
            if overridden_var and overridden_var.container == base:
                try:
                    if isinstance(overridden_var.value, RoutineDoc):
                        nodes[base].operations.remove(overridden_var)
                    else:
                        nodes[base].attributes.remove(overridden_var)
                except __HOLE__:
                    pass # var is filtered (eg private or magic)

    # Keep track of which nodes are part of the inheritance graph
    # (since link_attributes might add new nodes)
    inheritance_nodes = set(nodes.values())
        
    # Turn attributes into links.
    if options.get('link_attributes', True):
        for node in nodes.values():
            node.link_attributes(nodes)
            # Make sure that none of the new attribute edges break the
            # rank ordering assigned by inheritance.
            for edge in node.edges:
                if edge.end in inheritance_nodes:
                    edge['constraint'] = 'False'
                
    # Construct the graph.
    graph = DotGraph('UML class diagram for %s' % class_doc.canonical_name,
                     body='ranksep=.2\n;nodesep=.3\n')
    graph.nodes = nodes.values()
    
    # Add inheritance edges.
    for node in inheritance_nodes:
        for base in node.class_doc.bases:
            if base in nodes:
                graph.edges.append(DotGraphEdge(nodes[base], node,
                              dir='back', arrowtail='empty',
                              headport='body', tailport='body',
                              color=INH_LINK_COLOR, weight=100,
                              style='bold'))

    # And we're done!
    return graph

######################################################################
dataset/ETHPy150Open QuantSoftware/QuantSoftwareToolkit/Legacy/epydoc-3.0.1/epydoc/docwriter/dotgraph.py/uml_class_tree_graph