rolfedh-doc-utils 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl

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.
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ replace-link-attributes - Replace AsciiDoc attributes within link URLs with their actual values.
4
+
5
+ This script finds and replaces attribute references (like {attribute-name}) that appear
6
+ in the URL portion of AsciiDoc link macros (link: and xref:) with their resolved values
7
+ from attributes.adoc. Link text is preserved unchanged.
8
+ """
9
+
10
+ import argparse
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ from doc_utils.replace_link_attributes import (
16
+ find_attributes_files,
17
+ load_attributes,
18
+ resolve_nested_attributes,
19
+ replace_link_attributes_in_file,
20
+ find_adoc_files
21
+ )
22
+
23
+
24
+ def prompt_for_attributes_file(attributes_files: list[Path]) -> Optional[Path]:
25
+ """Prompt user to select or specify attributes file."""
26
+ if not attributes_files:
27
+ print("No attributes.adoc files found in the repository.")
28
+ response = input("Enter the path to your attributes.adoc file (or 'q' to quit): ").strip()
29
+ if response.lower() == 'q':
30
+ return None
31
+ path = Path(response)
32
+ if path.exists() and path.is_file():
33
+ return path
34
+ else:
35
+ print(f"Error: File not found: {response}")
36
+ return None
37
+
38
+ if len(attributes_files) == 1:
39
+ file_path = attributes_files[0]
40
+ response = input(f"Found attributes file: {file_path}\nUse this file? (y/n/q): ").strip().lower()
41
+ if response == 'y':
42
+ return file_path
43
+ elif response == 'q':
44
+ return None
45
+ else:
46
+ response = input("Enter the path to your attributes.adoc file (or 'q' to quit): ").strip()
47
+ if response.lower() == 'q':
48
+ return None
49
+ path = Path(response)
50
+ if path.exists() and path.is_file():
51
+ return path
52
+ else:
53
+ print(f"Error: File not found: {response}")
54
+ return None
55
+
56
+ # Multiple files found
57
+ print("\nFound multiple attributes.adoc files:")
58
+ for i, file_path in enumerate(attributes_files, 1):
59
+ print(f" {i}. {file_path}")
60
+ print(f" {len(attributes_files) + 1}. Enter custom path")
61
+
62
+ while True:
63
+ response = input(f"\nSelect option (1-{len(attributes_files) + 1}) or 'q' to quit: ").strip()
64
+ if response.lower() == 'q':
65
+ return None
66
+
67
+ try:
68
+ choice = int(response)
69
+ if 1 <= choice <= len(attributes_files):
70
+ return attributes_files[choice - 1]
71
+ elif choice == len(attributes_files) + 1:
72
+ response = input("Enter the path to your attributes.adoc file: ").strip()
73
+ path = Path(response)
74
+ if path.exists() and path.is_file():
75
+ return path
76
+ else:
77
+ print(f"Error: File not found: {response}")
78
+ else:
79
+ print(f"Invalid choice. Please enter a number between 1 and {len(attributes_files) + 1}")
80
+ except ValueError:
81
+ print("Invalid input. Please enter a number.")
82
+
83
+
84
+ def main():
85
+ parser = argparse.ArgumentParser(
86
+ description='Replace AsciiDoc attributes within link macros with their actual values.'
87
+ )
88
+ parser.add_argument(
89
+ '--dry-run', '-n',
90
+ action='store_true',
91
+ help='Show what would be changed without making actual modifications'
92
+ )
93
+ parser.add_argument(
94
+ '--path', '-p',
95
+ type=str,
96
+ default='.',
97
+ help='Repository path to search (default: current directory)'
98
+ )
99
+ parser.add_argument(
100
+ '--attributes-file', '-a',
101
+ type=str,
102
+ help='Path to attributes.adoc file (skips interactive selection)'
103
+ )
104
+
105
+ args = parser.parse_args()
106
+
107
+ # Determine repository root
108
+ repo_root = Path(args.path).resolve()
109
+
110
+ if not repo_root.exists() or not repo_root.is_dir():
111
+ print(f"Error: Directory not found: {repo_root}")
112
+ sys.exit(1)
113
+
114
+ print(f"{'DRY RUN MODE - ' if args.dry_run else ''}Searching in: {repo_root}")
115
+
116
+ # Find or get attributes file
117
+ if args.attributes_file:
118
+ attributes_file = Path(args.attributes_file)
119
+ if not attributes_file.exists():
120
+ print(f"Error: Specified attributes file not found: {attributes_file}")
121
+ sys.exit(1)
122
+ else:
123
+ print("\nSearching for attributes.adoc files...")
124
+ attributes_files = find_attributes_files(repo_root)
125
+ attributes_file = prompt_for_attributes_file(attributes_files)
126
+
127
+ if not attributes_file:
128
+ print("Operation cancelled.")
129
+ sys.exit(0)
130
+
131
+ print(f"\nLoading attributes from: {attributes_file}")
132
+ attributes = load_attributes(attributes_file)
133
+
134
+ if not attributes:
135
+ print("No attributes found in the file.")
136
+ sys.exit(1)
137
+
138
+ print(f"Found {len(attributes)} attributes")
139
+
140
+ # Resolve nested references
141
+ print("Resolving nested attribute references...")
142
+ attributes = resolve_nested_attributes(attributes)
143
+
144
+ # Find all AsciiDoc files
145
+ print(f"\nSearching for *.adoc files in {repo_root}")
146
+ adoc_files = find_adoc_files(repo_root)
147
+
148
+ # Exclude the attributes file itself
149
+ adoc_files = [f for f in adoc_files if f != attributes_file]
150
+
151
+ print(f"Found {len(adoc_files)} AsciiDoc files to process")
152
+
153
+ if args.dry_run:
154
+ print("\n*** DRY RUN MODE - No files will be modified ***\n")
155
+
156
+ # Process each file
157
+ total_replacements = 0
158
+ files_modified = 0
159
+
160
+ for file_path in adoc_files:
161
+ replacements = replace_link_attributes_in_file(file_path, attributes, args.dry_run)
162
+ if replacements > 0:
163
+ rel_path = file_path.relative_to(repo_root)
164
+ prefix = "[DRY RUN] " if args.dry_run else ""
165
+ print(f" {prefix}Modified {rel_path}: {replacements} replacements")
166
+ total_replacements += replacements
167
+ files_modified += 1
168
+
169
+ # Summary
170
+ print(f"\nSummary:")
171
+ if args.dry_run:
172
+ print(f" Would modify {files_modified} files")
173
+ print(f" Would make {total_replacements} replacements")
174
+ print("\nRun without --dry-run to apply changes.")
175
+ else:
176
+ print(f" Total files modified: {files_modified}")
177
+ print(f" Total replacements: {total_replacements}")
178
+
179
+ if total_replacements == 0:
180
+ print("\nNo attribute references found within link macros.")
181
+ else:
182
+ print("\nReplacement complete!")
183
+
184
+
185
+ if __name__ == '__main__':
186
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rolfedh-doc-utils
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: CLI tools for AsciiDoc documentation projects
5
5
  Author: Rolfe Dlugy-Hegwer
6
6
  License: MIT License
@@ -79,6 +79,8 @@ pip install -e .
79
79
 
80
80
  | Tool | Description | Usage |
81
81
  |------|-------------|-------|
82
+ | **`extract-link-attributes`** | Extracts link/xref macros with attributes into reusable definitions | `extract-link-attributes --dry-run` |
83
+ | **`replace-link-attributes`** | Resolves Vale LinkAttribute issues by replacing attributes in link URLs | `replace-link-attributes --dry-run` |
82
84
  | **`format-asciidoc-spacing`** | Standardizes spacing after headings and around includes | `format-asciidoc-spacing --dry-run modules/` |
83
85
  | **`check-scannability`** | Analyzes readability (sentence/paragraph length) | `check-scannability --max-words 25` |
84
86
  | **`archive-unused-files`** | Finds and archives unreferenced .adoc files | `archive-unused-files` (preview)<br>`archive-unused-files --archive` (execute) |
@@ -0,0 +1,23 @@
1
+ archive_unused_files.py,sha256=KMC5a1WL3rZ5owoVnncvfpT1YeMKbVXq9giHvadDgbM,1936
2
+ archive_unused_images.py,sha256=PG2o3haovYckgfhoPhl6KRG_a9czyZuqlLkzkupKTCY,1526
3
+ check_scannability.py,sha256=gcM-vFXKHGP_yFBz7-V5xbXWhIMmtMzBYIGwP9CFbzI,5140
4
+ extract_link_attributes.py,sha256=utDM1FE-VEr649HhIH5BreXvxDNLnnAJO9dB5rs5f9Q,2535
5
+ find_unused_attributes.py,sha256=fk-K32eoCVHxoj7RiBNgSmX1arBLuwYfdSAOMc-wIx0,1677
6
+ format_asciidoc_spacing.py,sha256=ROp-cdMs2_hk8H4z5ljT0iDgGtsiECZ8TVjjcN_oOWE,3874
7
+ replace_link_attributes.py,sha256=vg_aufw7dKXvh_epCKRNq_hEBMU_9crZ_JyJPpxSMNk,6454
8
+ doc_utils/__init__.py,sha256=qqZR3lohzkP63soymrEZPBGzzk6-nFzi4_tSffjmu_0,74
9
+ doc_utils/extract_link_attributes.py,sha256=qBpJuTXNrhy15klpqC0iELZzcSLztEzMSmhEnKyQZT0,15574
10
+ doc_utils/file_utils.py,sha256=fpTh3xx759sF8sNocdn_arsP3KAv8XA6cTQTAVIZiZg,4247
11
+ doc_utils/format_asciidoc_spacing.py,sha256=XnVJekaj39aDzjV3xFKl58flM41AaJzejxNYJIIAMz0,10139
12
+ doc_utils/replace_link_attributes.py,sha256=kBiePbxjQn3O2rzqmYY8Mqy_mJgZ6yw048vSZ5SSB5E,6587
13
+ doc_utils/scannability.py,sha256=XwlmHqDs69p_V36X7DLjPTy0DUoLszSGqYjJ9wE-3hg,982
14
+ doc_utils/topic_map_parser.py,sha256=tKcIO1m9r2K6dvPRGue58zqMr0O2zKU1gnZMzEE3U6o,4571
15
+ doc_utils/unused_adoc.py,sha256=2cbqcYr1os2EhETUU928BlPRlsZVSdI00qaMhqjSIqQ,5263
16
+ doc_utils/unused_attributes.py,sha256=HBgmHelqearfWl3TTC2bZGiJytjLADIgiGQUNKqXXPg,1847
17
+ doc_utils/unused_images.py,sha256=nqn36Bbrmon2KlGlcaruNjJJvTQ8_9H0WU9GvCW7rW8,1456
18
+ rolfedh_doc_utils-0.1.10.dist-info/licenses/LICENSE,sha256=vLxtwMVOJA_hEy8b77niTkdmQI9kNJskXHq0dBS36e0,1075
19
+ rolfedh_doc_utils-0.1.10.dist-info/METADATA,sha256=Kk1Ur-SbE2XIP55NJ7Y5oVB-KNScnlADwmZyFSthTXo,7180
20
+ rolfedh_doc_utils-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ rolfedh_doc_utils-0.1.10.dist-info/entry_points.txt,sha256=aQtQRDwcdDN-VLBCnQBfmoozzQiaCUZ9dqcLLv8fCkM,381
22
+ rolfedh_doc_utils-0.1.10.dist-info/top_level.txt,sha256=ILTc2mA4sHdDp0GvKC8JXO1I_DBP7vvF5hn-PFkMcL8,167
23
+ rolfedh_doc_utils-0.1.10.dist-info/RECORD,,
@@ -2,5 +2,7 @@
2
2
  archive-unused-files = archive_unused_files:main
3
3
  archive-unused-images = archive_unused_images:main
4
4
  check-scannability = check_scannability:main
5
+ extract-link-attributes = extract_link_attributes:main
5
6
  find-unused-attributes = find_unused_attributes:main
6
7
  format-asciidoc-spacing = format_asciidoc_spacing:main
8
+ replace-link-attributes = replace_link_attributes:main
@@ -2,5 +2,7 @@ archive_unused_files
2
2
  archive_unused_images
3
3
  check_scannability
4
4
  doc_utils
5
+ extract_link_attributes
5
6
  find_unused_attributes
6
7
  format_asciidoc_spacing
8
+ replace_link_attributes
@@ -1,18 +0,0 @@
1
- archive_unused_files.py,sha256=KMC5a1WL3rZ5owoVnncvfpT1YeMKbVXq9giHvadDgbM,1936
2
- archive_unused_images.py,sha256=PG2o3haovYckgfhoPhl6KRG_a9czyZuqlLkzkupKTCY,1526
3
- check_scannability.py,sha256=gcM-vFXKHGP_yFBz7-V5xbXWhIMmtMzBYIGwP9CFbzI,5140
4
- find_unused_attributes.py,sha256=fk-K32eoCVHxoj7RiBNgSmX1arBLuwYfdSAOMc-wIx0,1677
5
- format_asciidoc_spacing.py,sha256=Jy_V8c03KGRPJxu4uPdDuMsFg-Q-4AGD9BdkGnApUu8,13241
6
- doc_utils/__init__.py,sha256=qqZR3lohzkP63soymrEZPBGzzk6-nFzi4_tSffjmu_0,74
7
- doc_utils/file_utils.py,sha256=fpTh3xx759sF8sNocdn_arsP3KAv8XA6cTQTAVIZiZg,4247
8
- doc_utils/scannability.py,sha256=XwlmHqDs69p_V36X7DLjPTy0DUoLszSGqYjJ9wE-3hg,982
9
- doc_utils/topic_map_parser.py,sha256=tKcIO1m9r2K6dvPRGue58zqMr0O2zKU1gnZMzEE3U6o,4571
10
- doc_utils/unused_adoc.py,sha256=2cbqcYr1os2EhETUU928BlPRlsZVSdI00qaMhqjSIqQ,5263
11
- doc_utils/unused_attributes.py,sha256=HBgmHelqearfWl3TTC2bZGiJytjLADIgiGQUNKqXXPg,1847
12
- doc_utils/unused_images.py,sha256=nqn36Bbrmon2KlGlcaruNjJJvTQ8_9H0WU9GvCW7rW8,1456
13
- rolfedh_doc_utils-0.1.8.dist-info/licenses/LICENSE,sha256=vLxtwMVOJA_hEy8b77niTkdmQI9kNJskXHq0dBS36e0,1075
14
- rolfedh_doc_utils-0.1.8.dist-info/METADATA,sha256=KrjqTRhF_B26ZNt37SZXReMcV4-ztpy1aWHJepYxMDE,6891
15
- rolfedh_doc_utils-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- rolfedh_doc_utils-0.1.8.dist-info/entry_points.txt,sha256=dA-u4qlxKcewUAOFgaeW1NUIrkhsVknuwFuGTn1-84w,271
17
- rolfedh_doc_utils-0.1.8.dist-info/top_level.txt,sha256=WrYqTa4wbjRPMUl2hgJBfP734XsXLkhbnzUublx8S0s,119
18
- rolfedh_doc_utils-0.1.8.dist-info/RECORD,,