path-link 0.2.0__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.
- path_link-0.2.0.dist-info/METADATA +643 -0
- path_link-0.2.0.dist-info/RECORD +20 -0
- path_link-0.2.0.dist-info/WHEEL +5 -0
- path_link-0.2.0.dist-info/entry_points.txt +2 -0
- path_link-0.2.0.dist-info/licenses/LICENSE +23 -0
- path_link-0.2.0.dist-info/top_level.txt +1 -0
- project_paths/__init__.py +165 -0
- project_paths/builder.py +84 -0
- project_paths/builtin_validators/__init__.py +6 -0
- project_paths/builtin_validators/sandbox.py +159 -0
- project_paths/builtin_validators/strict.py +126 -0
- project_paths/cli.py +201 -0
- project_paths/docs/ai_guidelines.md +668 -0
- project_paths/docs/developer_guide.md +399 -0
- project_paths/docs/metadata.json +119 -0
- project_paths/get_paths.py +104 -0
- project_paths/main.py +2 -0
- project_paths/model.py +76 -0
- project_paths/project_paths_static.py +14 -0
- project_paths/validation.py +94 -0
project_paths/cli.py
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
"""Command-line interface for ptool-serena.
|
2
|
+
|
3
|
+
Provides three commands:
|
4
|
+
1. print - Print resolved paths as JSON
|
5
|
+
2. validate - Run validators and report results
|
6
|
+
3. gen-static - Generate static dataclass file
|
7
|
+
"""
|
8
|
+
|
9
|
+
import argparse
|
10
|
+
import json
|
11
|
+
import sys
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import NoReturn
|
14
|
+
|
15
|
+
from project_paths import ProjectPaths, write_dataclass_file, validate_or_raise
|
16
|
+
from project_paths.builtin_validators import StrictPathValidator
|
17
|
+
from project_paths.validation import PathValidationError
|
18
|
+
|
19
|
+
|
20
|
+
def cmd_print(args: argparse.Namespace) -> int:
|
21
|
+
"""Print resolved paths as JSON."""
|
22
|
+
try:
|
23
|
+
if args.source == "pyproject":
|
24
|
+
paths = ProjectPaths.from_pyproject()
|
25
|
+
elif args.source == "config":
|
26
|
+
# Default to .paths file in current directory
|
27
|
+
config_file = args.config if args.config else ".paths"
|
28
|
+
paths = ProjectPaths.from_config(config_file)
|
29
|
+
else:
|
30
|
+
print(f"Unknown source: {args.source}", file=sys.stderr)
|
31
|
+
return 1
|
32
|
+
|
33
|
+
# Convert paths to dict and serialize Path objects as strings
|
34
|
+
paths_dict = paths.to_dict()
|
35
|
+
serializable = {k: str(v) for k, v in paths_dict.items()}
|
36
|
+
|
37
|
+
print(json.dumps(serializable, indent=2))
|
38
|
+
return 0
|
39
|
+
|
40
|
+
except Exception as e:
|
41
|
+
print(f"Error loading paths: {e}", file=sys.stderr)
|
42
|
+
return 1
|
43
|
+
|
44
|
+
|
45
|
+
def cmd_validate(args: argparse.Namespace) -> int:
|
46
|
+
"""Run validators and report results."""
|
47
|
+
try:
|
48
|
+
# Load paths
|
49
|
+
if args.source == "pyproject":
|
50
|
+
paths = ProjectPaths.from_pyproject()
|
51
|
+
elif args.source == "config":
|
52
|
+
config_file = args.config if args.config else ".paths"
|
53
|
+
paths = ProjectPaths.from_config(config_file)
|
54
|
+
else:
|
55
|
+
print(f"Unknown source: {args.source}", file=sys.stderr)
|
56
|
+
return 1
|
57
|
+
|
58
|
+
# Run validation
|
59
|
+
if args.strict:
|
60
|
+
# Strict mode: require common paths exist
|
61
|
+
validator = StrictPathValidator(
|
62
|
+
required=["base_dir"], must_be_dir=["base_dir"], allow_symlinks=False
|
63
|
+
)
|
64
|
+
|
65
|
+
if args.raise_on_error:
|
66
|
+
# Raise exception on validation failure
|
67
|
+
validate_or_raise(paths, validator)
|
68
|
+
print("✅ All paths valid (strict mode)")
|
69
|
+
return 0
|
70
|
+
else:
|
71
|
+
# Just print results
|
72
|
+
result = validator.validate(paths)
|
73
|
+
if result.ok():
|
74
|
+
print("✅ All paths valid (strict mode)")
|
75
|
+
return 0
|
76
|
+
else:
|
77
|
+
print("❌ Validation failed:", file=sys.stderr)
|
78
|
+
for error in result.errors():
|
79
|
+
print(
|
80
|
+
f" ERROR [{error.code}] {error.field}: {error.message}",
|
81
|
+
file=sys.stderr,
|
82
|
+
)
|
83
|
+
for warning in result.warnings():
|
84
|
+
print(
|
85
|
+
f" WARN [{warning.code}] {warning.field}: {warning.message}",
|
86
|
+
file=sys.stderr,
|
87
|
+
)
|
88
|
+
return 1
|
89
|
+
else:
|
90
|
+
# Basic mode: just check that paths can be loaded
|
91
|
+
print("✅ Paths loaded successfully")
|
92
|
+
paths_dict = paths.to_dict()
|
93
|
+
print(f" Loaded {len(paths_dict)} paths")
|
94
|
+
return 0
|
95
|
+
|
96
|
+
except PathValidationError as e:
|
97
|
+
print(f"❌ Validation failed: {e}", file=sys.stderr)
|
98
|
+
return 1
|
99
|
+
except Exception as e:
|
100
|
+
print(f"Error: {e}", file=sys.stderr)
|
101
|
+
return 1
|
102
|
+
|
103
|
+
|
104
|
+
def cmd_gen_static(args: argparse.Namespace) -> int:
|
105
|
+
"""Generate static dataclass file."""
|
106
|
+
try:
|
107
|
+
# Determine output path
|
108
|
+
if args.out:
|
109
|
+
output_path = Path(args.out)
|
110
|
+
else:
|
111
|
+
# Default to src/project_paths/project_paths_static.py
|
112
|
+
output_path = None # write_dataclass_file uses default
|
113
|
+
|
114
|
+
# Generate static model
|
115
|
+
if output_path:
|
116
|
+
print(f"Generating static model at: {output_path}")
|
117
|
+
else:
|
118
|
+
print("Generating static model at default location")
|
119
|
+
|
120
|
+
write_dataclass_file(output_path=output_path)
|
121
|
+
print("✅ Static model generated successfully")
|
122
|
+
return 0
|
123
|
+
|
124
|
+
except Exception as e:
|
125
|
+
print(f"Error generating static model: {e}", file=sys.stderr)
|
126
|
+
return 1
|
127
|
+
|
128
|
+
|
129
|
+
def main() -> NoReturn:
|
130
|
+
"""Main entry point for ptool CLI."""
|
131
|
+
parser = argparse.ArgumentParser(
|
132
|
+
prog="ptool", description="Type-safe project path management tool"
|
133
|
+
)
|
134
|
+
|
135
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
136
|
+
|
137
|
+
# print command
|
138
|
+
print_parser = subparsers.add_parser("print", help="Print resolved paths as JSON")
|
139
|
+
print_parser.add_argument(
|
140
|
+
"--source",
|
141
|
+
choices=["config", "pyproject"],
|
142
|
+
default="pyproject",
|
143
|
+
help="Path configuration source (default: pyproject)",
|
144
|
+
)
|
145
|
+
print_parser.add_argument(
|
146
|
+
"--config", type=str, help="Path to .paths config file (default: .paths)"
|
147
|
+
)
|
148
|
+
|
149
|
+
# validate command
|
150
|
+
validate_parser = subparsers.add_parser(
|
151
|
+
"validate", help="Run validators and report results"
|
152
|
+
)
|
153
|
+
validate_parser.add_argument(
|
154
|
+
"--source",
|
155
|
+
choices=["config", "pyproject"],
|
156
|
+
default="pyproject",
|
157
|
+
help="Path configuration source (default: pyproject)",
|
158
|
+
)
|
159
|
+
validate_parser.add_argument(
|
160
|
+
"--config", type=str, help="Path to .paths config file (default: .paths)"
|
161
|
+
)
|
162
|
+
validate_parser.add_argument(
|
163
|
+
"--strict",
|
164
|
+
action="store_true",
|
165
|
+
help="Enable strict validation (check paths exist, no symlinks)",
|
166
|
+
)
|
167
|
+
validate_parser.add_argument(
|
168
|
+
"--raise",
|
169
|
+
dest="raise_on_error",
|
170
|
+
action="store_true",
|
171
|
+
help="Raise exception on validation failure",
|
172
|
+
)
|
173
|
+
|
174
|
+
# gen-static command
|
175
|
+
gen_static_parser = subparsers.add_parser(
|
176
|
+
"gen-static", help="Generate static dataclass file for IDE autocomplete"
|
177
|
+
)
|
178
|
+
gen_static_parser.add_argument(
|
179
|
+
"--out",
|
180
|
+
type=str,
|
181
|
+
help="Output path for static model (default: src/project_paths/project_paths_static.py)",
|
182
|
+
)
|
183
|
+
|
184
|
+
args = parser.parse_args()
|
185
|
+
|
186
|
+
# Dispatch to appropriate command handler
|
187
|
+
if args.command == "print":
|
188
|
+
exit_code = cmd_print(args)
|
189
|
+
elif args.command == "validate":
|
190
|
+
exit_code = cmd_validate(args)
|
191
|
+
elif args.command == "gen-static":
|
192
|
+
exit_code = cmd_gen_static(args)
|
193
|
+
else:
|
194
|
+
parser.print_help()
|
195
|
+
exit_code = 1
|
196
|
+
|
197
|
+
sys.exit(exit_code)
|
198
|
+
|
199
|
+
|
200
|
+
if __name__ == "__main__":
|
201
|
+
main()
|