def main(self, *, context):
    args = context.args
    descriptors = get_package_descriptors(args)
    decorators = topological_order_packages(descriptors, recursive_categories=('run',))
    select_package_decorators(args, decorators)
    if (not args.dot):
        if args.legend:
            print('+ marks when the package in this row can be processed')
            print('* marks a direct dependency from the package indicated by the + in the same column to the package in this row')
            print('. marks a transitive dependency')
            print()
        shown_decorators = list(filter((lambda d: d.selected), decorators))
        max_length = max(([len(m.descriptor.name) for m in shown_decorators] + [0]))
        lines = [m.descriptor.name.ljust((max_length + 2)) for m in shown_decorators]
        depends = [m.descriptor.get_dependencies() for m in shown_decorators]
        rec_depends = [m.descriptor.get_recursive_dependencies([d.descriptor for d in decorators], recursive_categories=('run',)) for m in shown_decorators]
        empty_cells = 0
        for (i, decorator) in enumerate(shown_decorators):
            for j in range(len(lines)):
                if (j == i):
                    lines[j] += '+'
                elif (shown_decorators[j].descriptor.name in depends[i]):
                    lines[j] += '*'
                elif (shown_decorators[j].descriptor.name in rec_depends[i]):
                    lines[j] += '.'
                else:
                    lines[j] += ' '
                    empty_cells += 1
        if args.density:
            empty_fraction = ((empty_cells / (len(lines) * (len(lines) - 1))) if (len(lines) > 1) else 1.0)
            density_percentage = (200.0 * (1.0 - empty_fraction))
            print(('dependency density %.2f %%' % density_percentage))
            print()
    else:
        lines = ['digraph graphname {']
        decorators_by_name = defaultdict(set)
        for deco in decorators:
            decorators_by_name[deco.descriptor.name].add(deco)
        selected_pkg_names = [m.descriptor.name for m in decorators if (m.selected or args.dot_include_skipped)]
        has_duplicate_names = (len(selected_pkg_names) != len(set(selected_pkg_names)))
        selected_pkg_names = set(selected_pkg_names)
        nodes = OrderedDict()
        for deco in reversed(decorators):
            if (deco.selected or args.dot_include_skipped):
                nodes[deco] = Path(deco.descriptor.path).parent
        direct_edges = defaultdict(set)
        for deco in reversed(decorators):
            if ((not deco.selected) and (not args.dot_include_skipped)):
                continue
            for (category, deps) in deco.descriptor.dependencies.items():
                for dep in deps:
                    if (dep not in selected_pkg_names):
                        continue
                    direct_edges[(deco, dep)].add(category)
        indirect_edges = defaultdict(set)
        for deco in reversed(decorators):
            if (not deco.selected):
                continue
            for (category, deps) in deco.descriptor.dependencies.items():
                for dep in deps:
                    if (dep in selected_pkg_names):
                        continue
                    if (dep not in decorators_by_name.keys()):
                        continue
                    for rdep in itertools.chain.from_iterable((d.recursive_dependencies for d in decorators_by_name[dep])):
                        if (rdep not in selected_pkg_names):
                            continue
                        if ((deco, rdep) in direct_edges):
                            continue
                        indirect_edges[(deco, rdep)].add(category)
        try:
            common_path = os.path.commonpath([str(p) for p in nodes.values()])
        except ValueError:
            common_path = None

        def get_node_data(decorator):
            nonlocal args
            nonlocal has_duplicate_names
            if (not has_duplicate_names):
                return (decorator.descriptor.name, ('' if (decorator.selected or (not args.dot_include_skipped)) else '[color = "gray" fontcolor = "gray"]'))
            descriptor_id = id(decorator.descriptor)
            return ('{decorator.descriptor.name}_{descriptor_id}'.format_map(locals()), ' [label = "{decorator.descriptor.name}"]'.format_map(locals()))
        if ((not args.dot_cluster) or (common_path is None)):
            for deco in nodes.keys():
                if ((not deco.selected) and (not args.dot_include_skipped)):
                    continue
                (node_name, attributes) = get_node_data(deco)
                lines.append('  "{node_name}"{attributes};'.format_map(locals()))
        else:
            clusters = defaultdict(set)
            for (deco, path) in nodes.items():
                clusters[path.relative_to(common_path)].add(deco)
            for (i, cluster) in zip(range(len(clusters)), clusters.items()):
                (path, decos) = cluster
                if path.name:
                    lines.append('  subgraph cluster_{i} {{'.format_map(locals()))
                    lines.append('    label = "{path}";'.format_map(locals()))
                    indent = '    '
                else:
                    indent = '  '
                for deco in decos:
                    (node_name, attributes) = get_node_data(deco)
                    lines.append('{indent}"{node_name}"{attributes};'.format_map(locals()))
                if path.name:
                    lines.append('  }')
        color_mapping = OrderedDict((('build', '#0000ff'), ('run', '#ff0000'), ('test', '#d2b48c')))
        for (style, edges) in zip(('', ', style="dashed"'), (direct_edges, indirect_edges)):
            for ((deco_start, node_end), categories) in edges.items():
                (start_name, _) = get_node_data(deco_start)
                for deco in decorators_by_name[node_end]:
                    (end_name, _) = get_node_data(deco)
                    edge_alpha = ('' if (deco_start.selected and deco.selected) else '77')
                    colors = ':'.join([(color + edge_alpha) for (category, color) in color_mapping.items() if (category in categories)])
                    lines.append('  "{start_name}" -> "{end_name}" [color="{colors}"{style}];'.format_map(locals()))
        if args.legend:
            lines.append('  subgraph cluster_legend {')
            lines.append('    color=gray')
            lines.append('    label="Legend";')
            lines.append('    margin=0;')
            lines.append('    node [label="", shape=none];')
            previous_node = '_legend_first'
            for (dependency_type, color) in color_mapping.items():
                next_node = ('_legend_' + dependency_type)
                lines.append('    {previous_node} -> {next_node} [label="{dependency_type} dep.", color="{color}"];'.format_map(locals()))
                previous_node = next_node
            lines.append('    {previous_node} -> _legend_last [label="indirect dep.", style="dashed"];'.format_map(locals()))
            lines.append('    {')
            lines.append('      rank=same;')
            lines.append('      _legend_first;')
            for dependency_type in color_mapping.keys():
                lines.append('      _legend_{dependency_type};'.format_map(locals()))
            lines.append('      _legend_last;')
            lines.append('    }')
            lines.append('  }')
        lines.append('}')
    for line in lines:
        print(line)
def main(self, *, context):
    args = context.args
    descriptors = get_package_descriptors(args)
    decorators = topological_order_packages(descriptors, recursive_categories=('run',))
    select_package_decorators(args, decorators)
    if (not args.dot):
        if args.legend:
            print('+ marks when the package in this row can be processed')
            print('* marks a direct dependency from the package indicated by the + in the same column to the package in this row')
            print('. marks a transitive dependency')
            print()
        shown_decorators = list(filter((lambda d: d.selected), decorators))
        max_length = max(([len(m.descriptor.name) for m in shown_decorators] + [0]))
        lines = [m.descriptor.name.ljust((max_length + 2)) for m in shown_decorators]
        depends = [m.descriptor.get_dependencies() for m in shown_decorators]
        rec_depends = [m.descriptor.get_recursive_dependencies([d.descriptor for d in decorators], recursive_categories=('run',)) for m in shown_decorators]
        empty_cells = 0
        for (i, decorator) in enumerate(shown_decorators):
            for j in range(len(lines)):
                if (j == i):
                    lines[j] += '+'
                elif (shown_decorators[j].descriptor.name in depends[i]):
                    lines[j] += '*'
                elif (shown_decorators[j].descriptor.name in rec_depends[i]):
                    lines[j] += '.'
                else:
                    lines[j] += ' '
                    empty_cells += 1
        if args.density:
            empty_fraction = ((empty_cells / (len(lines) * (len(lines) - 1))) if (len(lines) > 1) else 1.0)
            density_percentage = (200.0 * (1.0 - empty_fraction))
            print(('dependency density %.2f %%' % density_percentage))
            print()
    else:
        lines = ['digraph graphname {']
        decorators_by_name = defaultdict(set)
        for deco in decorators:
            decorators_by_name[deco.descriptor.name].add(deco)
        selected_pkg_names = [m.descriptor.name for m in decorators if (m.selected or args.dot_include_skipped)]
        has_duplicate_names = (len(selected_pkg_names) != len(set(selected_pkg_names)))
        selected_pkg_names = set(selected_pkg_names)
        nodes = OrderedDict()
        for deco in reversed(decorators):
            if (deco.selected or args.dot_include_skipped):
                nodes[deco] = Path(deco.descriptor.path).parent
        direct_edges = defaultdict(set)
        for deco in reversed(decorators):
            if ((not deco.selected) and (not args.dot_include_skipped)):
                continue
            for (category, deps) in deco.descriptor.dependencies.items():
                for dep in deps:
                    if (dep not in selected_pkg_names):
                        continue
                    direct_edges[(deco, dep)].add(category)
        indirect_edges = defaultdict(set)
        for deco in reversed(decorators):
            if (not deco.selected):
                continue
            for (category, deps) in deco.descriptor.dependencies.items():
                for dep in deps:
                    if (dep in selected_pkg_names):
                        continue
                    if (dep not in decorators_by_name.keys()):
                        continue
                    for rdep in itertools.chain.from_iterable((d.recursive_dependencies for d in decorators_by_name[dep])):
                        if (rdep not in selected_pkg_names):
                            continue
                        if ((deco, rdep) in direct_edges):
                            continue
                        indirect_edges[(deco, rdep)].add(category)
        try:
            common_path = os.path.commonpath([str(p) for p in nodes.values()])
        except ValueError:
            common_path = None

        def get_node_data(decorator):
            nonlocal args
            nonlocal has_duplicate_names
            if (not has_duplicate_names):
                return (decorator.descriptor.name, ( if (decorator.selected or (not args.dot_include_skipped)) else '[color = "gray" fontcolor = "gray"]'))
            descriptor_id = id(decorator.descriptor)
            return ('{decorator.descriptor.name}_{descriptor_id}'.format_map(locals()), ' [label = "{decorator.descriptor.name}"]'.format_map(locals()))
        if ((not args.dot_cluster) or (common_path is None)):
            for deco in nodes.keys():
                if ((not deco.selected) and (not args.dot_include_skipped)):
                    continue
                (node_name, attributes) = get_node_data(deco)
                lines.append('  "{node_name}"{attributes};'.format_map(locals()))
        else:
            clusters = defaultdict(set)
            for (deco, path) in nodes.items():
                clusters[path.relative_to(common_path)].add(deco)
            for (i, cluster) in zip(range(len(clusters)), clusters.items()):
                (path, decos) = cluster
                if path.name:
                    lines.append('  subgraph cluster_{i} {{'.format_map(locals()))
                    lines.append('    label = "{path}";'.format_map(locals()))
                    indent = '    '
                else:
                    indent = '  '
                for deco in decos:
                    (node_name, attributes) = get_node_data(deco)
                    lines.append('{indent}"{node_name}"{attributes};'.format_map(locals()))
                if path.name:
                    lines.append('  }')
        color_mapping = OrderedDict((('build', '#0000ff'), ('run', '#ff0000'), ('test', '#d2b48c')))
        for (style, edges) in zip((, ', style="dashed"'), (direct_edges, indirect_edges)):
            for ((deco_start, node_end), categories) in edges.items():
                (start_name, _) = get_node_data(deco_start)
                for deco in decorators_by_name[node_end]:
                    (end_name, _) = get_node_data(deco)
                    edge_alpha = ( if (deco_start.selected and deco.selected) else '77')
                    colors = ':'.join([(color + edge_alpha) for (category, color) in color_mapping.items() if (category in categories)])
                    lines.append('  "{start_name}" -> "{end_name}" [color="{colors}"{style}];'.format_map(locals()))
        if args.legend:
            lines.append('  subgraph cluster_legend {')
            lines.append('    color=gray')
            lines.append('    label="Legend";')
            lines.append('    margin=0;')
            lines.append('    node [label=, shape=none];')
            previous_node = '_legend_first'
            for (dependency_type, color) in color_mapping.items():
                next_node = ('_legend_' + dependency_type)
                lines.append('    {previous_node} -> {next_node} [label="{dependency_type} dep.", color="{color}"];'.format_map(locals()))
                previous_node = next_node
            lines.append('    {previous_node} -> _legend_last [label="indirect dep.", style="dashed"];'.format_map(locals()))
            lines.append('    {')
            lines.append('      rank=same;')
            lines.append('      _legend_first;')
            for dependency_type in color_mapping.keys():
                lines.append('      _legend_{dependency_type};'.format_map(locals()))
            lines.append('      _legend_last;')
            lines.append('    }')
            lines.append('  }')
        lines.append('}')
    for line in lines:
        print(line)