rolfedh-doc-utils 0.1.10__tar.gz → 0.1.11__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.
- {rolfedh_doc_utils-0.1.10/rolfedh_doc_utils.egg-info → rolfedh_doc_utils-0.1.11}/PKG-INFO +3 -2
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/README.md +2 -1
- rolfedh_doc_utils-0.1.11/doc_utils/unused_attributes.py +138 -0
- rolfedh_doc_utils-0.1.11/doc_utils/validate_links.py +576 -0
- rolfedh_doc_utils-0.1.11/find_unused_attributes.py +82 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/pyproject.toml +3 -2
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11/rolfedh_doc_utils.egg-info}/PKG-INFO +3 -2
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/SOURCES.txt +4 -1
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/entry_points.txt +1 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/top_level.txt +1 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_cli_entry_points.py +13 -6
- rolfedh_doc_utils-0.1.11/tests/test_validate_links.py +385 -0
- rolfedh_doc_utils-0.1.11/validate_links.py +202 -0
- rolfedh_doc_utils-0.1.10/doc_utils/unused_attributes.py +0 -50
- rolfedh_doc_utils-0.1.10/find_unused_attributes.py +0 -41
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/LICENSE +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/archive_unused_files.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/archive_unused_images.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/check_scannability.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/__init__.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/extract_link_attributes.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/file_utils.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/format_asciidoc_spacing.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/replace_link_attributes.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/scannability.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/topic_map_parser.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/unused_adoc.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/unused_images.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/extract_link_attributes.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/format_asciidoc_spacing.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/replace_link_attributes.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/dependency_links.txt +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/requires.txt +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/setup.cfg +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/setup.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_archive_unused_files.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_archive_unused_images.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_auto_discovery.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_check_scannability.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_extract_link_attributes.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_file_utils.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_fixture_archive_unused_files.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_fixture_archive_unused_images.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_fixture_check_scannability.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_parse_exclude_list.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_symlink_handling.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_topic_map_parser.py +0 -0
- {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_unused_attributes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rolfedh-doc-utils
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: CLI tools for AsciiDoc documentation projects
|
|
5
5
|
Author: Rolfe Dlugy-Hegwer
|
|
6
6
|
License: MIT License
|
|
@@ -79,9 +79,10 @@ pip install -e .
|
|
|
79
79
|
|
|
80
80
|
| Tool | Description | Usage |
|
|
81
81
|
|------|-------------|-------|
|
|
82
|
+
| **`validate-links`** [EXPERIMENTAL] | Validates all links in documentation, with URL transposition for preview environments | `validate-links --transpose "https://prod--https://preview"` |
|
|
82
83
|
| **`extract-link-attributes`** | Extracts link/xref macros with attributes into reusable definitions | `extract-link-attributes --dry-run` |
|
|
83
84
|
| **`replace-link-attributes`** | Resolves Vale LinkAttribute issues by replacing attributes in link URLs | `replace-link-attributes --dry-run` |
|
|
84
|
-
| **`format-asciidoc-spacing`** | Standardizes spacing after headings and around includes | `format-asciidoc-spacing --dry-run modules/` |
|
|
85
|
+
| **`format-asciidoc-spacing`** [EXPERIMENTAL] | Standardizes spacing after headings and around includes | `format-asciidoc-spacing --dry-run modules/` |
|
|
85
86
|
| **`check-scannability`** | Analyzes readability (sentence/paragraph length) | `check-scannability --max-words 25` |
|
|
86
87
|
| **`archive-unused-files`** | Finds and archives unreferenced .adoc files | `archive-unused-files` (preview)<br>`archive-unused-files --archive` (execute) |
|
|
87
88
|
| **`archive-unused-images`** | Finds and archives unreferenced images | `archive-unused-images` (preview)<br>`archive-unused-images --archive` (execute) |
|
|
@@ -46,9 +46,10 @@ pip install -e .
|
|
|
46
46
|
|
|
47
47
|
| Tool | Description | Usage |
|
|
48
48
|
|------|-------------|-------|
|
|
49
|
+
| **`validate-links`** [EXPERIMENTAL] | Validates all links in documentation, with URL transposition for preview environments | `validate-links --transpose "https://prod--https://preview"` |
|
|
49
50
|
| **`extract-link-attributes`** | Extracts link/xref macros with attributes into reusable definitions | `extract-link-attributes --dry-run` |
|
|
50
51
|
| **`replace-link-attributes`** | Resolves Vale LinkAttribute issues by replacing attributes in link URLs | `replace-link-attributes --dry-run` |
|
|
51
|
-
| **`format-asciidoc-spacing`** | Standardizes spacing after headings and around includes | `format-asciidoc-spacing --dry-run modules/` |
|
|
52
|
+
| **`format-asciidoc-spacing`** [EXPERIMENTAL] | Standardizes spacing after headings and around includes | `format-asciidoc-spacing --dry-run modules/` |
|
|
52
53
|
| **`check-scannability`** | Analyzes readability (sentence/paragraph length) | `check-scannability --max-words 25` |
|
|
53
54
|
| **`archive-unused-files`** | Finds and archives unreferenced .adoc files | `archive-unused-files` (preview)<br>`archive-unused-files --archive` (execute) |
|
|
54
55
|
| **`archive-unused-images`** | Finds and archives unreferenced images | `archive-unused-images` (preview)<br>`archive-unused-images --archive` (execute) |
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for finding unused AsciiDoc attributes.
|
|
3
|
+
|
|
4
|
+
Functions:
|
|
5
|
+
- parse_attributes_file: Parse attribute names from an attributes.adoc file.
|
|
6
|
+
- find_adoc_files: Recursively find all .adoc files in a directory (ignoring symlinks).
|
|
7
|
+
- scan_for_attribute_usage: Find which attributes are used in a set of .adoc files.
|
|
8
|
+
- find_unused_attributes: Main function to return unused attributes.
|
|
9
|
+
- find_attributes_files: Find all potential attributes files in the repository.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Set, List, Optional
|
|
16
|
+
|
|
17
|
+
def parse_attributes_file(attr_file: str) -> Set[str]:
|
|
18
|
+
attributes = set()
|
|
19
|
+
|
|
20
|
+
# Check if file exists
|
|
21
|
+
if not os.path.exists(attr_file):
|
|
22
|
+
raise FileNotFoundError(f"Attributes file not found: {attr_file}")
|
|
23
|
+
|
|
24
|
+
# Check if it's a file (not a directory)
|
|
25
|
+
if not os.path.isfile(attr_file):
|
|
26
|
+
raise ValueError(f"Path is not a file: {attr_file}")
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
with open(attr_file, 'r', encoding='utf-8') as f:
|
|
30
|
+
for line in f:
|
|
31
|
+
match = re.match(r'^:([\w-]+):', line.strip())
|
|
32
|
+
if match:
|
|
33
|
+
attributes.add(match.group(1))
|
|
34
|
+
except PermissionError:
|
|
35
|
+
raise PermissionError(f"Permission denied reading file: {attr_file}")
|
|
36
|
+
except UnicodeDecodeError as e:
|
|
37
|
+
raise ValueError(f"Unable to read file (encoding issue): {attr_file}\n{str(e)}")
|
|
38
|
+
|
|
39
|
+
return attributes
|
|
40
|
+
|
|
41
|
+
def find_adoc_files(root_dir: str) -> List[str]:
|
|
42
|
+
adoc_files = []
|
|
43
|
+
for dirpath, dirnames, filenames in os.walk(root_dir, followlinks=False):
|
|
44
|
+
for fname in filenames:
|
|
45
|
+
if fname.endswith('.adoc'):
|
|
46
|
+
full_path = os.path.join(dirpath, fname)
|
|
47
|
+
if not os.path.islink(full_path):
|
|
48
|
+
adoc_files.append(full_path)
|
|
49
|
+
return adoc_files
|
|
50
|
+
|
|
51
|
+
def scan_for_attribute_usage(adoc_files: List[str], attributes: Set[str]) -> Set[str]:
|
|
52
|
+
used = set()
|
|
53
|
+
attr_pattern = re.compile(r'\{([\w-]+)\}')
|
|
54
|
+
for file in adoc_files:
|
|
55
|
+
with open(file, 'r', encoding='utf-8') as f:
|
|
56
|
+
for line in f:
|
|
57
|
+
for match in attr_pattern.findall(line):
|
|
58
|
+
if match in attributes:
|
|
59
|
+
used.add(match)
|
|
60
|
+
return used
|
|
61
|
+
|
|
62
|
+
def find_attributes_files(root_dir: str = '.') -> List[str]:
|
|
63
|
+
"""Find all attributes.adoc files in the repository."""
|
|
64
|
+
attributes_files = []
|
|
65
|
+
root_path = Path(root_dir)
|
|
66
|
+
|
|
67
|
+
# Common attribute file patterns
|
|
68
|
+
patterns = ['**/attributes.adoc', '**/attributes*.adoc', '**/*attributes.adoc', '**/*-attributes.adoc']
|
|
69
|
+
|
|
70
|
+
for pattern in patterns:
|
|
71
|
+
for path in root_path.glob(pattern):
|
|
72
|
+
# Skip hidden directories and common build directories
|
|
73
|
+
parts = path.parts
|
|
74
|
+
if any(part.startswith('.') or part in ['target', 'build', 'node_modules', '.archive'] for part in parts):
|
|
75
|
+
continue
|
|
76
|
+
# Convert to string and avoid duplicates
|
|
77
|
+
str_path = str(path)
|
|
78
|
+
if str_path not in attributes_files:
|
|
79
|
+
attributes_files.append(str_path)
|
|
80
|
+
|
|
81
|
+
# Sort for consistent ordering
|
|
82
|
+
attributes_files.sort()
|
|
83
|
+
return attributes_files
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def select_attributes_file(attributes_files: List[str]) -> Optional[str]:
|
|
87
|
+
"""Interactive selection of attributes file from a list."""
|
|
88
|
+
if not attributes_files:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
if len(attributes_files) == 1:
|
|
92
|
+
print(f"Found attributes file: {attributes_files[0]}")
|
|
93
|
+
response = input("Use this file? (y/n): ").strip().lower()
|
|
94
|
+
if response == 'y':
|
|
95
|
+
return attributes_files[0]
|
|
96
|
+
else:
|
|
97
|
+
response = input("Enter the path to your attributes file: ").strip()
|
|
98
|
+
if os.path.exists(response) and os.path.isfile(response):
|
|
99
|
+
return response
|
|
100
|
+
else:
|
|
101
|
+
print(f"Error: File not found: {response}")
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
# Multiple files found
|
|
105
|
+
print("\nFound multiple attributes files:")
|
|
106
|
+
for i, file_path in enumerate(attributes_files, 1):
|
|
107
|
+
print(f" {i}. {file_path}")
|
|
108
|
+
print(f" {len(attributes_files) + 1}. Enter custom path")
|
|
109
|
+
|
|
110
|
+
while True:
|
|
111
|
+
response = input(f"\nSelect option (1-{len(attributes_files) + 1}) or 'q' to quit: ").strip()
|
|
112
|
+
if response.lower() == 'q':
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
choice = int(response)
|
|
117
|
+
if 1 <= choice <= len(attributes_files):
|
|
118
|
+
return attributes_files[choice - 1]
|
|
119
|
+
elif choice == len(attributes_files) + 1:
|
|
120
|
+
response = input("Enter the path to your attributes file: ").strip()
|
|
121
|
+
if os.path.exists(response) and os.path.isfile(response):
|
|
122
|
+
return response
|
|
123
|
+
else:
|
|
124
|
+
print(f"Error: File not found: {response}")
|
|
125
|
+
else:
|
|
126
|
+
print(f"Invalid choice. Please enter a number between 1 and {len(attributes_files) + 1}")
|
|
127
|
+
except ValueError:
|
|
128
|
+
print("Invalid input. Please enter a number.")
|
|
129
|
+
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def find_unused_attributes(attr_file: str, adoc_root: str = '.') -> List[str]:
|
|
134
|
+
attributes = parse_attributes_file(attr_file)
|
|
135
|
+
adoc_files = find_adoc_files(adoc_root)
|
|
136
|
+
used = scan_for_attribute_usage(adoc_files, attributes)
|
|
137
|
+
unused = sorted(attributes - used)
|
|
138
|
+
return unused
|