Skip to content

Commit

Permalink
Fix graphviz CLI failing (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaborbernat committed Jul 17, 2023
1 parent e861826 commit f405c66
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 40 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ html.skip_covered = false
paths.source = ["src", ".tox/*/lib/python*/site-packages", "*/src"]
run.parallel = true
run.plugins = ["covdefaults"]
report.fail_under = 89
subtract_omit = "*/__main__.py"

[tool.mypy]
Expand Down
21 changes: 11 additions & 10 deletions src/pipdeptree/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import sys
from typing import Sequence

from pipdeptree._cli import get_options
from pipdeptree._discovery import get_installed_distributions
Expand All @@ -11,29 +12,29 @@
from ._validate import validate


def main() -> None | int:
args = get_options()
result = handle_non_host_target(args)
def main(args: Sequence[str] | None = None) -> None | int:
options = get_options(args)
result = handle_non_host_target(options)
if result is not None:
return result

pkgs = get_installed_distributions(local_only=args.local_only, user_only=args.user_only)
pkgs = get_installed_distributions(local_only=options.local_only, user_only=options.user_only)
tree = PackageDAG.from_pkgs(pkgs)
is_text_output = not any([args.json, args.json_tree, args.output_format])
is_text_output = not any([options.json, options.json_tree, options.output_format])

return_code = validate(args, is_text_output, tree)
return_code = validate(options, is_text_output, tree)

# Reverse the tree (if applicable) before filtering, thus ensuring, that the filter will be applied on ReverseTree
if args.reverse:
if options.reverse:
tree = tree.reverse()

show_only = set(args.packages.split(",")) if args.packages else None
exclude = set(args.exclude.split(",")) if args.exclude else None
show_only = set(options.packages.split(",")) if options.packages else None
exclude = set(options.exclude.split(",")) if options.exclude else None

if show_only is not None or exclude is not None:
tree = tree.filter_nodes(show_only, exclude)

render(args, tree)
render(options, tree)

return return_code

Expand Down
8 changes: 4 additions & 4 deletions src/pipdeptree/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sys
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING, Sequence, cast

from .version import __version__

Expand All @@ -22,7 +22,7 @@ class Options(Namespace):
json: bool
json_tree: bool
mermaid: bool
graph_output: str | None
output_format: str | None
depth: float
encoding: str

Expand Down Expand Up @@ -135,9 +135,9 @@ def build_parser() -> ArgumentParser:
return parser


def get_options() -> Options:
def get_options(args: Sequence[str] | None) -> Options:
parser = build_parser()
return cast(Options, parser.parse_args())
return cast(Options, parser.parse_args(args))


__all__ = [
Expand Down
12 changes: 9 additions & 3 deletions src/pipdeptree/_render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ def render(options: Options, tree: PackageDAG) -> None:
elif options.mermaid:
print(render_mermaid(tree)) # noqa: T201
elif options.output_format:
assert options.graph_output is not None # noqa: S101
render_graphviz(tree, output_format=options.graph_output, reverse=options.reverse)
assert options.output_format is not None # noqa: S101
render_graphviz(tree, output_format=options.output_format, reverse=options.reverse)
else:
render_text(tree, options.depth, options.encoding_type, options.all, options.freeze)
render_text(
tree,
max_depth=options.depth,
encoding=options.encoding_type,
list_all=options.all,
frozen=options.freeze,
)


__all__ = [
Expand Down
5 changes: 3 additions & 2 deletions src/pipdeptree/_render/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

def render_text(
tree: PackageDAG,
*,
max_depth: float,
encoding: str,
list_all: bool = True, # noqa: FBT001, FBT002
frozen: bool = False, # noqa: FBT001, FBT002
list_all: bool = True,
frozen: bool = False,
) -> None:
"""
Print tree as text on console.
Expand Down
40 changes: 40 additions & 0 deletions tests/render/test_render.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from __future__ import annotations

from math import inf
from typing import TYPE_CHECKING
from unittest.mock import ANY

from pipdeptree.__main__ import main

if TYPE_CHECKING:
from pytest_mock import MockerFixture


def test_json_routing(mocker: MockerFixture) -> None:
render = mocker.patch("pipdeptree._render.render_json")
main(["--json"])
render.assert_called_once_with(ANY)


def test_json_tree_routing(mocker: MockerFixture) -> None:
render = mocker.patch("pipdeptree._render.render_json_tree")
main(["--json-tree"])
render.assert_called_once_with(ANY)


def test_mermaid_routing(mocker: MockerFixture) -> None:
render = mocker.patch("pipdeptree._render.render_mermaid")
main(["--mermaid"])
render.assert_called_once_with(ANY)


def test_grahpviz_routing(mocker: MockerFixture) -> None:
render = mocker.patch("pipdeptree._render.render_graphviz")
main(["--graph-output", "dot"])
render.assert_called_once_with(ANY, output_format="dot", reverse=False)


def test_text_routing(mocker: MockerFixture) -> None:
render = mocker.patch("pipdeptree._render.render_text")
main([])
render.assert_called_once_with(ANY, encoding="utf-8", frozen=False, list_all=False, max_depth=inf)
21 changes: 3 additions & 18 deletions tests/render/test_text.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import sys
from typing import TYPE_CHECKING

import pytest
Expand All @@ -11,20 +10,6 @@
from pipdeptree._models import PackageDAG


class MockStdout:
"""
A wrapper to stdout that mocks the `encoding` attribute (to have `render_text()` render with unicode/non-unicode)
and `write()` (so that `print()` calls can write to stdout).
"""

def __init__(self, encoding: str) -> None:
self.stdout = sys.stdout
self.encoding = encoding

def write(self, text: str) -> None:
self.stdout.write(text)


@pytest.mark.parametrize(
("list_all", "reverse", "unicode", "expected_output"),
[
Expand Down Expand Up @@ -258,7 +243,7 @@ def test_render_text( # noqa: PLR0913
) -> None:
tree = example_dag.reverse() if reverse else example_dag
encoding = "utf-8" if unicode else "ascii"
render_text(tree, float("inf"), encoding, list_all=list_all, frozen=False)
render_text(tree, max_depth=float("inf"), encoding=encoding, list_all=list_all, frozen=False)
captured = capsys.readouterr()
assert "\n".join(expected_output).strip() == captured.out.strip()

Expand Down Expand Up @@ -359,7 +344,7 @@ def test_render_text_given_depth(
expected_output: list[str],
example_dag: PackageDAG,
) -> None:
render_text(example_dag, level, encoding="utf-8" if unicode else "ascii")
render_text(example_dag, max_depth=level, encoding="utf-8" if unicode else "ascii")
captured = capsys.readouterr()
assert "\n".join(expected_output).strip() == captured.out.strip()

Expand Down Expand Up @@ -518,6 +503,6 @@ def test_render_text_encoding(
expected_output: list[str],
example_dag: PackageDAG,
) -> None:
render_text(example_dag, level, encoding, True, False)
render_text(example_dag, max_depth=level, encoding=encoding, list_all=True, frozen=False)
captured = capsys.readouterr()
assert "\n".join(expected_output).strip() == captured.out.strip()
6 changes: 3 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ set_env =
_COVERAGE_SRC = {envsitepackagesdir}/sphinx_argparse_cli
commands =
python -m pytest {tty:--color=yes} {posargs: \
--junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}pipdeptree \
--cov {toxinidir}{/}tests --cov-fail-under=85 \
--cov {envsitepackagesdir}{/}pipdeptree --cov {toxinidir}{/}tests \
--cov-config=pyproject.toml --no-cov-on-fail --cov-report term-missing:skip-covered --cov-context=test \
--cov-report html:{envtmpdir}{/}htmlcov --cov-report xml:{toxworkdir}{/}coverage.{envname}.xml \
--junitxml {toxworkdir}{/}junit.{envname}.xml \
tests}
diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}{/}coverage.{envname}.xml
diff-cover --compare-branch {env:DIFF_AGAINST:origin/main} {toxworkdir}{/}coverage.{envname}.xml --fail-under 100

[testenv:fix]
description = format the code base to adhere to our styles, and complain about what we cannot do automatically
Expand Down

0 comments on commit f405c66

Please sign in to comment.