Sun, 05 Mar 2023 12:26:12 +0100
Third Party packages
- Upgraded eradicate to version 2.2.0.
- Upgraded pipdeptree to version 2.5.2.
- Upgraded pip-licenses to version 4.1.0.
--- a/docs/ThirdParty.md Sat Mar 04 18:09:08 2023 +0100 +++ b/docs/ThirdParty.md Sun Mar 05 12:26:12 2023 +0100 @@ -5,11 +5,11 @@ | Name | Version | License | |:------------:|:---------:|:----------------------------| -| eradicate | 2.1.0 | MIT License (Expat License) | +| eradicate | 2.2.0 | MIT License (Expat License) | | jasy | 1.5-beta6 | MIT License (MIT) | | mccabe | 0.7.0 | MIT License (Expat License) | -| pipdeptree | 2.3.3 | MIT License (MIT) | -| pip-licenses | 4.0.2 | MIT License (MIT) | +| pipdeptree | 2.5.2 | MIT License (MIT) | +| pip-licenses | 4.1.0 | MIT License (MIT) | | pycodestyle | 2.10.0 | MIT License (Expat License) | | pyflakes | 3.0.1 | MIT License (MIT) | | | | |
--- a/docs/changelog.md Sat Mar 04 18:09:08 2023 +0100 +++ b/docs/changelog.md Sun Mar 05 12:26:12 2023 +0100 @@ -6,6 +6,10 @@ - Added functionality to search for known boot volumes in the UF2 flash dialog. - Added functionality to install packages using `mip` or `upip`. - Added support for WiFi enabled boards. +- Third Party packages + - Upgraded eradicate to version 2.2.0. + - Upgraded pipdeptree to version 2.5.2. + - Upgraded pip-licenses to version 4.1.0. ### Version 23.3 - bug fixes
--- a/src/eric7/PipInterface/pipdeptree.py Sat Mar 04 18:09:08 2023 +0100 +++ b/src/eric7/PipInterface/pipdeptree.py Sun Mar 05 12:26:12 2023 +0100 @@ -46,6 +46,7 @@ from collections.abc import Mapping from importlib import import_module from itertools import chain +from textwrap import dedent from pip._vendor import pkg_resources @@ -55,7 +56,7 @@ from pip import FrozenRequirement -__version__ = '2.3.3' # eric-ide modification: from version.py +__version__ = '2.5.2' # eric-ide modification: from version.py flatten = chain.from_iterable @@ -590,6 +591,91 @@ return json.dumps([aux(p) for p in nodes], indent=indent) +def render_mermaid(tree) -> str: + """Produce a Mermaid flowchart from the dependency graph. + + :param dict tree: dependency graph + """ + # List of reserved keywords in Mermaid that cannot be used as node names. + # See: https://github.com/mermaid-js/mermaid/issues/4182#issuecomment-1454787806 + reserved_ids: set[str] = { + "C4Component", + "C4Container", + "C4Deployment", + "C4Dynamic", + "_blank", + "_parent", + "_self", + "_top", + "call", + "class", + "classDef", + "click", + "end", + "flowchart", + "flowchart-v2", + "graph", + "interpolate", + "linkStyle", + "style", + "subgraph", + } + node_ids_map: dict[str:str] = {} + + def mermaid_id(key: str) -> str: + """Returns a valid Mermaid node ID from a string.""" + # If we have already seen this key, return the canonical ID. + canonical_id = node_ids_map.get(key) + if canonical_id is not None: + return canonical_id + # If the key is not a reserved keyword, return it as is, and update the map. + if key not in reserved_ids: + node_ids_map[key] = key + return key + # If the key is a reserved keyword, append a number to it. + number = 0 + while True: + new_id = f"{key}_{number}" + if new_id not in node_ids_map: + node_ids_map[key] = new_id + return new_id + number += 1 + + # Use a sets to avoid duplicate entries. + nodes: set[str] = set() + edges: set[str] = set() + + for pkg, deps in tree.items(): + pkg_label = f"{pkg.project_name}\\n{pkg.version}" + pkg_key = mermaid_id(pkg.key) + nodes.add(f'{pkg_key}["{pkg_label}"]') + for dep in deps: + edge_label = dep.version_spec or "any" + dep_key = mermaid_id(dep.key) + if dep.is_missing: + dep_label = f"{dep.project_name}\\n(missing)" + nodes.add(f'{dep_key}["{dep_label}"]:::missing') + edges.add(f"{pkg_key} -.-> {dep_key}") + else: + edges.add(f'{pkg_key} -- "{edge_label}" --> {dep_key}') + + # Produce the Mermaid Markdown. + indent = " " * 4 + output = dedent( + f"""\ + flowchart TD + {indent}classDef missing stroke-dasharray: 5 + """ + ) + # Sort the nodes and edges to make the output deterministic. + output += indent + output += f"\n{indent}".join(node for node in sorted(nodes)) + output += "\n" + indent + output += f"\n{indent}".join(edge for edge in sorted(edges)) + output += "\n" + return output + + def dump_graphviz(tree, output_format="dot", is_reverse=False): """Output dependency graph as one of the supported GraphViz output formats. @@ -652,7 +738,11 @@ # Allow output of dot format, even if GraphViz isn't installed. if output_format == "dot": - return graph.source + # Emulates graphviz.dot.Dot.__iter__() to force the sorting of graph.body. + # Fixes https://github.com/tox-dev/pipdeptree/issues/188 + # That way we can guarantee the output of the dot format is deterministic + # and stable. + return "".join([tuple(graph)[0]] + sorted(graph.body) + [graph._tail]) # As it's unknown if the selected output format is binary or not, try to # decode it as UTF8 and only print it out in binary if that's not possible. @@ -812,6 +902,12 @@ ), ) parser.add_argument( + "--mermaid", + action="store_true", + default=False, + help=("Display dependency tree as a Maermaid graph. " "This option overrides all other options."), + ) + parser.add_argument( "--graph-output", dest="output_format", help=( @@ -920,6 +1016,8 @@ print(render_json(tree, indent=4)) elif args.json_tree: print(render_json_tree(tree, indent=4)) + elif args.mermaid: + print(render_mermaid(tree)) elif args.output_format: output = dump_graphviz(tree, output_format=args.output_format, is_reverse=args.reverse) print_graphviz(output)
--- a/src/eric7/PipInterface/piplicenses.py Sat Mar 04 18:09:08 2023 +0100 +++ b/src/eric7/PipInterface/piplicenses.py Sun Mar 05 12:26:12 2023 +0100 @@ -97,7 +97,7 @@ __pkgname__ = "pip-licenses" -__version__ = "4.0.2" +__version__ = "4.1.0" __author__ = "raimon" __license__ = "MIT" __summary__ = ( @@ -269,7 +269,9 @@ ) if fail_on_licenses: - failed_licenses = license_names.intersection(fail_on_licenses) + failed_licenses = case_insensitive_set_intersect( + license_names, fail_on_licenses + ) if failed_licenses: sys.stderr.write( "fail-on license {} was found for package " @@ -282,7 +284,9 @@ sys.exit(1) if allow_only_licenses: - uncommon_licenses = license_names.difference(allow_only_licenses) + uncommon_licenses = case_insensitive_set_diff( + license_names, allow_only_licenses + ) if len(uncommon_licenses) == len(license_names): sys.stderr.write( "license {} not in allow-only licenses was found" @@ -367,6 +371,26 @@ return licenses +def case_insensitive_set_intersect(set_a, set_b): + """Same as set.intersection() but case-insensitive""" + common_items = set() + set_b_lower = {item.lower() for item in set_b} + for elem in set_a: + if elem.lower() in set_b_lower: + common_items.add(elem) + return common_items + + +def case_insensitive_set_diff(set_a, set_b): + """Same as set.difference() but case-insensitive""" + uncommon_items = set() + set_b_lower = {item.lower() for item in set_b} + for elem in set_a: + if not elem.lower() in set_b_lower: + uncommon_items.add(elem) + return uncommon_items + + def find_license_from_classifier(classifiers: list[str]) -> list[str]: licenses = [] for classifier in filter(lambda c: c.startswith("License"), classifiers):
--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/eradicate.py Sat Mar 04 18:09:08 2023 +0100 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/eradicate.py Sun Mar 05 12:26:12 2023 +0100 @@ -30,7 +30,7 @@ import re import tokenize -__version__ = '2.1.0' +__version__ = '2.2.0' class Eradicator(object): @@ -57,7 +57,9 @@ r'noqa', r'nosec', r'type:\s*ignore', + r'mypy:', r'fmt:\s*(on|off)', + r'yapf:\s*(enable|disable)', r'isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?\])?)', r'TODO', r'FIXME',