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.
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()