readability-cli 0.10.3__tar.gz → 0.10.5__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.
Files changed (38) hide show
  1. {readability_cli-0.10.3 → readability_cli-0.10.5}/PKG-INFO +12 -7
  2. {readability_cli-0.10.3 → readability_cli-0.10.5}/README.md +7 -3
  3. readability_cli-0.10.5/biome.json +5 -0
  4. {readability_cli-0.10.3 → readability_cli-0.10.5}/pyproject.toml +9 -2
  5. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/cli.py +8 -1
  6. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/configs/biome-default.json +1 -1
  7. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guide.py +38 -23
  8. {readability_cli-0.10.3 → readability_cli-0.10.5}/test_checking.py +1 -1
  9. {readability_cli-0.10.3 → readability_cli-0.10.5}/test_outline.py +16 -0
  10. {readability_cli-0.10.3 → readability_cli-0.10.5}/uv.lock +17 -6
  11. {readability_cli-0.10.3 → readability_cli-0.10.5}/.github/workflows/ci.yml +0 -0
  12. {readability_cli-0.10.3 → readability_cli-0.10.5}/.github/workflows/publish.yml +0 -0
  13. {readability_cli-0.10.3 → readability_cli-0.10.5}/.github/workflows/update-guides.yml +0 -0
  14. {readability_cli-0.10.3 → readability_cli-0.10.5}/.gitignore +0 -0
  15. {readability_cli-0.10.3 → readability_cli-0.10.5}/.python-version +0 -0
  16. {readability_cli-0.10.3 → readability_cli-0.10.5}/LICENSE +0 -0
  17. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/__init__.py +0 -0
  18. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/checking.py +0 -0
  19. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/configs/pyrefly.toml +0 -0
  20. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/configs/ruff.toml +0 -0
  21. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/Rguide.md +0 -0
  22. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/cppguide.md +0 -0
  23. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/csharp-style.md +0 -0
  24. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/docguide-style.md +0 -0
  25. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/go-guide.md +0 -0
  26. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/htmlcssguide.md +0 -0
  27. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/javaguide.md +0 -0
  28. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/jsguide.md +0 -0
  29. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/jsoncstyleguide.md +0 -0
  30. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/objcguide.md +0 -0
  31. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/pyguide.md +0 -0
  32. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/shellguide.md +0 -0
  33. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/tsguide.md +0 -0
  34. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/guides/vimscriptguide.md +0 -0
  35. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/outline.py +0 -0
  36. {readability_cli-0.10.3 → readability_cli-0.10.5}/readability/tools.py +0 -0
  37. {readability_cli-0.10.3 → readability_cli-0.10.5}/test_guide.py +0 -0
  38. {readability_cli-0.10.3 → readability_cli-0.10.5}/test_package.py +0 -0
@@ -1,15 +1,16 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: readability-cli
3
- Version: 0.10.3
3
+ Version: 0.10.5
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
7
7
  License-File: LICENSE
8
8
  Requires-Python: >=3.12
9
- Requires-Dist: beautifulsoup4>=4.14.3
10
9
  Requires-Dist: click>=8.3.1
11
- Requires-Dist: markdownify>=1.2.2
12
- Requires-Dist: requests>=2.33.0
10
+ Provides-Extra: sync
11
+ Requires-Dist: beautifulsoup4>=4.14.3; extra == 'sync'
12
+ Requires-Dist: markdownify>=1.2.2; extra == 'sync'
13
+ Requires-Dist: requests>=2.33.0; extra == 'sync'
13
14
  Description-Content-Type: text/markdown
14
15
 
15
16
  # Readability
@@ -372,9 +373,13 @@ Supported languages and their aliases:
372
373
  - vim [cached]
373
374
  ```
374
375
 
375
- Refresh the cache with `sync`, which takes languages or refetches everything:
376
+ Refresh the cache with `sync`, which takes languages or refetches everything.
377
+ It fetches and converts HTML, so it needs the `sync` extra; the bundled guides
378
+ are already converted, and every other command reads those:
376
379
 
377
380
  ```bash
381
+ pip install 'readability-cli[sync]'
382
+
378
383
  # Refetch one guide, or a few
379
384
  readability sync python shell
380
385
 
@@ -389,8 +394,8 @@ The tool stores local copies of the style guides in the
389
394
  the `guide` command uses these local files when they exist. The bundled copies
390
395
  are automatically synchronized weekly from the official
391
396
  [Google Style Guides](https://google.github.io/styleguide/) repository via
392
- GitHub Actions, and you can refresh your local cache at any time with the `sync`
393
- command.
397
+ GitHub Actions, so upgrading the package is usually enough. To refresh a local
398
+ cache yourself, install the `sync` extra and run the `sync` command.
394
399
 
395
400
  You can override the default guide directory by setting the
396
401
  `READABILITY_CACHE` environment variable. This is useful if you want to store
@@ -358,9 +358,13 @@ Supported languages and their aliases:
358
358
  - vim [cached]
359
359
  ```
360
360
 
361
- Refresh the cache with `sync`, which takes languages or refetches everything:
361
+ Refresh the cache with `sync`, which takes languages or refetches everything.
362
+ It fetches and converts HTML, so it needs the `sync` extra; the bundled guides
363
+ are already converted, and every other command reads those:
362
364
 
363
365
  ```bash
366
+ pip install 'readability-cli[sync]'
367
+
364
368
  # Refetch one guide, or a few
365
369
  readability sync python shell
366
370
 
@@ -375,8 +379,8 @@ The tool stores local copies of the style guides in the
375
379
  the `guide` command uses these local files when they exist. The bundled copies
376
380
  are automatically synchronized weekly from the official
377
381
  [Google Style Guides](https://google.github.io/styleguide/) repository via
378
- GitHub Actions, and you can refresh your local cache at any time with the `sync`
379
- command.
382
+ GitHub Actions, so upgrading the package is usually enough. To refresh a local
383
+ cache yourself, install the `sync` extra and run the `sync` command.
380
384
 
381
385
  You can override the default guide directory by setting the
382
386
  `READABILITY_CACHE` environment variable. This is useful if you want to store
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": ["./readability/configs/biome-default.json"],
3
+ "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
4
+ "files": { "includes": ["**", "!readability/guides"] }
5
+ }
@@ -1,12 +1,17 @@
1
1
  [project]
2
2
  name = "readability-cli"
3
- version = "0.10.3"
3
+ version = "0.10.5"
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"
7
7
  dependencies = [
8
- "beautifulsoup4>=4.14.3",
9
8
  "click>=8.3.1",
9
+ ]
10
+
11
+ # Only `sync` fetches and converts guides; the rest reads bundled copies.
12
+ [project.optional-dependencies]
13
+ sync = [
14
+ "beautifulsoup4>=4.14.3",
10
15
  "markdownify>=1.2.2",
11
16
  "requests>=2.33.0",
12
17
  ]
@@ -34,6 +39,8 @@ testpaths = ["."]
34
39
  [dependency-groups]
35
40
  dev = [
36
41
  "pytest>=9.0.2",
42
+ # The suite and the weekly guide refresh both exercise `sync`.
43
+ "readability-cli[sync]",
37
44
  "ruff>=0.15.8",
38
45
  ]
39
46
 
@@ -12,6 +12,7 @@ from readability.guide import get_guide
12
12
  from readability.guide import get_guides_dir
13
13
  from readability.guide import get_local_path
14
14
  from readability.guide import LANGUAGE_MAP
15
+ from readability.guide import load_sync_deps
15
16
  from readability.guide import refresh_guide
16
17
  from readability.outline import _echo_outline
17
18
  from readability.outline import _select_section
@@ -123,6 +124,9 @@ def sync(languages: Sequence[str], verbose: bool) -> None:
123
124
  if verbose:
124
125
  logger.setLevel(logging.DEBUG)
125
126
 
127
+ # A precondition, not a per-guide failure the loop should report N times
128
+ load_sync_deps()
129
+
126
130
  filenames = _resolve_filenames(languages)
127
131
  click.echo(f"Synchronizing {len(filenames)} style guide(s)...", err=True)
128
132
 
@@ -147,6 +151,10 @@ def sync(languages: Sequence[str], verbose: bool) -> None:
147
151
  err=True,
148
152
  )
149
153
 
154
+ # A guide that did not refetch leaves a stale local copy behind
155
+ if failure_count:
156
+ sys.exit(1)
157
+
150
158
 
151
159
  def _echo_languages() -> None:
152
160
  """Print the languages that have a guide, with their aliases."""
@@ -240,7 +248,6 @@ def check(paths: Sequence[str], fix: bool, unsafe: bool, verbose: bool) -> None:
240
248
  )
241
249
 
242
250
 
243
- # Main entry point for the CLI
244
251
  def main() -> None:
245
252
  """Main entry point for the CLI."""
246
253
  # Configured here, not at import, so library use has no side effects
@@ -8,7 +8,7 @@
8
8
  "linter": {
9
9
  "enabled": true,
10
10
  "rules": {
11
- "recommended": true
11
+ "preset": "recommended"
12
12
  }
13
13
  },
14
14
  "html": {
@@ -1,21 +1,46 @@
1
1
  """Fetch and cache Google style guides."""
2
2
 
3
+ import functools
3
4
  from importlib.resources import files
4
5
  import logging
5
6
  import os
7
+ import types
6
8
  import warnings
7
9
 
8
- from bs4 import BeautifulSoup
9
- from bs4 import XMLParsedAsHTMLWarning
10
10
  import click
11
- from markdownify import markdownify as md
12
- import requests
13
-
14
- warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
15
11
 
16
12
  logger = logging.getLogger("readability")
17
13
 
18
14
 
15
+ @functools.cache
16
+ def load_sync_deps() -> tuple[
17
+ types.ModuleType, types.ModuleType, types.ModuleType
18
+ ]:
19
+ """Imports the libraries only the guide refresh needs.
20
+
21
+ Guides ship pre-converted, so this stack stays out of the default
22
+ install rather than charging every user for `sync`.
23
+
24
+ Returns:
25
+ The requests, bs4, and markdownify modules.
26
+
27
+ Raises:
28
+ click.ClickException: If the sync extra is not installed.
29
+ """
30
+ try:
31
+ import bs4 # noqa: PLC0415
32
+ import markdownify # noqa: PLC0415
33
+ import requests # noqa: PLC0415
34
+ except ImportError as e:
35
+ raise click.ClickException(
36
+ "Refreshing style guides needs extra libraries. Install them "
37
+ "with: pip install 'readability-cli[sync]'"
38
+ ) from e
39
+
40
+ warnings.filterwarnings("ignore", category=bs4.XMLParsedAsHTMLWarning)
41
+ return requests, bs4, markdownify
42
+
43
+
19
44
  def get_guides_dir() -> str:
20
45
  """Get the directory where style guides are cached.
21
46
 
@@ -29,7 +54,6 @@ def get_guides_dir() -> str:
29
54
  return os.getenv("READABILITY_CACHE") or str(bundled_guides)
30
55
 
31
56
 
32
- # Mapping of languages to their Google Style Guide file paths
33
57
  LANGUAGE_MAP = {
34
58
  "python": "pyguide.md",
35
59
  "shell": "shellguide.md",
@@ -72,9 +96,9 @@ def get_guide_content(url: str) -> str:
72
96
  Raises:
73
97
  click.ClickException: If the HTTP request fails.
74
98
  """
99
+ requests, _, _ = load_sync_deps()
75
100
  logger.info("Fetching style guide from %s", url)
76
101
 
77
- # Perform the HTTP GET request with a timeout
78
102
  try:
79
103
  response = requests.get(url, timeout=10)
80
104
  response.raise_for_status()
@@ -97,9 +121,9 @@ def convert_to_markdown(content: str, filename: str) -> str:
97
121
  Returns:
98
122
  The converted markdown content.
99
123
  """
124
+ _, bs4, markdownify = load_sync_deps()
100
125
  logger.debug("Converting content for %s", filename)
101
126
 
102
- # Handle Markdown files directly
103
127
  if filename.endswith(".md"):
104
128
  return content
105
129
 
@@ -107,11 +131,10 @@ def convert_to_markdown(content: str, filename: str) -> str:
107
131
  if content.lstrip().startswith("<?xml"):
108
132
  content = content.split("?>", 1)[-1].lstrip()
109
133
 
110
- # Handle XML files (used for Vim script guide and JSON style guide)
134
+ # The Vim script and JSON guides are only published as XML
111
135
  if filename.endswith(".xml"):
112
- soup = BeautifulSoup(content, "html.parser")
136
+ soup = bs4.BeautifulSoup(content, "html.parser")
113
137
 
114
- # Add titles as headers
115
138
  guide = soup.find("guide")
116
139
  if guide:
117
140
  title = guide.get("title")
@@ -136,11 +159,10 @@ def convert_to_markdown(content: str, filename: str) -> str:
136
159
 
137
160
  for summary in soup.find_all("summary"):
138
161
  summary.name = "p"
139
- # Wrap content in strong tags
140
162
  content_str = summary.decode_contents()
141
163
  summary.clear()
142
164
  strong = soup.new_tag("strong")
143
- strong.append(BeautifulSoup(content_str, "html.parser"))
165
+ strong.append(bs4.BeautifulSoup(content_str, "html.parser"))
144
166
  summary.append(strong)
145
167
 
146
168
  for snippet in soup.find_all(["code_snippet", "bad_code_snippet"]):
@@ -157,14 +179,11 @@ def convert_to_markdown(content: str, filename: str) -> str:
157
179
  snippet.append(p)
158
180
  snippet.append(code)
159
181
 
160
- # Convert the modified soup to string and then to markdown
161
- return md(str(soup), **MARKDOWNIFY_OPTIONS)
182
+ return markdownify.markdownify(str(soup), **MARKDOWNIFY_OPTIONS)
162
183
 
163
- # Handle HTML files by converting them to Markdown
164
184
  if filename.endswith(".html"):
165
- return md(content, **MARKDOWNIFY_OPTIONS)
185
+ return markdownify.markdownify(content, **MARKDOWNIFY_OPTIONS)
166
186
 
167
- # Fallback to returning raw content
168
187
  return content
169
188
 
170
189
 
@@ -181,9 +200,7 @@ def get_local_path(filename: str) -> str:
181
200
  Returns:
182
201
  The full local path to the cached markdown file.
183
202
  """
184
- # Flatten the filename by replacing '/' with '-'
185
203
  flattened = filename.replace("/", "-")
186
- # Use the flattened filename and change extension to .md for uniform storage
187
204
  base_name = flattened.rsplit(".", 1)[0]
188
205
  return os.path.join(get_guides_dir(), f"{base_name}.md")
189
206
 
@@ -227,7 +244,6 @@ def get_guide(language: str, remote: bool = False) -> str:
227
244
  Raises:
228
245
  click.UsageError: If the language is not supported.
229
246
  """
230
- # Look up the filename in the mapping
231
247
  filename = LANGUAGE_MAP.get(language.lower())
232
248
  if not filename:
233
249
  error_msg = f"Language '{language}' is not supported."
@@ -239,7 +255,6 @@ def get_guide(language: str, remote: bool = False) -> str:
239
255
 
240
256
  local_path = get_local_path(filename)
241
257
 
242
- # If remote is False, check for local file first
243
258
  if not remote and os.path.exists(local_path):
244
259
  logger.info("Reading style guide from local file: %s", local_path)
245
260
  with open(local_path, encoding="utf-8") as f:
@@ -1030,7 +1030,7 @@ def test_bundled_default_configs_are_valid() -> None:
1030
1030
  "indentWidth": 2,
1031
1031
  "lineWidth": 80,
1032
1032
  }
1033
- assert biome_config["linter"]["rules"]["recommended"] is True
1033
+ assert biome_config["linter"]["rules"]["preset"] == "recommended"
1034
1034
  assert biome_config["html"]["parser"]["interpolation"] is True
1035
1035
 
1036
1036
  # The ruff defaults follow the Google Python style guide
@@ -4,6 +4,7 @@ from pathlib import Path
4
4
  from unittest.mock import MagicMock
5
5
  from unittest.mock import patch
6
6
 
7
+ import click
7
8
  from click.testing import CliRunner
8
9
 
9
10
  from readability.cli import cli
@@ -787,6 +788,21 @@ def test_sync_deduplicates_aliases(
787
788
  assert mock_get_content.call_count == 1
788
789
 
789
790
 
791
+ @patch("readability.guide.get_guide_content")
792
+ def test_sync_reports_a_failed_fetch_in_its_exit_code(
793
+ mock_get_content: MagicMock, tmp_path: Path
794
+ ) -> None:
795
+ """A stale local copy left behind is a failure, not a quiet success."""
796
+ mock_get_content.side_effect = click.ClickException("network is down")
797
+
798
+ with patch("readability.guide.get_guides_dir", return_value=str(tmp_path)):
799
+ runner = CliRunner()
800
+ result = runner.invoke(cli, ["sync", "python"])
801
+
802
+ assert result.exit_code != 0
803
+ assert "Failures: 1" in result.stderr
804
+
805
+
790
806
  def test_sync_rejects_an_unsupported_language(tmp_path: Path) -> None:
791
807
  """An unknown language is a typo, not a reason to sync everything."""
792
808
  with patch("readability.guide.get_guides_dir", return_value=str(tmp_path)):
@@ -1,7 +1,11 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 5
3
3
  requires-python = ">=3.12"
4
4
 
5
+ [options]
6
+ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
7
+ exclude-newer-span = "PT24H"
8
+
5
9
  [[package]]
6
10
  name = "beautifulsoup4"
7
11
  version = "4.14.3"
@@ -194,11 +198,15 @@ wheels = [
194
198
 
195
199
  [[package]]
196
200
  name = "readability-cli"
197
- version = "0.10.3"
201
+ version = "0.10.5"
198
202
  source = { editable = "." }
199
203
  dependencies = [
200
- { name = "beautifulsoup4" },
201
204
  { name = "click" },
205
+ ]
206
+
207
+ [package.optional-dependencies]
208
+ sync = [
209
+ { name = "beautifulsoup4" },
202
210
  { name = "markdownify" },
203
211
  { name = "requests" },
204
212
  ]
@@ -206,20 +214,23 @@ dependencies = [
206
214
  [package.dev-dependencies]
207
215
  dev = [
208
216
  { name = "pytest" },
217
+ { name = "readability-cli", extra = ["sync"] },
209
218
  { name = "ruff" },
210
219
  ]
211
220
 
212
221
  [package.metadata]
213
222
  requires-dist = [
214
- { name = "beautifulsoup4", specifier = ">=4.14.3" },
223
+ { name = "beautifulsoup4", marker = "extra == 'sync'", specifier = ">=4.14.3" },
215
224
  { name = "click", specifier = ">=8.3.1" },
216
- { name = "markdownify", specifier = ">=1.2.2" },
217
- { name = "requests", specifier = ">=2.33.0" },
225
+ { name = "markdownify", marker = "extra == 'sync'", specifier = ">=1.2.2" },
226
+ { name = "requests", marker = "extra == 'sync'", specifier = ">=2.33.0" },
218
227
  ]
228
+ provides-extras = ["sync"]
219
229
 
220
230
  [package.metadata.requires-dev]
221
231
  dev = [
222
232
  { name = "pytest", specifier = ">=9.0.2" },
233
+ { name = "readability-cli", extras = ["sync"] },
223
234
  { name = "ruff", specifier = ">=0.15.8" },
224
235
  ]
225
236