Skip to content

Empty selections leave stale metrics and ignore EDN output format #20

Open

Description

@Mazinkam

Summary

When selection contains no source files, run() returns success before replacing .metrics/dry.edn or applying the requested output format. A previous report remains on disk, while both --edn and --format edn print a plain-text sentence instead of the documented EDN map.

Verified against master at 66ff6d21a42c04afcad89c78a80066176d1294b0 (dryer 0.1.0), using Python 3.14.8.

Minimal reproduction

Requires Git and Python >=3.11. No package installation, parser downloads, or native dependencies are needed. Run in a fresh disposable checkout:

work=$(mktemp -d)
git clone https://github.com/unclebob/dryer.git "$work/dryer"
cd "$work/dryer"
git checkout 66ff6d21a42c04afcad89c78a80066176d1294b0
python3 -B - <<'PY'
"""Run from a dryer checkout with Python >=3.11; standard library only."""
import ast
import contextlib
import io
from pathlib import Path
import sys
import tempfile
import types

sys.dont_write_bytecode = True
sys.path.insert(0, str(Path('src').resolve()))
# Load the real CLI without its parser-dependent scan import. Empty selection
# must return before either omitted scan function can be called.
path = Path('src/dryer/cli.py')
tree = ast.parse(path.read_text())
tree.body = [n for n in tree.body if not (isinstance(n, ast.ImportFrom) and n.module == 'dryer.scan')]
cli = types.ModuleType('dryer_cli_probe')
sys.modules[cli.__name__] = cli
exec(compile(tree, str(path), 'exec'), cli.__dict__)

with tempfile.TemporaryDirectory() as directory:
    root = Path(directory)
    metrics = root / '.metrics/dry.edn'
    metrics.parent.mkdir()
    old = '{:candidates [{:score 1.0 :language "python" :left {:file "old.py" :start-line 1 :end-line 4} :right {:file "old2.py" :start-line 1 :end-line 4} :left-nodes 20 :right-nodes 20}]}\n'
    for flag in ('--edn', '--format'):
        metrics.write_text(old)
        output = io.StringIO()
        args = ['--root', directory, flag] + (['edn'] if flag == '--format' else [])
        with contextlib.redirect_stdout(output):
            code = cli.run(args)
        print(f'{args[2:]}: exit={code}, stdout={output.getvalue()!r}, stale_metrics={metrics.read_text() == old}')
        assert code == 0
        assert output.getvalue() == 'No source files to analyze.\n'
        assert metrics.read_text() == old
PY

This source-level probe executes the original functions through Python AST loading, omitting only parser-dependent imports. It does not replace any exercised function or run the installed CLI/parser end to end. Its assertions verify the observed defect, not the expected fixed behavior.

Actual result

['--edn']: exit=0, stdout='No source files to analyze.\n', stale_metrics=True
['--format', 'edn']: exit=0, stdout='No source files to analyze.\n', stale_metrics=True

Expected result

On an empty selection, write a fresh empty report and honor EDN output, or fail explicitly with a documented no-analysis status that cannot be mistaken for a successfully written report. The help currently defines exit 0 as “the report was written.”

Cause

The early return in run() precedes metrics writing and EDN rendering. render_edn([]) already supports an empty result.

Suggested regression test

Seed .metrics/dry.edn with a valid nonempty previous report, select zero files, and invoke both EDN spellings. Assert the selected empty-result contract: for a successful run, stdout and metrics contain the fresh {:candidates []} map, not old findings. Also cover a selection filter that matches nothing. The existing empty-project test checks only text stdout, not stale metrics or EDN.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions