Skip to content

Commit 3124758

Browse files
ptr727claude
andcommitted
Refuse Anything That Is Not a File or a Directory, Not Merely What Is Absent
Testing for existence covered a typo and nothing else. A FIFO, a socket, and a device all exist and are neither a file nor a directory, and `discover` reads each of them as `.`, so the run still scanned the caller's directory while the rule set anchored on the argument's parent. The invariant held for the case I pictured and not for its siblings. The check now asks what the path is rather than whether it is there, which covers both. Two cases: the missing path, and a real FIFO created in the test, skipped where `os.mkfifo` is unavailable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 8756f3a commit 3124758

2 files changed

Lines changed: 23 additions & 8 deletions

File tree

scripts/prose_lint.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,14 +1119,15 @@ def main(argv: list[str] | None = None) -> int:
11191119
# Only a repository declares a model, so two of them refuse and anything else resolves to one anchor.
11201120
# TestScanRootDecidesTheRuleSet carries the cases and the reason each one exists.
11211121
scan_paths = a.paths or ['.']
1122-
# A path that does not exist is refused rather than absorbed.
1123-
# `discover` reads a non-file, non-directory argument as `.`, so a typo scanned the caller's directory while the rule set anchored on the missing path's parent.
1124-
absent = [p for p in scan_paths if not Path(p).exists()]
1125-
if absent:
1122+
# Anything that is not a file or a directory is refused rather than absorbed.
1123+
# `discover` reads such an argument as `.`, so it scanned the caller's directory while the rule set anchored on the argument's parent.
1124+
# Tested for what it is rather than for whether it exists, since a FIFO, a socket, and a device all exist and are none of the two.
1125+
unusable = [p for p in scan_paths if not (Path(p).is_file() or Path(p).is_dir())]
1126+
if unusable:
11261127
# Quoted, since a path holding a space or a comma is unreadable in a bare comma-joined list.
1127-
print(f"error: requested path(s) do not exist: {quoted(absent)}. Refusing rather than "
1128-
'falling back to the current directory, which would scan one tree and choose the '
1129-
'rule set from another.', file=sys.stderr)
1128+
print(f"error: requested path(s) are not a file or a directory: {quoted(unusable)}. "
1129+
'Refusing rather than falling back to the current directory, which would scan one '
1130+
'tree and choose the rule set from another.', file=sys.stderr)
11301131
return 2
11311132
git_roots = {found for found in (repo_root(Path(p)) for p in scan_paths) if found}
11321133
if len(git_roots) > 1:

scripts/test_prose_lint.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import contextlib
1313
import io
1414
import json
15+
import os
1516
import re
1617
import subprocess
1718
import sys
@@ -1866,7 +1867,20 @@ def test_a_path_that_does_not_exist_is_refused_rather_than_absorbed(self) -> Non
18661867
with mock.patch.object(prose_lint, 'repo_root', return_value=''):
18671868
self.assertEqual(2, prose_lint.main(
18681869
[str(self.tmp / 'no-such-dir'), '--check', 'home-path']))
1869-
self.assertIn('do not exist', self.err.getvalue())
1870+
self.assertIn('not a file or a directory', self.err.getvalue())
1871+
1872+
@unittest.skipUnless(hasattr(os, 'mkfifo'), 'mkfifo is POSIX only')
1873+
def test_a_path_that_exists_but_is_neither_a_file_nor_a_directory_is_refused(self) -> None:
1874+
"""A FIFO, a socket, and a device all exist, and `discover` reads each of them as `.`.
1875+
1876+
Testing for existence therefore left the hole open on everything that is not a typo.
1877+
"""
1878+
fifo = self.tmp / 'a-fifo'
1879+
os.mkfifo(fifo)
1880+
self.assertTrue(fifo.exists())
1881+
with mock.patch.object(prose_lint, 'repo_root', return_value=''):
1882+
self.assertEqual(2, prose_lint.main([str(fifo), '--check', 'home-path']))
1883+
self.assertIn('not a file or a directory', self.err.getvalue())
18701884

18711885
def test_a_path_holding_a_space_or_comma_is_quoted_in_the_refusal(self) -> None:
18721886
"""Joined bare, one path with a comma in it reads as two paths and the message misleads."""

0 commit comments

Comments
 (0)