unitysvc-services 0.1.24__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.
Files changed (37) hide show
  1. unitysvc_services/__init__.py +4 -0
  2. unitysvc_services/api.py +421 -0
  3. unitysvc_services/cli.py +23 -0
  4. unitysvc_services/format_data.py +140 -0
  5. unitysvc_services/interactive_prompt.py +1132 -0
  6. unitysvc_services/list.py +216 -0
  7. unitysvc_services/models/__init__.py +71 -0
  8. unitysvc_services/models/base.py +1375 -0
  9. unitysvc_services/models/listing_data.py +118 -0
  10. unitysvc_services/models/listing_v1.py +56 -0
  11. unitysvc_services/models/provider_data.py +79 -0
  12. unitysvc_services/models/provider_v1.py +54 -0
  13. unitysvc_services/models/seller_data.py +120 -0
  14. unitysvc_services/models/seller_v1.py +42 -0
  15. unitysvc_services/models/service_data.py +114 -0
  16. unitysvc_services/models/service_v1.py +81 -0
  17. unitysvc_services/populate.py +207 -0
  18. unitysvc_services/publisher.py +1628 -0
  19. unitysvc_services/py.typed +0 -0
  20. unitysvc_services/query.py +688 -0
  21. unitysvc_services/scaffold.py +1103 -0
  22. unitysvc_services/schema/base.json +777 -0
  23. unitysvc_services/schema/listing_v1.json +1286 -0
  24. unitysvc_services/schema/provider_v1.json +952 -0
  25. unitysvc_services/schema/seller_v1.json +379 -0
  26. unitysvc_services/schema/service_v1.json +1306 -0
  27. unitysvc_services/test.py +965 -0
  28. unitysvc_services/unpublisher.py +505 -0
  29. unitysvc_services/update.py +287 -0
  30. unitysvc_services/utils.py +533 -0
  31. unitysvc_services/validator.py +731 -0
  32. unitysvc_services-0.1.24.dist-info/METADATA +184 -0
  33. unitysvc_services-0.1.24.dist-info/RECORD +37 -0
  34. unitysvc_services-0.1.24.dist-info/WHEEL +5 -0
  35. unitysvc_services-0.1.24.dist-info/entry_points.txt +3 -0
  36. unitysvc_services-0.1.24.dist-info/licenses/LICENSE +21 -0
  37. unitysvc_services-0.1.24.dist-info/top_level.txt +1 -0
@@ -0,0 +1,207 @@
1
+ """Populate command - populate services by executing provider scripts."""
2
+
3
+ import json
4
+ import os
5
+ import subprocess
6
+ import tomllib
7
+ from pathlib import Path
8
+
9
+ import typer
10
+ from rich.console import Console
11
+
12
+ from .format_data import format_data
13
+ from .utils import find_files_by_schema
14
+
15
+ app = typer.Typer(help="Populate services")
16
+ console = Console()
17
+
18
+
19
+ @app.command()
20
+ def populate(
21
+ data_dir: Path | None = typer.Argument(
22
+ None,
23
+ help="Directory containing provider data files (default: current directory)",
24
+ ),
25
+ provider_name: str | None = typer.Option(
26
+ None,
27
+ "--provider",
28
+ "-p",
29
+ help="Only populate services for a specific provider",
30
+ ),
31
+ dry_run: bool = typer.Option(
32
+ False,
33
+ "--dry-run",
34
+ help="Show what would be executed without actually running commands",
35
+ ),
36
+ ):
37
+ """
38
+ Populate services by executing provider-specific update scripts.
39
+
40
+ This command scans provider files for 'services_populator' configuration and executes
41
+ the specified commands with environment variables from 'provider_access_info'.
42
+
43
+ After successful execution, automatically runs formatting on all generated files to
44
+ ensure they conform to the format specification (equivalent to running 'usvc format').
45
+ """
46
+ # Set data directory
47
+ if data_dir is None:
48
+ data_dir = Path.cwd()
49
+
50
+ if not data_dir.is_absolute():
51
+ data_dir = Path.cwd() / data_dir
52
+
53
+ if not data_dir.exists():
54
+ console.print(f"[red]✗[/red] Data directory not found: {data_dir}", style="bold red")
55
+ raise typer.Exit(code=1)
56
+
57
+ console.print(f"[blue]Scanning for provider configurations in:[/blue] {data_dir}\n")
58
+
59
+ # Find all provider files by schema
60
+ provider_results = find_files_by_schema(data_dir, "provider_v1")
61
+ provider_files = [file_path for file_path, _, _ in provider_results]
62
+
63
+ if not provider_files:
64
+ console.print("[yellow]No provider files found.[/yellow]")
65
+ raise typer.Exit(code=0)
66
+
67
+ console.print(f"[cyan]Found {len(provider_files)} provider file(s)[/cyan]\n")
68
+
69
+ # Process each provider
70
+ total_executed = 0
71
+ total_skipped = 0
72
+ total_failed = 0
73
+
74
+ for provider_file in provider_files:
75
+ try:
76
+ # Load provider configuration
77
+ if provider_file.suffix == ".toml":
78
+ with open(provider_file, "rb") as f:
79
+ provider_config = tomllib.load(f)
80
+ else:
81
+ with open(provider_file) as f:
82
+ provider_config = json.load(f)
83
+
84
+ provider_name_in_file = provider_config.get("name", "unknown")
85
+
86
+ # Skip if provider filter is set and doesn't match
87
+ if provider_name and provider_name_in_file != provider_name:
88
+ console.print(f"[dim]Skipping provider: {provider_name_in_file} (filter: {provider_name})[/dim]")
89
+ total_skipped += 1
90
+ continue
91
+
92
+ # Check if services_populator is configured
93
+ services_populator = provider_config.get("services_populator")
94
+ if not services_populator:
95
+ console.print(f"[yellow]⏭️ Skipping {provider_name_in_file}: No services_populator configured[/yellow]")
96
+ total_skipped += 1
97
+ continue
98
+
99
+ command = services_populator.get("command")
100
+ if not command:
101
+ console.print(
102
+ f"[yellow]⏭️ Skipping {provider_name_in_file}: No command specified in services_populator[/yellow]"
103
+ )
104
+ total_skipped += 1
105
+ continue
106
+
107
+ console.print(f"[bold cyan]Processing provider:[/bold cyan] {provider_name_in_file}")
108
+
109
+ # Prepare environment variables from provider_access_info
110
+ env = os.environ.copy()
111
+ provider_access_info = provider_config.get("provider_access_info", {})
112
+ if provider_access_info:
113
+ for key, value in provider_access_info.items():
114
+ env[key] = str(value)
115
+ console.print(
116
+ f"[dim] Set {len(provider_access_info)} environment variable(s) from provider_access_info[/dim]"
117
+ )
118
+
119
+ # Get the provider directory (parent of provider file)
120
+ provider_dir = provider_file.parent
121
+
122
+ # Build command - handle both string and list formats
123
+ if isinstance(command, str):
124
+ cmd_parts = command.split()
125
+ else:
126
+ cmd_parts = command
127
+
128
+ # Resolve script path relative to provider directory
129
+ script_path = provider_dir / cmd_parts[0]
130
+ if script_path.exists():
131
+ cmd_parts[0] = str(script_path)
132
+
133
+ full_command = ["python3"] + cmd_parts
134
+
135
+ if dry_run:
136
+ console.print("[yellow] [DRY-RUN] Would execute command[/yellow]")
137
+ console.print(f"[yellow] {' '.join(full_command)}[/yellow]")
138
+ console.print(f"[yellow] under {provider_dir}[/yellow]")
139
+ if provider_access_info:
140
+ console.print("[yellow] with:[/yellow]")
141
+ for key, value in provider_access_info.items():
142
+ console.print(f"[yellow] {key}={value}[/yellow]")
143
+ console.print()
144
+ total_skipped += 1
145
+ continue
146
+ else:
147
+ console.print(f"[blue] Command:[/blue] {' '.join(full_command)}")
148
+ console.print(f"[blue] Working directory:[/blue] {provider_dir}")
149
+
150
+ # Execute the command
151
+ try:
152
+ result = subprocess.run(
153
+ full_command,
154
+ cwd=provider_dir,
155
+ env=env,
156
+ capture_output=False,
157
+ text=True,
158
+ )
159
+
160
+ if result.returncode == 0:
161
+ console.print(f"[green]✓[/green] Successfully populated services for {provider_name_in_file}\n")
162
+ total_executed += 1
163
+ else:
164
+ console.print(
165
+ f"[red]✗[/red] Command failed for {provider_name_in_file} (exit code: {result.returncode})\n",
166
+ style="bold red",
167
+ )
168
+ total_failed += 1
169
+
170
+ except subprocess.SubprocessError as e:
171
+ console.print(
172
+ f"[red]✗[/red] Failed to execute command for {provider_name_in_file}: {e}\n",
173
+ style="bold red",
174
+ )
175
+ total_failed += 1
176
+
177
+ except Exception as e:
178
+ console.print(
179
+ f"[red]✗[/red] Error processing {provider_file}: {e}\n",
180
+ style="bold red",
181
+ )
182
+ total_failed += 1
183
+
184
+ # Print summary
185
+ console.print("\n" + "=" * 50)
186
+ console.print("[bold]Populate Services Summary:[/bold]")
187
+ console.print(f" Total providers found: {len(provider_files)}")
188
+ console.print(f" [green]✓ Successfully executed: {total_executed}[/green]")
189
+ console.print(f" [yellow]⏭️ Skipped: {total_skipped}[/yellow]")
190
+ console.print(f" [red]✗ Failed: {total_failed}[/red]")
191
+
192
+ # Format generated files if any populate scripts executed successfully
193
+ if total_executed > 0 and not dry_run:
194
+ console.print("\n" + "=" * 50)
195
+ console.print("[bold cyan]Formatting generated files...[/bold cyan]")
196
+ console.print("[dim]Running automatic formatting to ensure data conforms to format specification[/dim]\n")
197
+
198
+ try:
199
+ # Run format command on the data directory
200
+ format_data(data_dir)
201
+ console.print("\n[green]✓ Formatting completed successfully[/green]")
202
+ except Exception as e:
203
+ console.print(f"\n[yellow]⚠ Warning: Formatting failed: {e}[/yellow]")
204
+ console.print("[dim]You may want to run 'usvc format' manually to fix formatting issues[/dim]")
205
+
206
+ if total_failed > 0:
207
+ raise typer.Exit(code=1)