Skip to content

Commit

Permalink
Add --detailed and --summary for controlling output verbosity
Browse files Browse the repository at this point in the history
This changes -v/--verbose and -q/--quiet back to _only_ controlling the
log level.
  • Loading branch information
jherland committed Feb 20, 2023
1 parent 5375df9 commit e168f1b
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 40 deletions.
7 changes: 5 additions & 2 deletions fawltydeps/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,11 @@ def main() -> int:
if settings.output_format == OutputFormat.JSON:
analysis.print_json(sys.stdout)
else:
analysis.print_human_readable(sys.stdout, details=settings.verbosity > 0)
if settings.verbosity <= 0:
analysis.print_human_readable(
sys.stdout,
details=settings.output_format == OutputFormat.HUMAN_DETAILED,
)
if settings.output_format == OutputFormat.HUMAN_SUMMARY:
print(f"\n{VERBOSE_PROMPT}\n")

# Exit codes:
Expand Down
43 changes: 29 additions & 14 deletions fawltydeps/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class Action(OrderedEnum):
class OutputFormat(OrderedEnum):
"""Output formats provided by the FawltyDeps application."""

HUMAN_READABLE = "human_readable"
HUMAN_SUMMARY = "human_summary"
HUMAN_DETAILED = "human_detailed"
JSON = "json"


Expand Down Expand Up @@ -98,7 +99,7 @@ class Settings(BaseSettings): # type: ignore
actions: Set[Action] = {Action.REPORT_UNDECLARED, Action.REPORT_UNUSED}
code: PathOrSpecial = Path(".")
deps: Path = Path(".")
output_format: OutputFormat = OutputFormat.HUMAN_READABLE
output_format: OutputFormat = OutputFormat.HUMAN_SUMMARY
ignore_undeclared: Set[str] = set()
ignore_unused: Set[str] = set()
verbosity: int = 0
Expand Down Expand Up @@ -221,13 +222,6 @@ def populate_parser_options(cls, parser: argparse._ActionsContainer) -> None:
" to looking for supported files in the current directory)"
),
)
parser.add_argument(
"--json",
dest="output_format",
action="store_const",
const="json",
help="Generate JSON output instead of a human-readable report",
)
parser.add_argument(
"--ignore-undeclared",
nargs="+",
Expand All @@ -246,17 +240,38 @@ def populate_parser_options(cls, parser: argparse._ActionsContainer) -> None:
" dependencies, e.g. --ignore-unused pylint black"
),
)

# Provide three mutually exclusive options to select output format.
output_format = parser.add_mutually_exclusive_group()
output_format.add_argument(
"--summary",
dest="output_format",
action="store_const",
const="human_summary",
help="Generate human-readable summary report",
)
output_format.add_argument(
"--detailed",
dest="output_format",
action="store_const",
const="human_detailed",
help="Generate human-readable detailed report",
)
output_format.add_argument(
"--json",
dest="output_format",
action="store_const",
const="json",
help="Generate JSON output instead of a human-readable report",
)

# The following two do not correspond directly to a Settings member,
# but the latter is subtracted from the former to make .verbosity.
parser.add_argument(
"-v",
"--verbose",
action="count",
help=(
"Increase log level (WARNING by default, -v: INFO, -vv: DEBUG)"
" and verbosity of the output (without location details by default,"
" -v, -vv: with location details)"
),
help="Increase log level (WARNING by default, -v: INFO, -vv: DEBUG)",
)
parser.add_argument(
"-q",
Expand Down
50 changes: 27 additions & 23 deletions tests/test_cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def run_fawltydeps(
return proc.stdout.strip(), proc.stderr.strip(), proc.returncode


def test_list_imports_verbose__from_dash__prints_imports_from_stdin():
def test_list_imports_detailed__from_dash__prints_imports_from_stdin():
code = dedent(
"""\
from pathlib import Path
Expand All @@ -79,14 +79,14 @@ def test_list_imports_verbose__from_dash__prints_imports_from_stdin():
"INFO:fawltydeps.extract_imports:Parsing Python code from standard input"
)
output, errors, returncode = run_fawltydeps(
"--list-imports", "-v", "--code=-", to_stdin=code
"--list-imports", "--detailed", "-v", "--code=-", to_stdin=code
)
assert output.splitlines() == expect
assert errors == expect_logs
assert returncode == 0


def test_list_imports_quiet__from_dash__prints_imports_from_stdin():
def test_list_imports_summary__from_dash__prints_imports_from_stdin():
code = dedent(
"""\
from pathlib import Path
Expand All @@ -100,7 +100,7 @@ def test_list_imports_quiet__from_dash__prints_imports_from_stdin():

expect = ["foo", "numpy", "requests"] # alphabetically sorted
output, errors, returncode = run_fawltydeps(
"--list-imports", "--code=-", to_stdin=code
"--list-imports", "--summary", "--code=-", to_stdin=code
)
assert output.splitlines()[:-2] == expect
assert errors == ""
Expand Down Expand Up @@ -129,7 +129,7 @@ def test_list_imports__from_py_file__prints_imports_from_file(write_tmp_files):
f"INFO:fawltydeps.extract_imports:Parsing Python file {tmp_path}/myfile.py"
)
output, errors, returncode = run_fawltydeps(
"--list-imports", "-v", f"--code={tmp_path}/myfile.py"
"--list-imports", "--detailed", "-v", f"--code={tmp_path}/myfile.py"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand Down Expand Up @@ -191,7 +191,7 @@ def test_list_imports__from_ipynb_file__prints_imports_from_file(write_tmp_files
f"INFO:fawltydeps.extract_imports:Parsing Notebook file {tmp_path}/myfile.ipynb"
)
output, errors, returncode = run_fawltydeps(
"--list-imports", "-v", f"--code={tmp_path}/myfile.ipynb"
"--list-imports", "--detailed", "-v", f"--code={tmp_path}/myfile.ipynb"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand Down Expand Up @@ -225,7 +225,7 @@ def test_list_imports__from_dir__prints_imports_from_py_and_ipynb_files_only(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
"--list-imports", "-v", f"--code={tmp_path}"
"--list-imports", "--detailed", "-v", f"--code={tmp_path}"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand Down Expand Up @@ -255,7 +255,7 @@ def test_list_imports__from_missing_file__fails_with_exit_code_2(tmp_path):
def test_list_imports__from_empty_dir__logs_but_extracts_nothing(tmp_path):
# Enable log level INFO with -v
output, errors, returncode = run_fawltydeps(
"--list-imports", f"--code={tmp_path}", "-v"
"--list-imports", f"--code={tmp_path}", "--detailed", "-v"
)
assert output == ""
assert f"Parsing Python files under {tmp_path}" in errors
Expand All @@ -275,7 +275,7 @@ def test_list_deps__dir__prints_deps_from_requirements_txt(
f"{tmp_path}/requirements.txt: requests",
]
output, errors, returncode = run_fawltydeps(
"--list-deps", "-v", f"--deps={tmp_path}"
"--list-deps", "--detailed", "-v", f"--deps={tmp_path}"
)
assert output.splitlines() == expect
assert errors == ""
Expand Down Expand Up @@ -360,7 +360,7 @@ def test_list_deps__missing_path__fails_with_exit_code_2(tmp_path):
def test_list_deps__empty_dir__verbosely_logs_but_extracts_nothing(tmp_path):
# Enable log level INFO with -v
output, errors, returncode = run_fawltydeps(
"--list-deps", f"--deps={tmp_path}", "-v"
"--list-deps", f"--deps={tmp_path}", "--detailed", "-v"
)
assert output == ""
assert errors == ""
Expand Down Expand Up @@ -401,7 +401,7 @@ def test_check__simple_project_with_missing_deps__reports_undeclared(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
"--check", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
"--check", "--detailed", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand All @@ -425,7 +425,7 @@ def test_check__simple_project_with_extra_deps__reports_unused(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
"--check", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
"--check", "--detailed", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand All @@ -452,7 +452,7 @@ def test_check__simple_project__can_report_both_undeclared_and_unused(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
"--check", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
"--check", "--detailed", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand Down Expand Up @@ -540,7 +540,11 @@ def test_check_undeclared__simple_project__reports_only_undeclared(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
"--check-undeclared", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
"--check-undeclared",
"--detailed",
"-v",
f"--code={tmp_path}",
f"--deps={tmp_path}",
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand All @@ -564,7 +568,7 @@ def test_check_unused__simple_project__reports_only_unused(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
"--check-unused", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
"--check-unused", "--detailed", "-v", f"--code={tmp_path}", f"--deps={tmp_path}"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand All @@ -591,7 +595,7 @@ def test__no_action__defaults_to_check_action(
f"INFO:fawltydeps.extract_imports:Parsing Python files under {tmp_path}"
)
output, errors, returncode = run_fawltydeps(
f"--code={tmp_path}", "-v", f"--deps={tmp_path}"
f"--code={tmp_path}", "--detailed", "-v", f"--deps={tmp_path}"
)
assert output.splitlines() == expect
assert errors == expect_logs
Expand All @@ -615,7 +619,7 @@ def test__no_options__defaults_to_check_action_in_current_dir(
" requirements.txt",
]
expect_logs = "INFO:fawltydeps.extract_imports:Parsing Python files under ."
output, errors, returncode = run_fawltydeps("-v", cwd=tmp_path)
output, errors, returncode = run_fawltydeps("--detailed", "-v", cwd=tmp_path)
assert output.splitlines() == expect
assert errors == expect_logs
assert returncode == 3
Expand Down Expand Up @@ -750,25 +754,25 @@ def test_cmdline_on_ignored_undeclared_option(
),
pytest.param(
{"actions": ["list_imports"]},
["--verbose"],
["--detailed"],
["code.py:1: requests"],
id="combine_actions_in_config_with_verbose_on_command_line",
id="combine_actions_in_config_with_detailed_on_command_line",
),
pytest.param(
{"actions": ["list_imports"], "verbosity": 3},
{"actions": ["list_imports"], "output_format": "human_detailed"},
["--list-deps"],
["requirements.txt: pandas"],
id="override_some_config_directives_on_command_line",
),
pytest.param(
{"actions": ["list_imports"], "verbosity": 3},
["--quiet"],
{"actions": ["list_imports"], "output_format": "human_detailed"},
["--summary"],
[
"requests",
"",
VERBOSE_PROMPT,
],
id="override_verbosity_from_config_with_quiet_on_command_line",
id="override_output_format_from_config_with_command_line_option",
),
],
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
actions={Action.REPORT_UNDECLARED, Action.REPORT_UNUSED},
code=Path("."),
deps=Path("."),
output_format=OutputFormat.HUMAN_READABLE,
output_format=OutputFormat.HUMAN_SUMMARY,
ignore_undeclared=set(),
ignore_unused=set(),
verbosity=0,
Expand Down

0 comments on commit e168f1b

Please sign in to comment.