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.
Files changed (48) hide show
  1. {rolfedh_doc_utils-0.1.10/rolfedh_doc_utils.egg-info → rolfedh_doc_utils-0.1.11}/PKG-INFO +3 -2
  2. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/README.md +2 -1
  3. rolfedh_doc_utils-0.1.11/doc_utils/unused_attributes.py +138 -0
  4. rolfedh_doc_utils-0.1.11/doc_utils/validate_links.py +576 -0
  5. rolfedh_doc_utils-0.1.11/find_unused_attributes.py +82 -0
  6. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/pyproject.toml +3 -2
  7. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11/rolfedh_doc_utils.egg-info}/PKG-INFO +3 -2
  8. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/SOURCES.txt +4 -1
  9. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/entry_points.txt +1 -0
  10. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/top_level.txt +1 -0
  11. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_cli_entry_points.py +13 -6
  12. rolfedh_doc_utils-0.1.11/tests/test_validate_links.py +385 -0
  13. rolfedh_doc_utils-0.1.11/validate_links.py +202 -0
  14. rolfedh_doc_utils-0.1.10/doc_utils/unused_attributes.py +0 -50
  15. rolfedh_doc_utils-0.1.10/find_unused_attributes.py +0 -41
  16. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/LICENSE +0 -0
  17. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/archive_unused_files.py +0 -0
  18. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/archive_unused_images.py +0 -0
  19. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/check_scannability.py +0 -0
  20. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/__init__.py +0 -0
  21. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/extract_link_attributes.py +0 -0
  22. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/file_utils.py +0 -0
  23. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/format_asciidoc_spacing.py +0 -0
  24. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/replace_link_attributes.py +0 -0
  25. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/scannability.py +0 -0
  26. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/topic_map_parser.py +0 -0
  27. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/unused_adoc.py +0 -0
  28. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/doc_utils/unused_images.py +0 -0
  29. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/extract_link_attributes.py +0 -0
  30. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/format_asciidoc_spacing.py +0 -0
  31. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/replace_link_attributes.py +0 -0
  32. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/dependency_links.txt +0 -0
  33. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/rolfedh_doc_utils.egg-info/requires.txt +0 -0
  34. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/setup.cfg +0 -0
  35. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/setup.py +0 -0
  36. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_archive_unused_files.py +0 -0
  37. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_archive_unused_images.py +0 -0
  38. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_auto_discovery.py +0 -0
  39. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_check_scannability.py +0 -0
  40. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_extract_link_attributes.py +0 -0
  41. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_file_utils.py +0 -0
  42. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_fixture_archive_unused_files.py +0 -0
  43. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_fixture_archive_unused_images.py +0 -0
  44. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_fixture_check_scannability.py +0 -0
  45. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_parse_exclude_list.py +0 -0
  46. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_symlink_handling.py +0 -0
  47. {rolfedh_doc_utils-0.1.10 → rolfedh_doc_utils-0.1.11}/tests/test_topic_map_parser.py +0 -0
  48. {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.10
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