readability-cli 0.10.0__tar.gz → 0.10.1__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.10.0 → readability_cli-0.10.1}/PKG-INFO +4 -3
- {readability_cli-0.10.0 → readability_cli-0.10.1}/README.md +3 -2
- {readability_cli-0.10.0 → readability_cli-0.10.1}/pyproject.toml +7 -1
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/checking.py +8 -10
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/cli.py +8 -9
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/configs/ruff.toml +8 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guide.py +5 -7
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/outline.py +19 -43
- {readability_cli-0.10.0 → readability_cli-0.10.1}/test_checking.py +19 -10
- {readability_cli-0.10.0 → readability_cli-0.10.1}/test_guide.py +9 -11
- {readability_cli-0.10.0 → readability_cli-0.10.1}/test_outline.py +16 -26
- {readability_cli-0.10.0 → readability_cli-0.10.1}/test_package.py +4 -2
- {readability_cli-0.10.0 → readability_cli-0.10.1}/uv.lock +1 -1
- {readability_cli-0.10.0 → readability_cli-0.10.1}/.github/workflows/ci.yml +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/.github/workflows/publish.yml +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/.github/workflows/update-guides.yml +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/.gitignore +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/.python-version +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/LICENSE +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/__init__.py +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/configs/biome-default.json +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/configs/pyrefly.toml +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/Rguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/cppguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/csharp-style.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/docguide-style.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/go-guide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/htmlcssguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/javaguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/jsguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/jsoncstyleguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/objcguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/pyguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/shellguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/tsguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/guides/vimscriptguide.md +0 -0
- {readability_cli-0.10.0 → readability_cli-0.10.1}/readability/tools.py +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: readability-cli
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
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
|
|
@@ -180,8 +180,9 @@ caller happened to be standing in.
|
|
|
180
180
|
|
|
181
181
|
Ruff and Pyrefly defaults follow the
|
|
182
182
|
[Google Python style guide](https://google.github.io/styleguide/pyguide.html):
|
|
183
|
-
80-column lines, Google docstrings, import
|
|
184
|
-
|
|
183
|
+
80-column lines, Google docstrings, one import per line sorted as isort's
|
|
184
|
+
`google` profile does, and full type checking. The Biome default applies the
|
|
185
|
+
80-column lines and two-space indentation of the
|
|
185
186
|
[Google JavaScript style guide](https://google.github.io/styleguide/jsguide.html)
|
|
186
187
|
and enables Biome's recommended lint rules. Project `biome.json` and
|
|
187
188
|
`biome.jsonc` files replace those bundled defaults for Biome-owned formats.
|
|
@@ -166,8 +166,9 @@ caller happened to be standing in.
|
|
|
166
166
|
|
|
167
167
|
Ruff and Pyrefly defaults follow the
|
|
168
168
|
[Google Python style guide](https://google.github.io/styleguide/pyguide.html):
|
|
169
|
-
80-column lines, Google docstrings, import
|
|
170
|
-
|
|
169
|
+
80-column lines, Google docstrings, one import per line sorted as isort's
|
|
170
|
+
`google` profile does, and full type checking. The Biome default applies the
|
|
171
|
+
80-column lines and two-space indentation of the
|
|
171
172
|
[Google JavaScript style guide](https://google.github.io/styleguide/jsguide.html)
|
|
172
173
|
and enables Biome's recommended lint rules. Project `biome.json` and
|
|
173
174
|
`biome.jsonc` files replace those bundled defaults for Biome-owned formats.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "readability-cli"
|
|
3
|
-
version = "0.10.
|
|
3
|
+
version = "0.10.1"
|
|
4
4
|
description = "A CLI to lint, format, and type-check code with Google-style defaults, and pull Google style guides in markdown format."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -47,6 +47,12 @@ ignore = ["PLR0911", "PLR0912", "PLR0913", "PLR0915", "PLR2004"]
|
|
|
47
47
|
[tool.ruff.lint.pydocstyle]
|
|
48
48
|
convention = "google"
|
|
49
49
|
|
|
50
|
+
[tool.ruff.lint.isort]
|
|
51
|
+
force-single-line = true
|
|
52
|
+
force-sort-within-sections = true
|
|
53
|
+
single-line-exclusions = ["typing"]
|
|
54
|
+
order-by-type = false
|
|
55
|
+
|
|
50
56
|
[tool.ruff.lint.per-file-ignores]
|
|
51
57
|
"test_*.py" = ["D"]
|
|
52
58
|
"*_test.py" = ["D"]
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
"""Run readability checks and report their coverage."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Sequence
|
|
3
4
|
import dataclasses
|
|
4
5
|
import logging
|
|
5
|
-
import subprocess
|
|
6
|
-
from collections.abc import Sequence
|
|
7
6
|
from pathlib import Path
|
|
7
|
+
import subprocess
|
|
8
8
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
|
-
from readability.tools import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
_tool_is_installed,
|
|
18
|
-
)
|
|
11
|
+
from readability.tools import _command_batches
|
|
12
|
+
from readability.tools import _get_tool_definitions
|
|
13
|
+
from readability.tools import _repository_root
|
|
14
|
+
from readability.tools import _should_run_tool
|
|
15
|
+
from readability.tools import _tool_is_installed
|
|
16
|
+
from readability.tools import ToolPlan
|
|
19
17
|
|
|
20
18
|
logger = logging.getLogger("readability")
|
|
21
19
|
DEFAULT_TIMEOUT = 60
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
"""Command-line interface for readability."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Sequence
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import sys
|
|
6
|
-
from collections.abc import Sequence
|
|
7
7
|
|
|
8
8
|
import click
|
|
9
9
|
|
|
10
10
|
from readability.checking import check_paths
|
|
11
|
-
from readability.guide import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
from readability.outline import _echo_outline, _select_section
|
|
11
|
+
from readability.guide import get_guide
|
|
12
|
+
from readability.guide import get_guides_dir
|
|
13
|
+
from readability.guide import get_local_path
|
|
14
|
+
from readability.guide import LANGUAGE_MAP
|
|
15
|
+
from readability.guide import refresh_guide
|
|
16
|
+
from readability.outline import _echo_outline
|
|
17
|
+
from readability.outline import _select_section
|
|
19
18
|
|
|
20
19
|
logger = logging.getLogger("readability")
|
|
21
20
|
|
|
@@ -14,6 +14,14 @@ ignore = ["PLR0911", "PLR0912", "PLR0913", "PLR0915", "PLR2004"]
|
|
|
14
14
|
[lint.pydocstyle]
|
|
15
15
|
convention = "google"
|
|
16
16
|
|
|
17
|
+
[lint.isort]
|
|
18
|
+
# Mirrors isort's google profile; its lexicographical and group-by-package
|
|
19
|
+
# options have no ruff equivalent
|
|
20
|
+
force-single-line = true
|
|
21
|
+
force-sort-within-sections = true
|
|
22
|
+
single-line-exclusions = ["typing"]
|
|
23
|
+
order-by-type = false
|
|
24
|
+
|
|
17
25
|
[lint.per-file-ignores]
|
|
18
26
|
# Docstring requirements are noise in test files
|
|
19
27
|
"test_*.py" = ["D"]
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""Fetch and cache Google style guides."""
|
|
2
2
|
|
|
3
|
+
from importlib.resources import files
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import warnings
|
|
6
|
-
from importlib.resources import files
|
|
7
7
|
|
|
8
|
+
from bs4 import BeautifulSoup
|
|
9
|
+
from bs4 import XMLParsedAsHTMLWarning
|
|
8
10
|
import click
|
|
9
|
-
import requests
|
|
10
|
-
from bs4 import BeautifulSoup, XMLParsedAsHTMLWarning
|
|
11
11
|
from markdownify import markdownify as md
|
|
12
|
+
import requests
|
|
12
13
|
|
|
13
14
|
warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
|
|
14
15
|
|
|
@@ -55,10 +56,7 @@ LANGUAGE_MAP = {
|
|
|
55
56
|
|
|
56
57
|
BASE_URL = "https://google.github.io/styleguide/"
|
|
57
58
|
|
|
58
|
-
#
|
|
59
|
-
# their underscores leaves 'from \_\_future\_\_ import' in the text a caller
|
|
60
|
-
# greps or reads. CommonMark would render a bare '__future__' as emphasis, but
|
|
61
|
-
# these guides are consumed as plain text, where fidelity matters more.
|
|
59
|
+
# Escaping underscores would leave 'from \_\_future\_\_ import' in the text
|
|
62
60
|
MARKDOWNIFY_OPTIONS = {"heading_style": "ATX", "escape_underscores": False}
|
|
63
61
|
|
|
64
62
|
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
"""Parse, search, and render style-guide outlines."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Iterator
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from dataclasses import dataclass
|
|
3
6
|
import re
|
|
4
7
|
import sys
|
|
5
|
-
from collections.abc import Iterator, Sequence
|
|
6
|
-
from dataclasses import dataclass
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
10
|
import click
|
|
10
11
|
|
|
11
|
-
#
|
|
12
|
-
# document structure. Guides for `#`-commented languages (shell, Python) would
|
|
13
|
-
# otherwise report hundreds of code comments as headings.
|
|
12
|
+
# Inside a fence is sample code, where a '#' comment is not a heading
|
|
14
13
|
FENCE_PATTERN = re.compile(r"^\s{0,3}(?:```|~~~)")
|
|
15
14
|
|
|
16
|
-
# Closing hashes are
|
|
17
|
-
# a title such as 'C#' keeps its trailing character.
|
|
15
|
+
# Closing hashes are space-separated, so a title like 'C#' keeps its own
|
|
18
16
|
HEADING_PATTERN = re.compile(r"^(#{1,6})\s+(.*?)(?:\s+#+)?\s*$")
|
|
19
17
|
|
|
20
|
-
# A
|
|
18
|
+
# A number the document prints itself, e.g. '2.2' in '2.2 Imports'
|
|
21
19
|
DOCUMENT_NUMBER_PATTERN = re.compile(r"^(\d+(?:\.\d+)*)\.?\s+(.+)$")
|
|
22
20
|
|
|
23
|
-
#
|
|
24
|
-
# otherwise trail the end of the preceding section.
|
|
21
|
+
# Link targets sit above their heading, not at the end of the one before
|
|
25
22
|
ANCHOR_PATTERN = re.compile(r'^<a id="[^"]*"></a>$')
|
|
26
23
|
|
|
27
|
-
#
|
|
28
|
-
# both sides are required so that a heading like '`Array<T>` Type' stays whole.
|
|
24
|
+
# Scoped reference separator; spaces required so '`Array<T>` Type' stays whole
|
|
29
25
|
PATH_SEPARATOR_PATTERN = re.compile(r"\s+>\s+")
|
|
30
26
|
|
|
31
27
|
|
|
@@ -148,8 +144,7 @@ def parse_headings(content: str) -> list[Heading]:
|
|
|
148
144
|
if not raw:
|
|
149
145
|
return []
|
|
150
146
|
|
|
151
|
-
#
|
|
152
|
-
# ('2.2') is the reliable signal; a bare leading digit is not.
|
|
147
|
+
# A dotted number is the reliable signal; a bare leading digit is not
|
|
153
148
|
document_numbers = [
|
|
154
149
|
match.group(1)
|
|
155
150
|
for _, _, text in raw
|
|
@@ -157,8 +152,7 @@ def parse_headings(content: str) -> list[Heading]:
|
|
|
157
152
|
]
|
|
158
153
|
numbered = any("." in number for number in document_numbers)
|
|
159
154
|
|
|
160
|
-
# A lone top-level heading is the
|
|
161
|
-
# than being the first section, so numbering starts with its children.
|
|
155
|
+
# A lone top-level heading is the title, so numbering starts below it
|
|
162
156
|
top_level = min(level for _, level, _ in raw)
|
|
163
157
|
top_level_lines = [line for line, level, _ in raw if level == top_level]
|
|
164
158
|
title_line = top_level_lines[0] if len(top_level_lines) == 1 else None
|
|
@@ -169,12 +163,7 @@ def parse_headings(content: str) -> list[Heading]:
|
|
|
169
163
|
index = "" if line == title_line else _next_index(open_headings, level)
|
|
170
164
|
number, title = _split_document_number(text, numbered)
|
|
171
165
|
|
|
172
|
-
# A guide
|
|
173
|
-
# are called, so its numbers address them. A positional index would
|
|
174
|
-
# drift wherever the guide skips one — pyguide has no 2.15 at all,
|
|
175
|
-
# and calling its 2.16 by that name would cite a section that does
|
|
176
|
-
# not exist. The tree still advances above, so headings the guide
|
|
177
|
-
# leaves unnumbered keep a positional index to be reachable by.
|
|
166
|
+
# A guide's own numbers address its sections; an index would drift
|
|
178
167
|
if number:
|
|
179
168
|
index = number
|
|
180
169
|
headings.append(
|
|
@@ -203,10 +192,7 @@ def _slugify(text: str) -> str:
|
|
|
203
192
|
return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
|
|
204
193
|
|
|
205
194
|
|
|
206
|
-
#
|
|
207
|
-
# over the shipped corpus, it marks the 4% that are expensive while leaving
|
|
208
|
-
# the rest unannotated: a size on every line would read '0' on 59% of them,
|
|
209
|
-
# which is noise in the one output whose job is to be scanned quickly.
|
|
195
|
+
# Marks the expensive 4% of the corpus; a size on every line would be noise
|
|
210
196
|
LARGE_SECTION_WORDS = 1200
|
|
211
197
|
|
|
212
198
|
|
|
@@ -263,8 +249,7 @@ def format_outline(headings: Sequence[Heading], content: str = "") -> str:
|
|
|
263
249
|
section = extract_section(content, headings, position)
|
|
264
250
|
size = _format_size(len(section.split()))
|
|
265
251
|
|
|
266
|
-
# The index already carries the guide's
|
|
267
|
-
# so printing the heading verbatim would show it twice.
|
|
252
|
+
# The index already carries the guide's number where it has one
|
|
268
253
|
lines.append(f"{indent}{prefix}{heading.title}{size}")
|
|
269
254
|
|
|
270
255
|
return "\n".join(lines)
|
|
@@ -281,8 +266,7 @@ def _matches_component(heading: Heading, component: str, exact: bool) -> bool:
|
|
|
281
266
|
Returns:
|
|
282
267
|
True if the component identifies the heading.
|
|
283
268
|
"""
|
|
284
|
-
# Numbers
|
|
285
|
-
# match unrelated sections.
|
|
269
|
+
# Numbers compare in full; a substring would match unrelated sections
|
|
286
270
|
wanted = component.strip().rstrip(".")
|
|
287
271
|
if wanted and wanted in {heading.index, heading.number}:
|
|
288
272
|
return True
|
|
@@ -369,8 +353,7 @@ def find_headings(headings: Sequence[Heading], reference: str) -> list[int]:
|
|
|
369
353
|
if not components:
|
|
370
354
|
return []
|
|
371
355
|
|
|
372
|
-
#
|
|
373
|
-
# in full, so 'Imports' does not also select 'Imports and Exports'.
|
|
356
|
+
# Whole matches first, so 'Imports' does not select 'Imports and Exports'
|
|
374
357
|
for exact in (True, False):
|
|
375
358
|
matches = [
|
|
376
359
|
position
|
|
@@ -512,9 +495,7 @@ def find_mentions(
|
|
|
512
495
|
|
|
513
496
|
lines = content.splitlines()
|
|
514
497
|
|
|
515
|
-
#
|
|
516
|
-
# because it is the whole guide rather than a section within it, so
|
|
517
|
-
# crediting the preamble to it would suggest reading everything.
|
|
498
|
+
# The title has no index, so crediting it would suggest reading everything
|
|
518
499
|
sections = [
|
|
519
500
|
(position, _section_bounds(lines, headings, position))
|
|
520
501
|
for position in range(len(headings))
|
|
@@ -526,10 +507,7 @@ def find_mentions(
|
|
|
526
507
|
if needle not in line.lower():
|
|
527
508
|
continue
|
|
528
509
|
|
|
529
|
-
# Bounds nest, so the last
|
|
530
|
-
# innermost one that prints it. A section sheds the anchors of the
|
|
531
|
-
# heading below it, and those fall to an ancestor rather than to the
|
|
532
|
-
# section they merely trail.
|
|
510
|
+
# Bounds nest, so the last one covering a line is what prints it
|
|
533
511
|
holders = [
|
|
534
512
|
position
|
|
535
513
|
for position, (start, end) in sections
|
|
@@ -577,8 +555,7 @@ def _echo_outline(content: str, language: str) -> None:
|
|
|
577
555
|
example = _example_reference(headings)
|
|
578
556
|
if example:
|
|
579
557
|
count = sum(1 for heading in headings if heading.index)
|
|
580
|
-
# stdout is block-buffered
|
|
581
|
-
# lands above the outline it is meant to follow
|
|
558
|
+
# Redirected stdout is block-buffered, so the hint would land first
|
|
582
559
|
sys.stdout.flush()
|
|
583
560
|
click.echo(
|
|
584
561
|
f"# {count} sections · print one: "
|
|
@@ -656,8 +633,7 @@ def _select_section(content: str, reference: str, language: str) -> str:
|
|
|
656
633
|
if not matches:
|
|
657
634
|
_report_no_heading(content, headings, reference, language)
|
|
658
635
|
|
|
659
|
-
#
|
|
660
|
-
# guides repeat headings ('Decision' appears under every Python rule).
|
|
636
|
+
# Guides repeat headings, so name every candidate rather than guess
|
|
661
637
|
if len(matches) > 1:
|
|
662
638
|
click.echo(
|
|
663
639
|
f"Error: '{reference}' matches {len(matches)} headings in the "
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
import subprocess
|
|
3
4
|
import tomllib
|
|
4
|
-
from
|
|
5
|
-
from unittest.mock import MagicMock
|
|
5
|
+
from unittest.mock import call
|
|
6
|
+
from unittest.mock import MagicMock
|
|
7
|
+
from unittest.mock import patch
|
|
6
8
|
|
|
7
|
-
import pytest
|
|
8
9
|
from click.testing import CliRunner
|
|
10
|
+
import pytest
|
|
9
11
|
|
|
10
|
-
from readability.checking import
|
|
12
|
+
from readability.checking import check_paths
|
|
13
|
+
from readability.checking import CheckReport
|
|
11
14
|
from readability.cli import cli
|
|
12
|
-
from readability.tools import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
_has_project_config,
|
|
17
|
-
)
|
|
15
|
+
from readability.tools import _bundled_config
|
|
16
|
+
from readability.tools import _get_tool_definitions
|
|
17
|
+
from readability.tools import _has_project_config
|
|
18
|
+
from readability.tools import TOOL_RUNNERS
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def _source_file(directory: Path, name: str) -> Path:
|
|
@@ -1035,6 +1036,14 @@ def test_bundled_default_configs_are_valid() -> None:
|
|
|
1035
1036
|
assert ruff_config["line-length"] == 80
|
|
1036
1037
|
assert ruff_config["lint"]["pydocstyle"]["convention"] == "google"
|
|
1037
1038
|
|
|
1039
|
+
# Import sorting mirrors isort's google profile
|
|
1040
|
+
assert ruff_config["lint"]["isort"] == {
|
|
1041
|
+
"force-single-line": True,
|
|
1042
|
+
"force-sort-within-sections": True,
|
|
1043
|
+
"single-line-exclusions": ["typing"],
|
|
1044
|
+
"order-by-type": False,
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1038
1047
|
|
|
1039
1048
|
@patch("readability.checking._check_path")
|
|
1040
1049
|
def test_check_paths_aggregates_str_and_path_inputs(
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from unittest.mock import MagicMock
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
from unittest.mock import patch
|
|
4
5
|
|
|
5
6
|
import click
|
|
7
|
+
from click.testing import CliRunner
|
|
6
8
|
import pytest
|
|
7
9
|
import requests
|
|
8
|
-
from click.testing import CliRunner
|
|
9
10
|
|
|
10
11
|
from readability.cli import cli
|
|
11
|
-
from readability.guide import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
get_local_path,
|
|
17
|
-
)
|
|
12
|
+
from readability.guide import convert_to_markdown
|
|
13
|
+
from readability.guide import get_guide
|
|
14
|
+
from readability.guide import get_guide_content
|
|
15
|
+
from readability.guide import get_guides_dir
|
|
16
|
+
from readability.guide import get_local_path
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
def test_get_guide_unsupported() -> None:
|
|
@@ -159,8 +158,7 @@ def test_sync_command(
|
|
|
159
158
|
result = runner.invoke(cli, ["sync"])
|
|
160
159
|
|
|
161
160
|
assert result.exit_code == 0
|
|
162
|
-
#
|
|
163
|
-
# regardless of log level rather than logged as narration.
|
|
161
|
+
# An asked-for outcome, so reported regardless of log level
|
|
164
162
|
assert "Sync complete" in result.stderr
|
|
165
163
|
# Check if at least one guide was "synced" (written to tmp_path)
|
|
166
164
|
assert len(os.listdir(tmp_path)) > 0
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from unittest.mock import MagicMock
|
|
4
|
+
from unittest.mock import MagicMock
|
|
5
|
+
from unittest.mock import patch
|
|
5
6
|
|
|
6
7
|
from click.testing import CliRunner
|
|
7
8
|
|
|
8
|
-
from readability.cli import cli
|
|
9
|
-
from readability.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from readability.outline import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
find_mentions,
|
|
19
|
-
parse_headings,
|
|
20
|
-
)
|
|
9
|
+
from readability.cli import cli
|
|
10
|
+
from readability.cli import main
|
|
11
|
+
from readability.guide import get_local_path
|
|
12
|
+
from readability.guide import LANGUAGE_MAP
|
|
13
|
+
from readability.outline import _iter_heading_lines
|
|
14
|
+
from readability.outline import _unique_reference
|
|
15
|
+
from readability.outline import extract_section
|
|
16
|
+
from readability.outline import find_headings
|
|
17
|
+
from readability.outline import find_mentions
|
|
18
|
+
from readability.outline import parse_headings
|
|
21
19
|
|
|
22
20
|
NUMBERED_GUIDE = """# Sample Style Guide
|
|
23
21
|
|
|
@@ -456,10 +454,7 @@ def test_cli_section_heading_containing_an_angle_bracket(
|
|
|
456
454
|
assert "Array body." in result.stdout
|
|
457
455
|
|
|
458
456
|
|
|
459
|
-
#
|
|
460
|
-
# _iter_heading_lines). These tests are the evidence for that decision: the
|
|
461
|
-
# scope it covers, the constructs it rejects, and its behaviour over every
|
|
462
|
-
# guide actually shipped. A failure here means the scope has been outgrown.
|
|
457
|
+
# Evidence the scanner need not be a Markdown parser; a failure means it is
|
|
463
458
|
|
|
464
459
|
|
|
465
460
|
def test_heading_scanner_rejects_non_headings() -> None:
|
|
@@ -551,8 +546,7 @@ def test_every_shipped_guide_parses_into_unique_sections() -> None:
|
|
|
551
546
|
f"{language}: duplicate positional indices"
|
|
552
547
|
)
|
|
553
548
|
|
|
554
|
-
# A
|
|
555
|
-
# or a shebang, neither of which appears in a real guide heading.
|
|
549
|
+
# A fenced line would carry a comment marker or a shebang
|
|
556
550
|
for heading in headings:
|
|
557
551
|
assert not heading.title.startswith("!"), (
|
|
558
552
|
f"{language}: shebang parsed as heading: {heading.title!r}"
|
|
@@ -575,9 +569,7 @@ def test_every_shipped_guide_heading_is_addressable() -> None:
|
|
|
575
569
|
)
|
|
576
570
|
assert titles[0] is headings[0], f"{language}: title is not first"
|
|
577
571
|
|
|
578
|
-
#
|
|
579
|
-
# numbers those drift from the tree, so one string can name two
|
|
580
|
-
# headings. What must always hold is that some reference resolves.
|
|
572
|
+
# A guide's own numbers drift, so only 'some reference resolves' holds
|
|
581
573
|
for position, heading in enumerate(headings):
|
|
582
574
|
reference = _unique_reference(headings, position).strip('"')
|
|
583
575
|
matches = find_headings(headings, reference)
|
|
@@ -587,9 +579,7 @@ def test_every_shipped_guide_heading_is_addressable() -> None:
|
|
|
587
579
|
)
|
|
588
580
|
|
|
589
581
|
|
|
590
|
-
#
|
|
591
|
-
# 'truefalse' appears only inside pyguide's anchors, which is the shape that
|
|
592
|
-
# used to be credited to whichever section happened to precede them.
|
|
582
|
+
# Common words, plus 'truefalse' which appears only inside pyguide's anchors
|
|
593
583
|
MENTION_PROBES = (
|
|
594
584
|
"truefalse",
|
|
595
585
|
"indent",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from importlib.resources import files
|
|
2
2
|
|
|
3
3
|
import readability
|
|
4
|
-
from readability.checking import
|
|
5
|
-
from readability.
|
|
4
|
+
from readability.checking import check_paths
|
|
5
|
+
from readability.checking import CheckReport
|
|
6
|
+
from readability.cli import cli
|
|
7
|
+
from readability.cli import main
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def test_public_modules_are_importable() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""Build executable plans for readability's canonical tools."""
|
|
2
2
|
|
|
3
|
-
import logging
|
|
4
|
-
import os
|
|
5
|
-
import shutil
|
|
6
|
-
import tomllib
|
|
7
3
|
from collections.abc import Sequence
|
|
8
4
|
from dataclasses import dataclass
|
|
9
5
|
from importlib.resources import files
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
10
8
|
from pathlib import Path
|
|
9
|
+
import shutil
|
|
10
|
+
import tomllib
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger("readability")
|
|
13
13
|
|