readability-cli 0.9.0__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {readability_cli-0.9.0 → readability_cli-0.10.0}/PKG-INFO +35 -3
- {readability_cli-0.9.0 → readability_cli-0.10.0}/README.md +34 -2
- {readability_cli-0.9.0 → readability_cli-0.10.0}/pyproject.toml +1 -1
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/checking.py +35 -20
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/cli.py +8 -21
- readability_cli-0.10.0/readability/tools.py +560 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/test_checking.py +655 -37
- readability_cli-0.9.0/readability/tools.py +0 -407
- {readability_cli-0.9.0 → readability_cli-0.10.0}/.github/workflows/ci.yml +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/.github/workflows/publish.yml +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/.github/workflows/update-guides.yml +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/.gitignore +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/.python-version +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/LICENSE +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/__init__.py +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/configs/biome-default.json +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/configs/pyrefly.toml +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/configs/ruff.toml +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guide.py +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/Rguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/cppguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/csharp-style.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/docguide-style.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/go-guide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/htmlcssguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/javaguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/jsguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/jsoncstyleguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/objcguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/pyguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/shellguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/tsguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/guides/vimscriptguide.md +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/readability/outline.py +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/test_guide.py +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/test_outline.py +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/test_package.py +0 -0
- {readability_cli-0.9.0 → readability_cli-0.10.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: readability-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: A CLI to lint, format, and type-check code with Google-style defaults, and pull Google style guides in markdown format.
|
|
5
5
|
Project-URL: Homepage, https://github.com/owahltinez/readability
|
|
6
6
|
Project-URL: Repository, https://github.com/owahltinez/readability
|
|
@@ -168,8 +168,13 @@ The returned `CheckReport` records only whether findings occurred and which
|
|
|
168
168
|
tools ran, were skipped, or failed. Detailed tool findings are still written as
|
|
169
169
|
each tool runs. Every path is validated before any tool runs, and a missing one
|
|
170
170
|
raises `FileNotFoundError` rather than being misreported as a finding. Relative
|
|
171
|
-
paths remain relative to the process working directory
|
|
172
|
-
configuration discovery
|
|
171
|
+
paths remain relative to the process working directory. `project_root` bounds
|
|
172
|
+
configuration discovery, locates project-local tool installs, and roots
|
|
173
|
+
ignore-file discovery. Because it bounds discovery, a caller that passes one
|
|
174
|
+
gets the same verdict for the same files wherever they sit, which is what makes
|
|
175
|
+
the result usable as a baseline. It defaults to the repository the process is
|
|
176
|
+
in, so the command agrees with the tools rather than with the directory the
|
|
177
|
+
caller happened to be standing in.
|
|
173
178
|
|
|
174
179
|
### Configuring Formats
|
|
175
180
|
|
|
@@ -189,6 +194,33 @@ into it through that tool's native configuration. Gofmt has no project settings.
|
|
|
189
194
|
The bundled Biome file requires Biome 2.5 or later, matching the fallback
|
|
190
195
|
runner's version floor.
|
|
191
196
|
|
|
197
|
+
Configuration is found per file, in the file's own directory and then its
|
|
198
|
+
ancestors, which is where each canonical tool looks. A package keeping its
|
|
199
|
+
settings in a subdirectory is checked against them however it is reached:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Uses web/biome.json, not the bundled default
|
|
203
|
+
readability check web/app.ts
|
|
204
|
+
|
|
205
|
+
# Uses the repository root's [tool.ruff], not the bundled default
|
|
206
|
+
cd pkg && readability check module.py
|
|
207
|
+
|
|
208
|
+
# pkg/ is checked against pkg/pyproject.toml, the rest against the defaults
|
|
209
|
+
readability check .
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A tool is only told which configuration to use when the file has none, because
|
|
213
|
+
each one resolves its own hierarchy: a single Ruff invocation over two packages
|
|
214
|
+
declaring different line lengths reports each against its own. So a mixed tree
|
|
215
|
+
needs no choosing between configurations, and the bundled default never
|
|
216
|
+
overrides one the project declared.
|
|
217
|
+
|
|
218
|
+
Discovery is per tool, so configuring one leaves the others on their bundled
|
|
219
|
+
defaults. It is also bounded, by the repository the command runs in, or by
|
|
220
|
+
`project_root` when calling `check_paths` directly. Configuration outside that
|
|
221
|
+
boundary is not read, so the same paths get the same answer wherever the
|
|
222
|
+
repository sits.
|
|
223
|
+
|
|
192
224
|
## Style Guides
|
|
193
225
|
|
|
194
226
|
The `guide` command reads a Google style guide, using the local cache when
|
|
@@ -154,8 +154,13 @@ The returned `CheckReport` records only whether findings occurred and which
|
|
|
154
154
|
tools ran, were skipped, or failed. Detailed tool findings are still written as
|
|
155
155
|
each tool runs. Every path is validated before any tool runs, and a missing one
|
|
156
156
|
raises `FileNotFoundError` rather than being misreported as a finding. Relative
|
|
157
|
-
paths remain relative to the process working directory
|
|
158
|
-
configuration discovery
|
|
157
|
+
paths remain relative to the process working directory. `project_root` bounds
|
|
158
|
+
configuration discovery, locates project-local tool installs, and roots
|
|
159
|
+
ignore-file discovery. Because it bounds discovery, a caller that passes one
|
|
160
|
+
gets the same verdict for the same files wherever they sit, which is what makes
|
|
161
|
+
the result usable as a baseline. It defaults to the repository the process is
|
|
162
|
+
in, so the command agrees with the tools rather than with the directory the
|
|
163
|
+
caller happened to be standing in.
|
|
159
164
|
|
|
160
165
|
### Configuring Formats
|
|
161
166
|
|
|
@@ -175,6 +180,33 @@ into it through that tool's native configuration. Gofmt has no project settings.
|
|
|
175
180
|
The bundled Biome file requires Biome 2.5 or later, matching the fallback
|
|
176
181
|
runner's version floor.
|
|
177
182
|
|
|
183
|
+
Configuration is found per file, in the file's own directory and then its
|
|
184
|
+
ancestors, which is where each canonical tool looks. A package keeping its
|
|
185
|
+
settings in a subdirectory is checked against them however it is reached:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# Uses web/biome.json, not the bundled default
|
|
189
|
+
readability check web/app.ts
|
|
190
|
+
|
|
191
|
+
# Uses the repository root's [tool.ruff], not the bundled default
|
|
192
|
+
cd pkg && readability check module.py
|
|
193
|
+
|
|
194
|
+
# pkg/ is checked against pkg/pyproject.toml, the rest against the defaults
|
|
195
|
+
readability check .
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
A tool is only told which configuration to use when the file has none, because
|
|
199
|
+
each one resolves its own hierarchy: a single Ruff invocation over two packages
|
|
200
|
+
declaring different line lengths reports each against its own. So a mixed tree
|
|
201
|
+
needs no choosing between configurations, and the bundled default never
|
|
202
|
+
overrides one the project declared.
|
|
203
|
+
|
|
204
|
+
Discovery is per tool, so configuring one leaves the others on their bundled
|
|
205
|
+
defaults. It is also bounded, by the repository the command runs in, or by
|
|
206
|
+
`project_root` when calling `check_paths` directly. Configuration outside that
|
|
207
|
+
boundary is not read, so the same paths get the same answer wherever the
|
|
208
|
+
repository sits.
|
|
209
|
+
|
|
178
210
|
## Style Guides
|
|
179
211
|
|
|
180
212
|
The `guide` command reads a Google style guide, using the local cache when
|
|
@@ -12,6 +12,7 @@ from readability.tools import (
|
|
|
12
12
|
ToolPlan,
|
|
13
13
|
_command_batches,
|
|
14
14
|
_get_tool_definitions,
|
|
15
|
+
_repository_root,
|
|
15
16
|
_should_run_tool,
|
|
16
17
|
_tool_is_installed,
|
|
17
18
|
)
|
|
@@ -74,8 +75,10 @@ def check_paths(
|
|
|
74
75
|
Args:
|
|
75
76
|
paths: Files or directories to check, as strings or paths. Relative
|
|
76
77
|
paths are interpreted from the current working directory.
|
|
77
|
-
project_root:
|
|
78
|
-
|
|
78
|
+
project_root: Bounds config discovery, so the same files get the
|
|
79
|
+
same verdict wherever the tree sits, and locates tool installs
|
|
80
|
+
and ignore files. Defaults to the repository the process is in.
|
|
81
|
+
It does not rebase paths.
|
|
79
82
|
fix: Whether to apply automatic fixes.
|
|
80
83
|
|
|
81
84
|
Returns:
|
|
@@ -85,7 +88,11 @@ def check_paths(
|
|
|
85
88
|
FileNotFoundError: If any requested path does not exist. Every path is
|
|
86
89
|
validated before any tools run.
|
|
87
90
|
"""
|
|
88
|
-
|
|
91
|
+
here = Path.cwd()
|
|
92
|
+
repository = _repository_root(here)
|
|
93
|
+
root = project_root if project_root is not None else (repository or here)
|
|
94
|
+
# A named root bounds the search; otherwise each path's repository does
|
|
95
|
+
boundary = project_root
|
|
89
96
|
requested_paths = [Path(path) for path in paths]
|
|
90
97
|
missing_path = next(
|
|
91
98
|
(path for path in requested_paths if not path.exists()), None
|
|
@@ -95,7 +102,7 @@ def check_paths(
|
|
|
95
102
|
|
|
96
103
|
report = CheckReport()
|
|
97
104
|
for path in requested_paths:
|
|
98
|
-
path_report = _check_path(path, root, fix=fix)
|
|
105
|
+
path_report = _check_path(path, root, boundary, fix=fix)
|
|
99
106
|
if not path_report.ran:
|
|
100
107
|
path_report.unverified_paths.append(path)
|
|
101
108
|
report.absorb(path_report)
|
|
@@ -103,13 +110,18 @@ def check_paths(
|
|
|
103
110
|
|
|
104
111
|
|
|
105
112
|
def _check_path(
|
|
106
|
-
path: Path,
|
|
113
|
+
path: Path,
|
|
114
|
+
project_root: Path,
|
|
115
|
+
boundary: Path | None = None,
|
|
116
|
+
fix: bool = False,
|
|
107
117
|
) -> CheckReport:
|
|
108
118
|
"""Apply relevant tools to a single path.
|
|
109
119
|
|
|
110
120
|
Args:
|
|
111
121
|
path: The path (file or directory) to check.
|
|
112
|
-
project_root:
|
|
122
|
+
project_root: Locates tool installs and ignore files.
|
|
123
|
+
boundary: Outermost directory config discovery may consider, or
|
|
124
|
+
None to bound it by the path's own repository.
|
|
113
125
|
fix: Whether to apply automatic fixes.
|
|
114
126
|
|
|
115
127
|
Returns:
|
|
@@ -117,14 +129,16 @@ def _check_path(
|
|
|
117
129
|
"""
|
|
118
130
|
logger.info("Checking path: %s", path)
|
|
119
131
|
|
|
132
|
+
if boundary is None:
|
|
133
|
+
boundary = _repository_root(path) or Path(path.resolve().anchor)
|
|
134
|
+
|
|
120
135
|
# Iterate through all supported tool definitions
|
|
121
136
|
report = CheckReport()
|
|
122
|
-
for tool in _get_tool_definitions(path, project_root):
|
|
137
|
+
for tool in _get_tool_definitions(path, project_root, boundary):
|
|
123
138
|
if not _should_run_tool(tool, path):
|
|
124
139
|
continue
|
|
125
140
|
|
|
126
|
-
# A
|
|
127
|
-
# which is not the same as a clean result
|
|
141
|
+
# A wanted but absent tool is a hole in coverage, not a clean result
|
|
128
142
|
if not _tool_is_installed(tool):
|
|
129
143
|
report.skipped.add(tool.name)
|
|
130
144
|
continue
|
|
@@ -156,10 +170,7 @@ def _run_tool(
|
|
|
156
170
|
target_count = len(tool.targets or ())
|
|
157
171
|
try:
|
|
158
172
|
if fix:
|
|
159
|
-
#
|
|
160
|
-
# exit non-zero when something is left over, which is a finding
|
|
161
|
-
# rather than a failure, so the check below still gets to run
|
|
162
|
-
# and report what they could not deal with.
|
|
173
|
+
# Leftovers exit non-zero, which is a finding rather than a failure
|
|
163
174
|
for phase in ("format", "fix"):
|
|
164
175
|
configured_command = getattr(tool, phase)
|
|
165
176
|
if configured_command:
|
|
@@ -208,8 +219,7 @@ def _run_tool(
|
|
|
208
219
|
f"{result.stdout}\n{result.stderr}"
|
|
209
220
|
)
|
|
210
221
|
|
|
211
|
-
#
|
|
212
|
-
# nothing. Whatever it managed before that stays in ran.
|
|
222
|
+
# Never starting, or timing out, means this tool checked nothing
|
|
213
223
|
except (subprocess.SubprocessError, OSError) as e:
|
|
214
224
|
logger.warning("Could not run %s: %s", tool.name, e)
|
|
215
225
|
report.failed.add(tool.name)
|
|
@@ -227,14 +237,19 @@ def _tool_checked_files(
|
|
|
227
237
|
result: The completed subprocess.
|
|
228
238
|
|
|
229
239
|
Returns:
|
|
230
|
-
False when
|
|
240
|
+
False when the tool explicitly reports that it processed no files.
|
|
231
241
|
"""
|
|
232
|
-
|
|
242
|
+
# Biome says this for an unmatched target, Ruff when excludes cover them all
|
|
243
|
+
zero_file_reports = {
|
|
244
|
+
"biome": ("Checked 0 files", "Formatted 0 files"),
|
|
245
|
+
"ruff": ("No Python files found",),
|
|
246
|
+
}
|
|
247
|
+
summaries = zero_file_reports.get(tool_name)
|
|
248
|
+
if summaries is None:
|
|
233
249
|
return True
|
|
250
|
+
|
|
234
251
|
output = f"{result.stdout or ''}\n{result.stderr or ''}"
|
|
235
|
-
|
|
236
|
-
zero_file_summaries = ("Checked 0 files", "Formatted 0 files")
|
|
237
|
-
return not any(summary in output for summary in zero_file_summaries)
|
|
252
|
+
return not any(summary in output for summary in summaries)
|
|
238
253
|
|
|
239
254
|
|
|
240
255
|
def _capture_tool_command(
|
|
@@ -60,8 +60,7 @@ def guide(
|
|
|
60
60
|
_echo_languages()
|
|
61
61
|
return
|
|
62
62
|
|
|
63
|
-
# Refusing beats
|
|
64
|
-
# caller ends up reading the wrong thing without being told.
|
|
63
|
+
# Refusing beats a silent precedence rule the caller cannot see
|
|
65
64
|
if full and reference:
|
|
66
65
|
raise click.UsageError("--full takes the whole guide, so REF cannot.")
|
|
67
66
|
|
|
@@ -106,8 +105,7 @@ def _resolve_filenames(languages: Sequence[str]) -> list[str]:
|
|
|
106
105
|
f"Language '{language}' is not supported. Supported "
|
|
107
106
|
f"languages: {', '.join(sorted(LANGUAGE_MAP.keys()))}"
|
|
108
107
|
)
|
|
109
|
-
# Aliases
|
|
110
|
-
# fetched once however many of its names were given
|
|
108
|
+
# Aliases share a guide, fetched once however many names were given
|
|
111
109
|
if filename not in filenames:
|
|
112
110
|
filenames.append(filename)
|
|
113
111
|
|
|
@@ -190,8 +188,7 @@ def check(paths: Sequence[str], fix: bool, verbose: bool) -> None:
|
|
|
190
188
|
err=True,
|
|
191
189
|
)
|
|
192
190
|
|
|
193
|
-
# A tool that
|
|
194
|
-
# so its silence is not a pass however far the rest of the run got
|
|
191
|
+
# A tool that never ran checked nothing, so its silence is not a pass
|
|
195
192
|
if report.failed:
|
|
196
193
|
click.echo(
|
|
197
194
|
f"Error: Could not run: {', '.join(sorted(report.failed))}. "
|
|
@@ -200,14 +197,11 @@ def check(paths: Sequence[str], fix: bool, verbose: bool) -> None:
|
|
|
200
197
|
)
|
|
201
198
|
sys.exit(1)
|
|
202
199
|
|
|
203
|
-
# Findings
|
|
204
|
-
# not process any files (for example, a Biome configuration error).
|
|
200
|
+
# Findings fail the check even if the tool processed no files
|
|
205
201
|
if report.findings:
|
|
206
202
|
sys.exit(1)
|
|
207
203
|
|
|
208
|
-
# Having run nothing is not a pass
|
|
209
|
-
# command became a silent no-op wherever its tools were absent, gating
|
|
210
|
-
# nothing while every caller read the exit code as approval.
|
|
204
|
+
# Having run nothing is not a pass, it is a silent no-op gating nothing
|
|
211
205
|
if not report.ran and report.skipped:
|
|
212
206
|
click.echo(
|
|
213
207
|
f"Error: Every tool for {len(paths)} path(s) is missing, so "
|
|
@@ -216,9 +210,7 @@ def check(paths: Sequence[str], fix: bool, verbose: bool) -> None:
|
|
|
216
210
|
)
|
|
217
211
|
sys.exit(1)
|
|
218
212
|
|
|
219
|
-
# No tool applying is a fact
|
|
220
|
-
# is nothing to install and nothing to fix. It still cannot be reported
|
|
221
|
-
# as a clean result, because nothing was inspected.
|
|
213
|
+
# No tool applying is a fact, not a fault, but still not a clean result
|
|
222
214
|
if not report.ran:
|
|
223
215
|
click.echo(
|
|
224
216
|
f"No tool applies to {len(paths)} path(s); nothing was checked.",
|
|
@@ -235,8 +227,7 @@ def check(paths: Sequence[str], fix: bool, verbose: bool) -> None:
|
|
|
235
227
|
err=True,
|
|
236
228
|
)
|
|
237
229
|
|
|
238
|
-
#
|
|
239
|
-
# nothing at all and left the caller unable to tell it from a no-op.
|
|
230
|
+
# Without this a clean run printed nothing and read as a no-op
|
|
240
231
|
click.echo(
|
|
241
232
|
f"No findings in {checked_path_count} path(s) "
|
|
242
233
|
f"({', '.join(sorted(report.ran))}).",
|
|
@@ -247,11 +238,7 @@ def check(paths: Sequence[str], fix: bool, verbose: bool) -> None:
|
|
|
247
238
|
# Main entry point for the CLI
|
|
248
239
|
def main() -> None:
|
|
249
240
|
"""Main entry point for the CLI."""
|
|
250
|
-
#
|
|
251
|
-
# module as a library (e.g. from lemming) has no side effects
|
|
252
|
-
# WARNING, not INFO: everything logged below it narrates progress, which
|
|
253
|
-
# is what --verbose is for. Leaving it on meant there was no quiet mode
|
|
254
|
-
# and the flag could only add DEBUG on top.
|
|
241
|
+
# Configured here, not at import, so library use has no side effects
|
|
255
242
|
logging.basicConfig(
|
|
256
243
|
level=logging.WARNING,
|
|
257
244
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|