pltr-cli 0.10.0__py3-none-any.whl → 0.12.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.
- pltr/__init__.py +1 -1
- pltr/cli.py +16 -0
- pltr/commands/admin.py +553 -9
- pltr/commands/aip_agents.py +333 -0
- pltr/commands/connectivity.py +309 -1
- pltr/commands/cp.py +103 -0
- pltr/commands/dataset.py +104 -4
- pltr/commands/mediasets.py +176 -0
- pltr/commands/ontology.py +137 -13
- pltr/commands/orchestration.py +167 -11
- pltr/commands/project.py +249 -0
- pltr/commands/resource.py +452 -0
- pltr/commands/sql.py +54 -7
- pltr/commands/third_party_applications.py +82 -0
- pltr/services/admin.py +318 -1
- pltr/services/aip_agents.py +147 -0
- pltr/services/base.py +104 -1
- pltr/services/connectivity.py +139 -0
- pltr/services/copy.py +391 -0
- pltr/services/dataset.py +80 -9
- pltr/services/mediasets.py +144 -9
- pltr/services/ontology.py +119 -1
- pltr/services/orchestration.py +133 -1
- pltr/services/project.py +136 -0
- pltr/services/resource.py +227 -0
- pltr/services/sql.py +44 -20
- pltr/services/third_party_applications.py +53 -0
- pltr/utils/formatting.py +195 -1
- pltr/utils/pagination.py +325 -0
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/METADATA +5 -3
- pltr_cli-0.12.0.dist-info/RECORD +62 -0
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/WHEEL +1 -1
- pltr_cli-0.10.0.dist-info/RECORD +0 -55
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/entry_points.txt +0 -0
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/licenses/LICENSE +0 -0
pltr/utils/formatting.py
CHANGED
|
@@ -4,7 +4,7 @@ Output formatting utilities for CLI commands.
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import csv
|
|
7
|
-
from typing import Any, Dict, List, Optional, Union
|
|
7
|
+
from typing import Any, Dict, List, Optional, Union, Callable
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from io import StringIO
|
|
10
10
|
|
|
@@ -12,6 +12,12 @@ from rich.console import Console
|
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
from rich import print as rich_print
|
|
14
14
|
|
|
15
|
+
# Type checking import to avoid circular dependencies
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
pass
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
class OutputFormatter:
|
|
17
23
|
"""Handles different output formats for CLI commands."""
|
|
@@ -460,6 +466,118 @@ class OutputFormatter:
|
|
|
460
466
|
else:
|
|
461
467
|
f.write(str(data))
|
|
462
468
|
|
|
469
|
+
def format_paginated_output(
|
|
470
|
+
self,
|
|
471
|
+
result: Any, # PaginationResult
|
|
472
|
+
format_type: str = "table",
|
|
473
|
+
output_file: Optional[str] = None,
|
|
474
|
+
formatter_fn: Optional[Callable] = None,
|
|
475
|
+
) -> Optional[str]:
|
|
476
|
+
"""
|
|
477
|
+
Format paginated results with metadata.
|
|
478
|
+
|
|
479
|
+
This method handles display of paginated data and automatically
|
|
480
|
+
includes pagination information based on the output format.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
result: PaginationResult object with .data and .metadata attributes
|
|
484
|
+
format_type: Output format ('table', 'json', 'csv')
|
|
485
|
+
output_file: Optional output file path
|
|
486
|
+
formatter_fn: Optional custom formatter function for the data
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
Formatted string if no output file specified
|
|
490
|
+
|
|
491
|
+
Example:
|
|
492
|
+
>>> result = PaginationResult(data=[...], metadata=metadata)
|
|
493
|
+
>>> formatter.format_paginated_output(result, "json")
|
|
494
|
+
"""
|
|
495
|
+
# Extract data and metadata
|
|
496
|
+
data = result.data if hasattr(result, "data") else result
|
|
497
|
+
metadata = result.metadata if hasattr(result, "metadata") else None
|
|
498
|
+
|
|
499
|
+
# JSON format: include pagination metadata in output
|
|
500
|
+
if format_type == "json":
|
|
501
|
+
if metadata:
|
|
502
|
+
output_data = {
|
|
503
|
+
"data": data,
|
|
504
|
+
"pagination": {
|
|
505
|
+
"page": metadata.current_page,
|
|
506
|
+
"items_count": metadata.items_fetched,
|
|
507
|
+
"has_more": metadata.has_more,
|
|
508
|
+
"total_pages_fetched": metadata.total_pages_fetched,
|
|
509
|
+
},
|
|
510
|
+
}
|
|
511
|
+
# Include next_page_token if available
|
|
512
|
+
if metadata.next_page_token:
|
|
513
|
+
output_data["pagination"]["next_page_token"] = (
|
|
514
|
+
metadata.next_page_token
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
return self._format_json(output_data, output_file)
|
|
518
|
+
else:
|
|
519
|
+
# No metadata, format data directly
|
|
520
|
+
return self._format_json(data, output_file)
|
|
521
|
+
|
|
522
|
+
# Table/CSV format: format data normally, then print pagination info
|
|
523
|
+
else:
|
|
524
|
+
# Format the data using custom formatter or default
|
|
525
|
+
if formatter_fn:
|
|
526
|
+
formatted_result = formatter_fn(data, format_type, output_file)
|
|
527
|
+
else:
|
|
528
|
+
formatted_result = self.format_output(data, format_type, output_file)
|
|
529
|
+
|
|
530
|
+
# Print pagination info to console (even when saving to file)
|
|
531
|
+
# For CSV/table formats, pagination metadata is shown on console
|
|
532
|
+
# while data is written to file
|
|
533
|
+
if metadata:
|
|
534
|
+
self.print_pagination_info(metadata)
|
|
535
|
+
|
|
536
|
+
return formatted_result
|
|
537
|
+
|
|
538
|
+
def print_pagination_info(self, metadata: Any) -> None: # PaginationMetadata
|
|
539
|
+
"""
|
|
540
|
+
Print pagination information to the console.
|
|
541
|
+
|
|
542
|
+
This provides users with helpful information about the current
|
|
543
|
+
pagination state and how to fetch more data.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
metadata: PaginationMetadata object
|
|
547
|
+
|
|
548
|
+
Example output:
|
|
549
|
+
Fetched 20 items (page 1)
|
|
550
|
+
Next page: --page-token abc123
|
|
551
|
+
Fetch all: Add --all flag
|
|
552
|
+
"""
|
|
553
|
+
if not metadata:
|
|
554
|
+
return
|
|
555
|
+
|
|
556
|
+
# Build info message
|
|
557
|
+
info_lines = []
|
|
558
|
+
|
|
559
|
+
# Current state
|
|
560
|
+
info_lines.append(
|
|
561
|
+
f"Fetched {metadata.items_fetched} items (page {metadata.current_page})"
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# Next steps if more data available
|
|
565
|
+
if metadata.has_more:
|
|
566
|
+
if metadata.next_page_token:
|
|
567
|
+
info_lines.append(f"Next page: --page-token {metadata.next_page_token}")
|
|
568
|
+
else:
|
|
569
|
+
# Iterator pattern without explicit token
|
|
570
|
+
info_lines.append(
|
|
571
|
+
f"Next page: Use --max-pages {metadata.current_page + 1}"
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
info_lines.append("Fetch all: Add --all flag")
|
|
575
|
+
else:
|
|
576
|
+
info_lines.append("No more pages available")
|
|
577
|
+
|
|
578
|
+
# Print as info message
|
|
579
|
+
self.print_info("\n".join(info_lines))
|
|
580
|
+
|
|
463
581
|
def format_sql_results(
|
|
464
582
|
self,
|
|
465
583
|
results: Any,
|
|
@@ -826,6 +944,38 @@ class OutputFormatter:
|
|
|
826
944
|
|
|
827
945
|
return self.format_output(formatted_schedules, format_type, output_file)
|
|
828
946
|
|
|
947
|
+
def format_schedule_runs_list(
|
|
948
|
+
self,
|
|
949
|
+
runs: List[Dict[str, Any]],
|
|
950
|
+
format_type: str = "table",
|
|
951
|
+
output_file: Optional[str] = None,
|
|
952
|
+
) -> Optional[str]:
|
|
953
|
+
"""
|
|
954
|
+
Format list of schedule runs.
|
|
955
|
+
|
|
956
|
+
Args:
|
|
957
|
+
runs: List of schedule run dictionaries
|
|
958
|
+
format_type: Output format
|
|
959
|
+
output_file: Optional output file path
|
|
960
|
+
|
|
961
|
+
Returns:
|
|
962
|
+
Formatted string if no output file specified
|
|
963
|
+
"""
|
|
964
|
+
formatted_runs = []
|
|
965
|
+
for run in runs:
|
|
966
|
+
build_rid = run.get("build_rid", "")
|
|
967
|
+
formatted_run = {
|
|
968
|
+
"RID": run.get("rid", ""),
|
|
969
|
+
"Status": run.get("status", ""),
|
|
970
|
+
"Started": self._format_datetime(run.get("started_time")),
|
|
971
|
+
"Finished": self._format_datetime(run.get("finished_time")),
|
|
972
|
+
"Build": build_rid[:40] + "..." if len(build_rid) > 40 else build_rid,
|
|
973
|
+
"Result": run.get("result", ""),
|
|
974
|
+
}
|
|
975
|
+
formatted_runs.append(formatted_run)
|
|
976
|
+
|
|
977
|
+
return self.format_output(formatted_runs, format_type, output_file)
|
|
978
|
+
|
|
829
979
|
# MediaSets formatting methods
|
|
830
980
|
|
|
831
981
|
def format_media_item_info(
|
|
@@ -953,6 +1103,50 @@ class OutputFormatter:
|
|
|
953
1103
|
else:
|
|
954
1104
|
return self.format_output(reference, format_type, output_file)
|
|
955
1105
|
|
|
1106
|
+
def format_thumbnail_status(
|
|
1107
|
+
self,
|
|
1108
|
+
status: Dict[str, Any],
|
|
1109
|
+
format_type: str = "table",
|
|
1110
|
+
output_file: Optional[str] = None,
|
|
1111
|
+
) -> Optional[str]:
|
|
1112
|
+
"""
|
|
1113
|
+
Format thumbnail calculation status for display.
|
|
1114
|
+
|
|
1115
|
+
Args:
|
|
1116
|
+
status: Thumbnail status dictionary
|
|
1117
|
+
format_type: Output format
|
|
1118
|
+
output_file: Optional output file path
|
|
1119
|
+
|
|
1120
|
+
Returns:
|
|
1121
|
+
Formatted string if no output file specified
|
|
1122
|
+
"""
|
|
1123
|
+
if format_type == "table":
|
|
1124
|
+
details = []
|
|
1125
|
+
|
|
1126
|
+
property_order = [
|
|
1127
|
+
("status", "Status"),
|
|
1128
|
+
("transformation_id", "Transformation ID"),
|
|
1129
|
+
("media_item_rid", "Media Item RID"),
|
|
1130
|
+
]
|
|
1131
|
+
|
|
1132
|
+
for key, label in property_order:
|
|
1133
|
+
if status.get(key) is not None:
|
|
1134
|
+
details.append({"Property": label, "Value": str(status[key])})
|
|
1135
|
+
|
|
1136
|
+
# Add any remaining properties
|
|
1137
|
+
for key, value in status.items():
|
|
1138
|
+
if (
|
|
1139
|
+
key not in [prop[0] for prop in property_order]
|
|
1140
|
+
and value is not None
|
|
1141
|
+
):
|
|
1142
|
+
details.append(
|
|
1143
|
+
{"Property": key.replace("_", " ").title(), "Value": str(value)}
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
return self.format_output(details, format_type, output_file)
|
|
1147
|
+
else:
|
|
1148
|
+
return self.format_output(status, format_type, output_file)
|
|
1149
|
+
|
|
956
1150
|
# Dataset formatting methods
|
|
957
1151
|
|
|
958
1152
|
def format_branches(
|
pltr/utils/pagination.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pagination utilities for handling large result sets.
|
|
3
|
+
|
|
4
|
+
This module provides a unified interface for pagination across different
|
|
5
|
+
SDK patterns used by the Foundry platform.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class PaginationConfig:
|
|
14
|
+
"""
|
|
15
|
+
Configuration for pagination behavior.
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
page_size: Number of items per page (None = use service default)
|
|
19
|
+
max_pages: Maximum number of pages to fetch (None = fetch all)
|
|
20
|
+
page_token: Token to resume from a specific page
|
|
21
|
+
fetch_all: If True, overrides max_pages to fetch all available pages
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
page_size: Optional[int] = None
|
|
25
|
+
max_pages: Optional[int] = 1 # Default: fetch single page
|
|
26
|
+
page_token: Optional[str] = None
|
|
27
|
+
fetch_all: bool = False
|
|
28
|
+
|
|
29
|
+
def should_show_progress(self) -> bool:
|
|
30
|
+
"""
|
|
31
|
+
Determine if progress tracking should be shown.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
True if fetching multiple pages with known max_pages
|
|
35
|
+
"""
|
|
36
|
+
return self.max_pages is not None and self.max_pages > 1 and not self.fetch_all
|
|
37
|
+
|
|
38
|
+
def effective_max_pages(self) -> Optional[int]:
|
|
39
|
+
"""
|
|
40
|
+
Get the effective maximum pages to fetch.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
None if fetch_all is True, otherwise max_pages value
|
|
44
|
+
"""
|
|
45
|
+
return None if self.fetch_all else self.max_pages
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class PaginationMetadata:
|
|
50
|
+
"""
|
|
51
|
+
Metadata about pagination state.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
current_page: Current page number (1-indexed)
|
|
55
|
+
items_fetched: Total number of items fetched
|
|
56
|
+
next_page_token: Token for fetching the next page (if available)
|
|
57
|
+
has_more: Whether more pages are available
|
|
58
|
+
total_pages_fetched: Total number of pages fetched so far
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
current_page: int = 1
|
|
62
|
+
items_fetched: int = 0
|
|
63
|
+
next_page_token: Optional[str] = None
|
|
64
|
+
has_more: bool = False
|
|
65
|
+
total_pages_fetched: int = 0
|
|
66
|
+
|
|
67
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Convert metadata to dictionary for JSON serialization.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Dictionary representation of metadata
|
|
73
|
+
"""
|
|
74
|
+
result: Dict[str, Any] = {
|
|
75
|
+
"page": self.current_page,
|
|
76
|
+
"items_count": self.items_fetched,
|
|
77
|
+
"has_more": self.has_more,
|
|
78
|
+
"total_pages_fetched": self.total_pages_fetched,
|
|
79
|
+
}
|
|
80
|
+
if self.next_page_token:
|
|
81
|
+
result["next_page_token"] = self.next_page_token
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class PaginationResult:
|
|
87
|
+
"""
|
|
88
|
+
Wrapper for paginated results with metadata.
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
data: List of items fetched
|
|
92
|
+
metadata: Pagination metadata
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
data: List[Any] = field(default_factory=list)
|
|
96
|
+
metadata: PaginationMetadata = field(default_factory=PaginationMetadata)
|
|
97
|
+
|
|
98
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
99
|
+
"""
|
|
100
|
+
Convert result to dictionary for JSON serialization.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dictionary with data and pagination metadata
|
|
104
|
+
"""
|
|
105
|
+
return {
|
|
106
|
+
"data": self.data,
|
|
107
|
+
"pagination": self.metadata.to_dict(),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class ResponsePaginationHandler:
|
|
112
|
+
"""
|
|
113
|
+
Handler for SDK Pattern B: Response-based pagination.
|
|
114
|
+
|
|
115
|
+
This handler works with SDK methods that return response objects
|
|
116
|
+
with explicit `.data` and `.next_page_token` attributes.
|
|
117
|
+
|
|
118
|
+
Example SDK methods:
|
|
119
|
+
- admin.User.list()
|
|
120
|
+
- orchestration.Build.search()
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def collect_pages(
|
|
124
|
+
self,
|
|
125
|
+
fetch_fn: Callable[[Optional[str]], Dict[str, Any]],
|
|
126
|
+
config: PaginationConfig,
|
|
127
|
+
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
128
|
+
) -> PaginationResult:
|
|
129
|
+
"""
|
|
130
|
+
Collect pages using a fetch function.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
fetch_fn: Function that accepts a page_token and returns a dict
|
|
134
|
+
with 'data' and 'next_page_token' keys
|
|
135
|
+
config: Pagination configuration
|
|
136
|
+
progress_callback: Optional callback(page_num, items_count)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
PaginationResult with collected items and metadata
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> def fetch(token):
|
|
143
|
+
... response = service.list(page_token=token)
|
|
144
|
+
... return {"data": response.data, "next_page_token": response.next_page_token}
|
|
145
|
+
>>> handler = ResponsePaginationHandler()
|
|
146
|
+
>>> result = handler.collect_pages(fetch, config)
|
|
147
|
+
"""
|
|
148
|
+
all_items: List[Any] = []
|
|
149
|
+
page_num = 0
|
|
150
|
+
current_token = config.page_token
|
|
151
|
+
max_pages = config.effective_max_pages()
|
|
152
|
+
|
|
153
|
+
while True:
|
|
154
|
+
try:
|
|
155
|
+
# Fetch the current page
|
|
156
|
+
response = fetch_fn(current_token)
|
|
157
|
+
page_data = response.get("data", [])
|
|
158
|
+
next_token = response.get("next_page_token")
|
|
159
|
+
|
|
160
|
+
# Add items from this page
|
|
161
|
+
all_items.extend(page_data)
|
|
162
|
+
page_num += 1
|
|
163
|
+
|
|
164
|
+
# Update progress
|
|
165
|
+
if progress_callback:
|
|
166
|
+
progress_callback(page_num, len(all_items))
|
|
167
|
+
|
|
168
|
+
# Check if we should continue
|
|
169
|
+
has_more = next_token is not None
|
|
170
|
+
should_stop = (
|
|
171
|
+
not has_more # No more pages
|
|
172
|
+
or (
|
|
173
|
+
max_pages is not None and page_num >= max_pages
|
|
174
|
+
) # Reached max pages
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if should_stop:
|
|
178
|
+
metadata = PaginationMetadata(
|
|
179
|
+
current_page=page_num,
|
|
180
|
+
items_fetched=len(all_items),
|
|
181
|
+
next_page_token=next_token,
|
|
182
|
+
has_more=has_more,
|
|
183
|
+
total_pages_fetched=page_num,
|
|
184
|
+
)
|
|
185
|
+
return PaginationResult(data=all_items, metadata=metadata)
|
|
186
|
+
|
|
187
|
+
# Continue to next page
|
|
188
|
+
current_token = next_token
|
|
189
|
+
|
|
190
|
+
except Exception as e:
|
|
191
|
+
# Error occurred while fetching - return partial results
|
|
192
|
+
# Include the error information in metadata for user awareness
|
|
193
|
+
if all_items:
|
|
194
|
+
# We have partial results - return what we got so far
|
|
195
|
+
import sys
|
|
196
|
+
|
|
197
|
+
print(
|
|
198
|
+
f"\nWarning: Error fetching page {page_num + 1}: {e}",
|
|
199
|
+
file=sys.stderr,
|
|
200
|
+
)
|
|
201
|
+
print(
|
|
202
|
+
f"Returning partial results ({len(all_items)} items from {page_num} pages)",
|
|
203
|
+
file=sys.stderr,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
metadata = PaginationMetadata(
|
|
207
|
+
current_page=page_num,
|
|
208
|
+
items_fetched=len(all_items),
|
|
209
|
+
next_page_token=current_token, # Token for retry
|
|
210
|
+
has_more=True, # Assume more pages exist
|
|
211
|
+
total_pages_fetched=page_num,
|
|
212
|
+
)
|
|
213
|
+
return PaginationResult(data=all_items, metadata=metadata)
|
|
214
|
+
else:
|
|
215
|
+
# No data fetched yet - re-raise the error
|
|
216
|
+
raise
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class IteratorPaginationHandler:
|
|
220
|
+
"""
|
|
221
|
+
Handler for SDK Pattern A: Iterator-based pagination.
|
|
222
|
+
|
|
223
|
+
This handler works with SDK methods that return ResourceIterator instances
|
|
224
|
+
that automatically paginate internally and expose next_page_token.
|
|
225
|
+
|
|
226
|
+
Example SDK methods:
|
|
227
|
+
- ontology.OntologyObject.list()
|
|
228
|
+
- Dataset.File.list()
|
|
229
|
+
|
|
230
|
+
Note: The SDK's ResourceIterator exposes .next_page_token and .data properties
|
|
231
|
+
which we leverage for proper pagination support.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def collect_pages(
|
|
235
|
+
self,
|
|
236
|
+
iterator: Any, # ResourceIterator from SDK
|
|
237
|
+
config: PaginationConfig,
|
|
238
|
+
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
239
|
+
) -> PaginationResult:
|
|
240
|
+
"""
|
|
241
|
+
Collect pages from a ResourceIterator using SDK's pagination.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
iterator: ResourceIterator instance from SDK (has .next_page_token property)
|
|
245
|
+
config: Pagination configuration
|
|
246
|
+
progress_callback: Optional callback(page_num, items_count)
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
PaginationResult with collected items and metadata
|
|
250
|
+
|
|
251
|
+
Note:
|
|
252
|
+
This leverages the SDK's ResourceIterator.next_page_token property
|
|
253
|
+
for proper token-based pagination and resume capability.
|
|
254
|
+
"""
|
|
255
|
+
all_items: List[Any] = []
|
|
256
|
+
page_num = 0
|
|
257
|
+
max_pages = config.effective_max_pages()
|
|
258
|
+
next_token = None
|
|
259
|
+
has_more = False
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
# Collect items from the iterator
|
|
263
|
+
for item in iterator:
|
|
264
|
+
all_items.append(item)
|
|
265
|
+
|
|
266
|
+
# Check if we've completed a "page" worth of items
|
|
267
|
+
page_size = config.page_size or 20 # Default page size
|
|
268
|
+
if len(all_items) % page_size == 0:
|
|
269
|
+
page_num += 1
|
|
270
|
+
|
|
271
|
+
# Update progress
|
|
272
|
+
if progress_callback:
|
|
273
|
+
progress_callback(page_num, len(all_items))
|
|
274
|
+
|
|
275
|
+
# Check if we should stop
|
|
276
|
+
if max_pages is not None and page_num >= max_pages:
|
|
277
|
+
# Capture next_page_token BEFORE breaking while iterator state is known
|
|
278
|
+
if hasattr(iterator, "next_page_token"):
|
|
279
|
+
next_token = iterator.next_page_token
|
|
280
|
+
has_more = next_token is not None
|
|
281
|
+
break
|
|
282
|
+
|
|
283
|
+
# Calculate final page number if items don't align with page_size
|
|
284
|
+
if len(all_items) % page_size != 0:
|
|
285
|
+
page_num += 1
|
|
286
|
+
if progress_callback:
|
|
287
|
+
progress_callback(page_num, len(all_items))
|
|
288
|
+
|
|
289
|
+
# If we didn't break early (exhausted iterator), check for next_page_token
|
|
290
|
+
if next_token is None and hasattr(iterator, "next_page_token"):
|
|
291
|
+
next_token = iterator.next_page_token
|
|
292
|
+
has_more = next_token is not None
|
|
293
|
+
|
|
294
|
+
except Exception as e:
|
|
295
|
+
# Error occurred during iteration - return partial results
|
|
296
|
+
if all_items:
|
|
297
|
+
# We have partial results
|
|
298
|
+
import sys
|
|
299
|
+
|
|
300
|
+
print(
|
|
301
|
+
f"\nWarning: Error during iteration: {e}",
|
|
302
|
+
file=sys.stderr,
|
|
303
|
+
)
|
|
304
|
+
print(
|
|
305
|
+
f"Returning partial results ({len(all_items)} items)",
|
|
306
|
+
file=sys.stderr,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Try to get next_page_token if available
|
|
310
|
+
if hasattr(iterator, "next_page_token"):
|
|
311
|
+
next_token = iterator.next_page_token
|
|
312
|
+
has_more = next_token is not None
|
|
313
|
+
else:
|
|
314
|
+
# No data fetched yet - re-raise the error
|
|
315
|
+
raise
|
|
316
|
+
|
|
317
|
+
metadata = PaginationMetadata(
|
|
318
|
+
current_page=page_num,
|
|
319
|
+
items_fetched=len(all_items),
|
|
320
|
+
next_page_token=next_token,
|
|
321
|
+
has_more=has_more,
|
|
322
|
+
total_pages_fetched=page_num,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return PaginationResult(data=all_items, metadata=metadata)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pltr-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Command-line interface for Palantir Foundry APIs
|
|
5
5
|
Project-URL: Homepage, https://github.com/anjor/pltr-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/anjor/pltr-cli
|
|
@@ -29,6 +29,7 @@ Requires-Python: >=3.9
|
|
|
29
29
|
Requires-Dist: click-repl>=0.3.0
|
|
30
30
|
Requires-Dist: foundry-platform-sdk>=1.27.0
|
|
31
31
|
Requires-Dist: keyring>=25.6.0
|
|
32
|
+
Requires-Dist: pandas>=2.0.0
|
|
32
33
|
Requires-Dist: python-dotenv>=1.1.1
|
|
33
34
|
Requires-Dist: requests>=2.32.4
|
|
34
35
|
Requires-Dist: rich>=14.1.0
|
|
@@ -37,7 +38,7 @@ Description-Content-Type: text/markdown
|
|
|
37
38
|
|
|
38
39
|
# pltr-cli
|
|
39
40
|
|
|
40
|
-
A comprehensive command-line interface for Palantir Foundry APIs, providing
|
|
41
|
+
A comprehensive command-line interface for Palantir Foundry APIs, providing 81+ commands for data analysis, dataset management, ontology operations, orchestration, SQL queries, folder management, and administrative tasks.
|
|
41
42
|
|
|
42
43
|
## Overview
|
|
43
44
|
|
|
@@ -215,6 +216,7 @@ pltr connectivity import table <conn-rid> <table-name> <dataset-rid> --execute
|
|
|
215
216
|
# Administrative
|
|
216
217
|
pltr admin user current # Current user info
|
|
217
218
|
pltr admin user list # List users
|
|
219
|
+
pltr third-party-apps get <rid> # Get third-party application details
|
|
218
220
|
|
|
219
221
|
# Interactive & Tools
|
|
220
222
|
pltr shell # Interactive mode
|
|
@@ -470,7 +472,7 @@ See **[API Wrapper Documentation](docs/api/wrapper.md)** for detailed architectu
|
|
|
470
472
|
|
|
471
473
|
pltr-cli is **production-ready** with comprehensive features:
|
|
472
474
|
|
|
473
|
-
- ✅ **
|
|
475
|
+
- ✅ **81+ Commands** across 11 command groups
|
|
474
476
|
- ✅ **273 Unit Tests** with 67% code coverage
|
|
475
477
|
- ✅ **Published on PyPI** with automated releases
|
|
476
478
|
- ✅ **Cross-Platform** support (Windows, macOS, Linux)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
pltr/__init__.py,sha256=eHjt9DPsMbptabS2yGx9Yhbyzq5hFSUHXb7zc8Q_8-o,23
|
|
2
|
+
pltr/__main__.py,sha256=HWJ49UoAYBQCf8kjuySPmBTuUjTZrOx-y6PzMTyS1KE,879
|
|
3
|
+
pltr/cli.py,sha256=jyUbgOzqJkbZUr1NcnJDSXIhBFG7WMn-8q8TYoQTDzU,3085
|
|
4
|
+
pltr/auth/__init__.py,sha256=G0V-Rh25FaJsH2nhrf146XQQG_ApdbyPJNuHJC25kgk,38
|
|
5
|
+
pltr/auth/base.py,sha256=LvmCwS7A0q0CITcym8udPzdACL52_jSGusiaeCTOaE8,981
|
|
6
|
+
pltr/auth/manager.py,sha256=ZqlGefr1a8MGx0g7kkQhpmiuVp0XTg3f43yMBCk-IRo,4305
|
|
7
|
+
pltr/auth/oauth.py,sha256=uTl5T3MSPlq8Jb3c45hib0vj-GQoyLxmS_NbnKez5dI,2844
|
|
8
|
+
pltr/auth/storage.py,sha256=C7I3-22CJcnrKGNdxk9nXjphsnqQVguT5gNfAnR78Ok,2474
|
|
9
|
+
pltr/auth/token.py,sha256=V48kxGn7CFbNGo17er5oI_ZA3xJ3iS9TsFjphZYqS2s,1925
|
|
10
|
+
pltr/commands/__init__.py,sha256=iOLJ1ql4mz-3-4nz21hAqjd-9IjpYAIxr9SJQKHNFxM,29
|
|
11
|
+
pltr/commands/admin.py,sha256=2E38LAFP5O1bTFzBASd-7IO7NAWPRoIkWm2nOjEczq8,34998
|
|
12
|
+
pltr/commands/aip_agents.py,sha256=vPFRNmj6hR8sbedaIjgb77T-b4-cmodCwV4Z2m8T5NE,10324
|
|
13
|
+
pltr/commands/alias.py,sha256=r9xMsQNrGvaixSlspzoO2IXQ44LFXuZM4itt8vC0dRc,6862
|
|
14
|
+
pltr/commands/completion.py,sha256=YTxaRL4-rDs5n7aXf3ogFsxbHVJUBo_HiBbd0fbBPZ0,10870
|
|
15
|
+
pltr/commands/configure.py,sha256=oYj-VlOEj3MDwtB2RC4bYOYzI_sXTanPnz7y1GmMTqY,4800
|
|
16
|
+
pltr/commands/connectivity.py,sha256=6oqKjEx0a2vLGIXhDrwRXVP5ATa36QQQ7dNOvXrqM-8,25442
|
|
17
|
+
pltr/commands/cp.py,sha256=yYUJ9gf_px2NvbmDEFtWkhHM96ddzL5-2pJUTSW0dIg,3050
|
|
18
|
+
pltr/commands/dataset.py,sha256=TEqyALNgSO1jOX6RJvOHkvsvfC-oYL_94__q0fd4gIg,56693
|
|
19
|
+
pltr/commands/folder.py,sha256=ItUp49lyDWIoMv8RaNDo7JFyrlk-r_Klab9FCPXwUM8,10721
|
|
20
|
+
pltr/commands/mediasets.py,sha256=u_Ju3vsWl8ZVQvKxArk9pFQJVVrr9eua1X0PTaDxDwk,21832
|
|
21
|
+
pltr/commands/ontology.py,sha256=ArDdlYdYy9CnPQWCMKHbwWYmWKFx48jul224urQDENM,22956
|
|
22
|
+
pltr/commands/orchestration.py,sha256=qQxSdG9p76dj2FgYxv8IY-D6WrSDxNJik-vYVkDWARM,28483
|
|
23
|
+
pltr/commands/project.py,sha256=jFwsA9LzYVuOEPGRjULOPnv7E0DgxXIy4uSmE-pKqU4,22662
|
|
24
|
+
pltr/commands/resource.py,sha256=NxLVspn4PaBMNIXHM3yhmOMavxods5-Ehe_uqR3Y7lo,33837
|
|
25
|
+
pltr/commands/resource_role.py,sha256=pM0DQxLBU9xyIYzLv1Y0sOMZG5oZ1INNSkMubYBGHJM,15394
|
|
26
|
+
pltr/commands/shell.py,sha256=QLF7TEGpaau9i0A9s3VjnegvtHde-SLRqI4suJFT4WI,3622
|
|
27
|
+
pltr/commands/space.py,sha256=R9TN9OQVDtFB92DOjrh81_YYajiQaqRNELsBHK4O-pI,21944
|
|
28
|
+
pltr/commands/sql.py,sha256=Nn3fyrqS11LxWV_w0V73Wvv5dxHATPBKEPQHlS8HMvA,14148
|
|
29
|
+
pltr/commands/third_party_applications.py,sha256=0fGSx7anaO4sVxE8-I8lQlOqdRO5J46CjQ3ABjAdI-8,2508
|
|
30
|
+
pltr/commands/verify.py,sha256=n8LWhbfGieYa-a5_l3MxqkYbdpyVf8-i0FQIL__AaPA,6650
|
|
31
|
+
pltr/config/__init__.py,sha256=Y6gARy5lUHy-OJaOUxtfXoeQVNZV5QHLl6uKHQ8tpTk,41
|
|
32
|
+
pltr/config/aliases.py,sha256=ZmesZWMfa5riZlVe3fyC7EI3uIzxEGsDHz-8shOpIbM,6947
|
|
33
|
+
pltr/config/profiles.py,sha256=XMUIp6Ez5LNC6rGXZe2JLH7IKepXhARtuc8ASUA9FYA,3431
|
|
34
|
+
pltr/config/settings.py,sha256=bfIiosPqH_W73TOHS71DvgZdAHka4fJDopU1SvBRFuQ,2908
|
|
35
|
+
pltr/services/__init__.py,sha256=zQpgrqPdAkZI-nobi33mctU2-iGNgazzvjBVY8YRbSQ,101
|
|
36
|
+
pltr/services/admin.py,sha256=0nxQEYYY0H3dCica0_gSdhdeZR6v04Z46LuvX22cYuE,20650
|
|
37
|
+
pltr/services/aip_agents.py,sha256=fyzJPXcaIAPwWvDDtwR53fpDTDfSUD0DEJvieaOzOpQ,5218
|
|
38
|
+
pltr/services/base.py,sha256=R64ao_k-HihjW-WugDsJlo0Z4sV3aBGe0v2TyN4FwvM,7266
|
|
39
|
+
pltr/services/connectivity.py,sha256=0KNoZJFms5R3-zHiWlCSQe5aeBWlxIiwIxpOJw7Y-dI,15107
|
|
40
|
+
pltr/services/copy.py,sha256=bJEQYVAiouRMiz25QU3ET7Q6g4oXIqeTsuyrbwQpuxs,15081
|
|
41
|
+
pltr/services/dataset.py,sha256=EkMJQm-opqchbg6b_pLHS8otdTi0wli7ANa3eHSZD2A,47560
|
|
42
|
+
pltr/services/folder.py,sha256=mWElyvn-wXPB5sv8Ik_dLeW5JM6jZg3g9KKBk6UcrlQ,5389
|
|
43
|
+
pltr/services/mediasets.py,sha256=dNUGVLb50WMOXq06_A6UIrELRa23c4yfjlzpF1vGD8w,14499
|
|
44
|
+
pltr/services/ontology.py,sha256=1E3ejSansyyQRorvws1eeKpbYMBVIOrvEWdVNoqrovI,18652
|
|
45
|
+
pltr/services/orchestration.py,sha256=C-jEW2Q2Mggpdff_WGdnAaRoytj5XyX7n5pEAAJKOfQ,19270
|
|
46
|
+
pltr/services/project.py,sha256=kiY1hRqeFouDhIXubHwtz2ZClB94PhCLJryD1FJARhY,12504
|
|
47
|
+
pltr/services/resource.py,sha256=hUlYRIwZvgp7V-o50QJDXcVE2aGvq7XHaURXWYm6RuU,17712
|
|
48
|
+
pltr/services/resource_role.py,sha256=Ootor16c6PR9TNxe6KJyd4W2lYM_HHDxJk-JvZhgRxU,10608
|
|
49
|
+
pltr/services/space.py,sha256=4uea1nQ6CA-6_xWoD6n49E4Zm6KbW_7Cq9o89JorMTE,11544
|
|
50
|
+
pltr/services/sql.py,sha256=sVB7A-vp3QdrdFcFCX3e3eGsEwaxcmYS3qwDNDMx1oY,12509
|
|
51
|
+
pltr/services/third_party_applications.py,sha256=v-V4CQl371DhSI0l-WMVf-vxfXWddwjS7_DE2IRtad4,1966
|
|
52
|
+
pltr/utils/__init__.py,sha256=DF7TigL1XbKVGM5VjgU8_8AGIszofkdO80oCzLGGnTE,38
|
|
53
|
+
pltr/utils/alias_resolver.py,sha256=DIF7P1UnUU8kqocJfIDEWjYq4s8_0KfqRZBbECeZEh8,1539
|
|
54
|
+
pltr/utils/completion.py,sha256=bjeqjleEfB2YcQFpcxvF0GoQ763F6KBbULSZC4FWY_g,4980
|
|
55
|
+
pltr/utils/formatting.py,sha256=CE0eVojkLIBfMevBDt4BKN_jyIgdLjNSNhpba3gRJGE,57502
|
|
56
|
+
pltr/utils/pagination.py,sha256=BgJ-AOAQQ1grF05lB8Lz3Pi5B8c_boEsa9WQo84Uako,11219
|
|
57
|
+
pltr/utils/progress.py,sha256=BKYbiLO61uhQbibabU7pxvvbAWMRLRmqk4pZldBQK_g,9053
|
|
58
|
+
pltr_cli-0.12.0.dist-info/METADATA,sha256=wFYEN6U4kEnUfjzuHxPGQc0AX6htysCfUe-4Ncirpv4,17815
|
|
59
|
+
pltr_cli-0.12.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
60
|
+
pltr_cli-0.12.0.dist-info/entry_points.txt,sha256=8tvEcW04kA_oAE2Dwwu-Og9efjl4ESJvs4AzlP2KBdQ,38
|
|
61
|
+
pltr_cli-0.12.0.dist-info/licenses/LICENSE,sha256=6VUFd_ytnOBD2O1tmkKrA-smigi9QEhYr_tge4h4z8Y,1070
|
|
62
|
+
pltr_cli-0.12.0.dist-info/RECORD,,
|