hippius 0.2.2__py3-none-any.whl → 0.2.4__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.
- {hippius-0.2.2.dist-info → hippius-0.2.4.dist-info}/METADATA +213 -156
- hippius-0.2.4.dist-info/RECORD +16 -0
- hippius_sdk/__init__.py +10 -21
- hippius_sdk/cli.py +282 -2627
- hippius_sdk/cli_assets.py +10 -0
- hippius_sdk/cli_handlers.py +2773 -0
- hippius_sdk/cli_parser.py +607 -0
- hippius_sdk/cli_rich.py +247 -0
- hippius_sdk/client.py +70 -22
- hippius_sdk/config.py +109 -142
- hippius_sdk/ipfs.py +435 -58
- hippius_sdk/ipfs_core.py +22 -1
- hippius_sdk/substrate.py +234 -553
- hippius_sdk/utils.py +84 -2
- hippius-0.2.2.dist-info/RECORD +0 -12
- {hippius-0.2.2.dist-info → hippius-0.2.4.dist-info}/WHEEL +0 -0
- {hippius-0.2.2.dist-info → hippius-0.2.4.dist-info}/entry_points.txt +0 -0
hippius_sdk/cli_rich.py
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
"""Rich UI components for the Hippius SDK CLI."""
|
2
|
+
|
3
|
+
import argparse
|
4
|
+
import sys
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
6
|
+
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.panel import Panel
|
9
|
+
from rich.progress import (BarColumn, Progress, SpinnerColumn, TextColumn,
|
10
|
+
TimeElapsedColumn, TimeRemainingColumn)
|
11
|
+
from rich.table import Table
|
12
|
+
from rich.text import Text
|
13
|
+
|
14
|
+
# Create a global console instance
|
15
|
+
console = Console()
|
16
|
+
|
17
|
+
|
18
|
+
def log(message: str, style: Optional[str] = None) -> None:
|
19
|
+
"""Log a message to the console with optional styling.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
message: The message to log
|
23
|
+
style: Optional style to apply to the message
|
24
|
+
"""
|
25
|
+
console.print(message, style=style)
|
26
|
+
|
27
|
+
|
28
|
+
def info(message: str) -> None:
|
29
|
+
"""Log an info message to the console.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
message: The info message to log
|
33
|
+
"""
|
34
|
+
console.print(f"[blue]INFO:[/blue] {message}")
|
35
|
+
|
36
|
+
|
37
|
+
def success(message: str) -> None:
|
38
|
+
"""Log a success message to the console.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
message: The success message to log
|
42
|
+
"""
|
43
|
+
console.print(f"[green]SUCCESS:[/green] {message}")
|
44
|
+
|
45
|
+
|
46
|
+
def warning(message: str) -> None:
|
47
|
+
"""Log a warning message to the console.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
message: The warning message to log
|
51
|
+
"""
|
52
|
+
console.print(f"[yellow]WARNING:[/yellow] {message}")
|
53
|
+
|
54
|
+
|
55
|
+
def error(message: str) -> None:
|
56
|
+
"""Log an error message to the console.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
message: The error message to log
|
60
|
+
"""
|
61
|
+
console.print(f"[bold red]ERROR:[/bold red] {message}")
|
62
|
+
|
63
|
+
|
64
|
+
def print_table(
|
65
|
+
title: str,
|
66
|
+
data: List[Dict[str, Any]],
|
67
|
+
columns: List[str],
|
68
|
+
style: Optional[str] = None,
|
69
|
+
) -> None:
|
70
|
+
"""Print a table of data.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
title: The title of the table
|
74
|
+
data: List of dictionaries containing the data
|
75
|
+
columns: List of column names to include
|
76
|
+
style: Optional style to apply to the table
|
77
|
+
"""
|
78
|
+
# Create table with optional style and expanded width
|
79
|
+
table = Table(title=title, style=style, expand=True, show_edge=True)
|
80
|
+
|
81
|
+
# Add columns
|
82
|
+
for column in columns:
|
83
|
+
table.add_column(column)
|
84
|
+
|
85
|
+
# Add rows, applying style to each cell if provided
|
86
|
+
for row in data:
|
87
|
+
values = [str(row.get(column, "")) for column in columns]
|
88
|
+
if style:
|
89
|
+
# Apply style to each cell value if a style is provided
|
90
|
+
styled_values = [f"[{style}]{value}[/{style}]" for value in values]
|
91
|
+
table.add_row(*styled_values)
|
92
|
+
else:
|
93
|
+
table.add_row(*values)
|
94
|
+
|
95
|
+
# Print the table
|
96
|
+
console.print(table)
|
97
|
+
|
98
|
+
|
99
|
+
def print_panel(content: str, title: Optional[str] = None) -> None:
|
100
|
+
"""Print content in a panel.
|
101
|
+
|
102
|
+
Args:
|
103
|
+
content: The content to display in the panel
|
104
|
+
title: Optional title for the panel
|
105
|
+
"""
|
106
|
+
console.print(Panel(content, title=title))
|
107
|
+
|
108
|
+
|
109
|
+
def create_progress() -> Progress:
|
110
|
+
"""Create a Rich progress bar for tracking operations.
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
A Rich Progress instance configured for the Hippius CLI
|
114
|
+
"""
|
115
|
+
return Progress(
|
116
|
+
SpinnerColumn(),
|
117
|
+
TextColumn("[bold blue]{task.description}"),
|
118
|
+
BarColumn(),
|
119
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
120
|
+
TimeElapsedColumn(),
|
121
|
+
TimeRemainingColumn(),
|
122
|
+
console=console,
|
123
|
+
)
|
124
|
+
|
125
|
+
|
126
|
+
def print_help_text(parser: "argparse.ArgumentParser"):
|
127
|
+
"""Print help text with Rich formatting.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
parser: The argparse parser to display help for
|
131
|
+
"""
|
132
|
+
# Get the help text from the parser
|
133
|
+
import io
|
134
|
+
|
135
|
+
buffer = io.StringIO()
|
136
|
+
parser.print_help(buffer)
|
137
|
+
help_text = buffer.getvalue()
|
138
|
+
|
139
|
+
# Split the help text into sections
|
140
|
+
sections = help_text.split("\n\n")
|
141
|
+
|
142
|
+
# Process and print each section with appropriate styling
|
143
|
+
for i, section in enumerate(sections):
|
144
|
+
if i == 0: # Usage section
|
145
|
+
lines = section.split("\n")
|
146
|
+
title = lines[0]
|
147
|
+
usage = "\n".join(lines[1:]) if len(lines) > 1 else ""
|
148
|
+
console.print(f"[bold cyan]{title}[/bold cyan]")
|
149
|
+
if usage:
|
150
|
+
console.print(f"[yellow]{usage}[/yellow]")
|
151
|
+
elif "positional arguments:" in section:
|
152
|
+
lines = section.split("\n")
|
153
|
+
title = lines[0]
|
154
|
+
args = "\n".join(lines[1:])
|
155
|
+
console.print(f"\n[bold green]{title}[/bold green]")
|
156
|
+
console.print(args)
|
157
|
+
elif "options:" in section:
|
158
|
+
lines = section.split("\n")
|
159
|
+
title = lines[0]
|
160
|
+
opts = "\n".join(lines[1:])
|
161
|
+
console.print(f"\n[bold green]{title}[/bold green]")
|
162
|
+
console.print(opts)
|
163
|
+
elif "examples:" in section:
|
164
|
+
lines = section.split("\n")
|
165
|
+
title = lines[0]
|
166
|
+
examples = "\n".join(lines[1:])
|
167
|
+
console.print(f"\n[bold magenta]{title}[/bold magenta]")
|
168
|
+
console.print(f"[cyan]{examples}[/cyan]")
|
169
|
+
else:
|
170
|
+
console.print(f"\n{section}")
|
171
|
+
|
172
|
+
|
173
|
+
class RichHelpAction(argparse.Action):
|
174
|
+
"""Custom help action that displays the Hippius logo and uses Rich formatting."""
|
175
|
+
|
176
|
+
def __init__(
|
177
|
+
self,
|
178
|
+
option_strings,
|
179
|
+
dest=argparse.SUPPRESS,
|
180
|
+
default=argparse.SUPPRESS,
|
181
|
+
help=None,
|
182
|
+
):
|
183
|
+
super().__init__(
|
184
|
+
option_strings=option_strings,
|
185
|
+
dest=dest,
|
186
|
+
default=default,
|
187
|
+
nargs=0,
|
188
|
+
help=help,
|
189
|
+
)
|
190
|
+
|
191
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
192
|
+
# Display the Hippius logo banner when help is requested
|
193
|
+
from hippius_sdk.cli_assets import HERO_TITLE
|
194
|
+
|
195
|
+
console.print(HERO_TITLE, style="bold cyan")
|
196
|
+
|
197
|
+
# Use our print_help_text function instead of the default formatter
|
198
|
+
print_help_text(parser)
|
199
|
+
parser.exit()
|
200
|
+
|
201
|
+
|
202
|
+
class ProgressTracker:
|
203
|
+
"""Helper class for tracking progress in async operations with Rich progress bars."""
|
204
|
+
|
205
|
+
def __init__(self, description: str, total: int):
|
206
|
+
"""Initialize a progress tracker.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
description: Description for the progress bar
|
210
|
+
total: Total number of items to process
|
211
|
+
"""
|
212
|
+
self.progress = create_progress()
|
213
|
+
self.task_id = None
|
214
|
+
self.description = description
|
215
|
+
self.total = total
|
216
|
+
self.completed = 0
|
217
|
+
|
218
|
+
def __enter__(self):
|
219
|
+
"""Context manager entry that initializes the progress bar."""
|
220
|
+
self.progress.__enter__()
|
221
|
+
self.task_id = self.progress.add_task(self.description, total=self.total)
|
222
|
+
return self
|
223
|
+
|
224
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
225
|
+
"""Context manager exit that properly closes the progress bar."""
|
226
|
+
self.progress.__exit__(exc_type, exc_val, exc_tb)
|
227
|
+
|
228
|
+
def update(self, advance: int = 1):
|
229
|
+
"""Update the progress bar.
|
230
|
+
|
231
|
+
Args:
|
232
|
+
advance: Number of steps to advance by (default: 1)
|
233
|
+
"""
|
234
|
+
self.completed += advance
|
235
|
+
self.progress.update(self.task_id, completed=self.completed)
|
236
|
+
|
237
|
+
def set_description(self, description: str):
|
238
|
+
"""Update the progress bar description.
|
239
|
+
|
240
|
+
Args:
|
241
|
+
description: New description text
|
242
|
+
"""
|
243
|
+
self.progress.update(self.task_id, description=description)
|
244
|
+
|
245
|
+
def finish(self):
|
246
|
+
"""Mark the progress as complete."""
|
247
|
+
self.progress.update(self.task_id, completed=self.total)
|
hippius_sdk/client.py
CHANGED
@@ -3,7 +3,7 @@ Main client for the Hippius SDK.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import base64
|
6
|
-
from typing import Any, Dict, List, Optional
|
6
|
+
from typing import Any, Callable, Dict, List, Optional
|
7
7
|
|
8
8
|
import nacl.secret
|
9
9
|
import nacl.utils
|
@@ -46,7 +46,9 @@ class HippiusClient:
|
|
46
46
|
"""
|
47
47
|
# Load configuration values if not explicitly provided
|
48
48
|
if ipfs_gateway is None:
|
49
|
-
ipfs_gateway = get_config_value(
|
49
|
+
ipfs_gateway = get_config_value(
|
50
|
+
"ipfs", "gateway", "https://get.hippius.network"
|
51
|
+
)
|
50
52
|
|
51
53
|
if ipfs_api_url is None:
|
52
54
|
ipfs_api_url = get_config_value(
|
@@ -62,8 +64,8 @@ class HippiusClient:
|
|
62
64
|
"substrate", "url", "wss://rpc.hippius.network"
|
63
65
|
)
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
+
# Don't try to get a seed phrase from the legacy location
|
68
|
+
# The substrate_client will handle getting it from the active account
|
67
69
|
|
68
70
|
if encrypt_by_default is None:
|
69
71
|
encrypt_by_default = get_config_value(
|
@@ -80,18 +82,13 @@ class HippiusClient:
|
|
80
82
|
encrypt_by_default=encrypt_by_default,
|
81
83
|
encryption_key=encryption_key,
|
82
84
|
)
|
83
|
-
|
84
85
|
# Initialize Substrate client
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
)
|
92
|
-
except Exception as e:
|
93
|
-
print(f"Warning: Could not initialize Substrate client: {e}")
|
94
|
-
self.substrate_client = None
|
86
|
+
self.substrate_client = SubstrateClient(
|
87
|
+
url=substrate_url,
|
88
|
+
seed_phrase=substrate_seed_phrase,
|
89
|
+
password=seed_phrase_password,
|
90
|
+
account_name=account_name,
|
91
|
+
)
|
95
92
|
|
96
93
|
async def upload_file(
|
97
94
|
self, file_path: str, encrypt: Optional[bool] = None
|
@@ -201,7 +198,7 @@ class HippiusClient:
|
|
201
198
|
cid, max_display_bytes, format_output, decrypt=decrypt
|
202
199
|
)
|
203
200
|
|
204
|
-
def exists(self, cid: str) -> Dict[str, Any]:
|
201
|
+
async def exists(self, cid: str) -> Dict[str, Any]:
|
205
202
|
"""
|
206
203
|
Check if a CID exists on IPFS.
|
207
204
|
|
@@ -215,9 +212,9 @@ class HippiusClient:
|
|
215
212
|
- formatted_cid: Formatted version of the CID
|
216
213
|
- gateway_url: URL to access the content if it exists
|
217
214
|
"""
|
218
|
-
return self.ipfs_client.exists(cid)
|
215
|
+
return await self.ipfs_client.exists(cid)
|
219
216
|
|
220
|
-
def pin(self, cid: str) -> Dict[str, Any]:
|
217
|
+
async def pin(self, cid: str) -> Dict[str, Any]:
|
221
218
|
"""
|
222
219
|
Pin a CID to IPFS to keep it available.
|
223
220
|
|
@@ -231,7 +228,7 @@ class HippiusClient:
|
|
231
228
|
- formatted_cid: Formatted version of the CID
|
232
229
|
- message: Status message
|
233
230
|
"""
|
234
|
-
return self.ipfs_client.pin(cid)
|
231
|
+
return await self.ipfs_client.pin(cid)
|
235
232
|
|
236
233
|
def format_cid(self, cid: str) -> str:
|
237
234
|
"""
|
@@ -336,7 +333,7 @@ class HippiusClient:
|
|
336
333
|
temp_dir: str = None,
|
337
334
|
max_retries: int = 3,
|
338
335
|
verbose: bool = True,
|
339
|
-
) ->
|
336
|
+
) -> Dict:
|
340
337
|
"""
|
341
338
|
Reconstruct a file from erasure-coded chunks using its metadata.
|
342
339
|
|
@@ -348,7 +345,7 @@ class HippiusClient:
|
|
348
345
|
verbose: Whether to print progress information
|
349
346
|
|
350
347
|
Returns:
|
351
|
-
|
348
|
+
Dict: containing file reconstruction info.
|
352
349
|
|
353
350
|
Raises:
|
354
351
|
ValueError: If reconstruction fails
|
@@ -372,6 +369,8 @@ class HippiusClient:
|
|
372
369
|
miner_ids: List[str] = None,
|
373
370
|
max_retries: int = 3,
|
374
371
|
verbose: bool = True,
|
372
|
+
progress_callback: Optional[Callable[[str, int, int], None]] = None,
|
373
|
+
publish: bool = True,
|
375
374
|
) -> Dict[str, Any]:
|
376
375
|
"""
|
377
376
|
Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
|
@@ -387,9 +386,14 @@ class HippiusClient:
|
|
387
386
|
miner_ids: List of specific miner IDs to use for storage
|
388
387
|
max_retries: Maximum number of retry attempts
|
389
388
|
verbose: Whether to print progress information
|
389
|
+
progress_callback: Optional callback function for progress updates
|
390
|
+
Function receives (stage_name, current, total)
|
391
|
+
publish: Whether to publish to the blockchain (True) or just perform local
|
392
|
+
erasure coding without publishing (False). When False, no password
|
393
|
+
is needed for seed phrase access.
|
390
394
|
|
391
395
|
Returns:
|
392
|
-
dict: Result including metadata CID and transaction hash
|
396
|
+
dict: Result including metadata CID and transaction hash (if published)
|
393
397
|
|
394
398
|
Raises:
|
395
399
|
ValueError: If parameters are invalid
|
@@ -405,4 +409,48 @@ class HippiusClient:
|
|
405
409
|
substrate_client=self.substrate_client,
|
406
410
|
max_retries=max_retries,
|
407
411
|
verbose=verbose,
|
412
|
+
progress_callback=progress_callback,
|
413
|
+
publish=publish,
|
414
|
+
)
|
415
|
+
|
416
|
+
async def delete_file(
|
417
|
+
self, cid: str, cancel_from_blockchain: bool = True
|
418
|
+
) -> Dict[str, Any]:
|
419
|
+
"""
|
420
|
+
Delete a file from IPFS and optionally cancel its storage on the blockchain.
|
421
|
+
|
422
|
+
Args:
|
423
|
+
cid: Content Identifier (CID) of the file to delete
|
424
|
+
cancel_from_blockchain: Whether to also cancel the storage request from the blockchain
|
425
|
+
|
426
|
+
Returns:
|
427
|
+
Dict containing the result of the operation
|
428
|
+
|
429
|
+
Raises:
|
430
|
+
RuntimeError: If deletion fails completely
|
431
|
+
"""
|
432
|
+
return await self.ipfs_client.delete_file(cid, cancel_from_blockchain)
|
433
|
+
|
434
|
+
async def delete_ec_file(
|
435
|
+
self,
|
436
|
+
metadata_cid: str,
|
437
|
+
cancel_from_blockchain: bool = True,
|
438
|
+
parallel_limit: int = 20,
|
439
|
+
) -> Dict[str, Any]:
|
440
|
+
"""
|
441
|
+
Delete an erasure-coded file, including all its chunks in parallel.
|
442
|
+
|
443
|
+
Args:
|
444
|
+
metadata_cid: CID of the metadata file for the erasure-coded file
|
445
|
+
cancel_from_blockchain: Whether to cancel storage from blockchain
|
446
|
+
parallel_limit: Maximum number of concurrent deletion operations
|
447
|
+
|
448
|
+
Returns:
|
449
|
+
Dict containing the result of the operation
|
450
|
+
|
451
|
+
Raises:
|
452
|
+
RuntimeError: If deletion fails completely
|
453
|
+
"""
|
454
|
+
return await self.ipfs_client.delete_ec_file(
|
455
|
+
metadata_cid, cancel_from_blockchain, parallel_limit
|
408
456
|
)
|