hippius 0.2.2__py3-none-any.whl → 0.2.3__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.3.dist-info}/METADATA +8 -7
- hippius-0.2.3.dist-info/RECORD +16 -0
- hippius_sdk/__init__.py +1 -1
- hippius_sdk/cli.py +277 -2628
- hippius_sdk/cli_assets.py +8 -0
- hippius_sdk/cli_handlers.py +2370 -0
- hippius_sdk/cli_parser.py +602 -0
- hippius_sdk/cli_rich.py +253 -0
- hippius_sdk/client.py +56 -8
- hippius_sdk/config.py +1 -1
- hippius_sdk/ipfs.py +372 -16
- hippius_sdk/ipfs_core.py +22 -1
- hippius_sdk/substrate.py +215 -525
- hippius_sdk/utils.py +84 -2
- hippius-0.2.2.dist-info/RECORD +0 -12
- {hippius-0.2.2.dist-info → hippius-0.2.3.dist-info}/WHEEL +0 -0
- {hippius-0.2.2.dist-info → hippius-0.2.3.dist-info}/entry_points.txt +0 -0
hippius_sdk/cli_rich.py
ADDED
@@ -0,0 +1,253 @@
|
|
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 (
|
10
|
+
BarColumn,
|
11
|
+
Progress,
|
12
|
+
SpinnerColumn,
|
13
|
+
TextColumn,
|
14
|
+
TimeElapsedColumn,
|
15
|
+
TimeRemainingColumn,
|
16
|
+
)
|
17
|
+
from rich.table import Table
|
18
|
+
from rich.text import Text
|
19
|
+
|
20
|
+
# Create a global console instance
|
21
|
+
console = Console()
|
22
|
+
|
23
|
+
|
24
|
+
def log(message: str, style: Optional[str] = None) -> None:
|
25
|
+
"""Log a message to the console with optional styling.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
message: The message to log
|
29
|
+
style: Optional style to apply to the message
|
30
|
+
"""
|
31
|
+
console.print(message, style=style)
|
32
|
+
|
33
|
+
|
34
|
+
def info(message: str) -> None:
|
35
|
+
"""Log an info message to the console.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
message: The info message to log
|
39
|
+
"""
|
40
|
+
console.print(f"[blue]INFO:[/blue] {message}")
|
41
|
+
|
42
|
+
|
43
|
+
def success(message: str) -> None:
|
44
|
+
"""Log a success message to the console.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
message: The success message to log
|
48
|
+
"""
|
49
|
+
console.print(f"[green]SUCCESS:[/green] {message}")
|
50
|
+
|
51
|
+
|
52
|
+
def warning(message: str) -> None:
|
53
|
+
"""Log a warning message to the console.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
message: The warning message to log
|
57
|
+
"""
|
58
|
+
console.print(f"[yellow]WARNING:[/yellow] {message}")
|
59
|
+
|
60
|
+
|
61
|
+
def error(message: str) -> None:
|
62
|
+
"""Log an error message to the console.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
message: The error message to log
|
66
|
+
"""
|
67
|
+
console.print(f"[bold red]ERROR:[/bold red] {message}")
|
68
|
+
|
69
|
+
|
70
|
+
def print_table(
|
71
|
+
title: str,
|
72
|
+
data: List[Dict[str, Any]],
|
73
|
+
columns: List[str],
|
74
|
+
style: Optional[str] = None,
|
75
|
+
) -> None:
|
76
|
+
"""Print a table of data.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
title: The title of the table
|
80
|
+
data: List of dictionaries containing the data
|
81
|
+
columns: List of column names to include
|
82
|
+
style: Optional style to apply to the table
|
83
|
+
"""
|
84
|
+
# Create table with optional style and expanded width
|
85
|
+
table = Table(title=title, style=style, expand=True, show_edge=True)
|
86
|
+
|
87
|
+
# Add columns
|
88
|
+
for column in columns:
|
89
|
+
table.add_column(column)
|
90
|
+
|
91
|
+
# Add rows, applying style to each cell if provided
|
92
|
+
for row in data:
|
93
|
+
values = [str(row.get(column, "")) for column in columns]
|
94
|
+
if style:
|
95
|
+
# Apply style to each cell value if a style is provided
|
96
|
+
styled_values = [f"[{style}]{value}[/{style}]" for value in values]
|
97
|
+
table.add_row(*styled_values)
|
98
|
+
else:
|
99
|
+
table.add_row(*values)
|
100
|
+
|
101
|
+
# Print the table
|
102
|
+
console.print(table)
|
103
|
+
|
104
|
+
|
105
|
+
def print_panel(content: str, title: Optional[str] = None) -> None:
|
106
|
+
"""Print content in a panel.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
content: The content to display in the panel
|
110
|
+
title: Optional title for the panel
|
111
|
+
"""
|
112
|
+
console.print(Panel(content, title=title))
|
113
|
+
|
114
|
+
|
115
|
+
def create_progress() -> Progress:
|
116
|
+
"""Create a Rich progress bar for tracking operations.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
A Rich Progress instance configured for the Hippius CLI
|
120
|
+
"""
|
121
|
+
return Progress(
|
122
|
+
SpinnerColumn(),
|
123
|
+
TextColumn("[bold blue]{task.description}"),
|
124
|
+
BarColumn(),
|
125
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
126
|
+
TimeElapsedColumn(),
|
127
|
+
TimeRemainingColumn(),
|
128
|
+
console=console,
|
129
|
+
)
|
130
|
+
|
131
|
+
|
132
|
+
def print_help_text(parser: "argparse.ArgumentParser"):
|
133
|
+
"""Print help text with Rich formatting.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
parser: The argparse parser to display help for
|
137
|
+
"""
|
138
|
+
# Get the help text from the parser
|
139
|
+
import io
|
140
|
+
|
141
|
+
buffer = io.StringIO()
|
142
|
+
parser.print_help(buffer)
|
143
|
+
help_text = buffer.getvalue()
|
144
|
+
|
145
|
+
# Split the help text into sections
|
146
|
+
sections = help_text.split("\n\n")
|
147
|
+
|
148
|
+
# Process and print each section with appropriate styling
|
149
|
+
for i, section in enumerate(sections):
|
150
|
+
if i == 0: # Usage section
|
151
|
+
lines = section.split("\n")
|
152
|
+
title = lines[0]
|
153
|
+
usage = "\n".join(lines[1:]) if len(lines) > 1 else ""
|
154
|
+
console.print(f"[bold cyan]{title}[/bold cyan]")
|
155
|
+
if usage:
|
156
|
+
console.print(f"[yellow]{usage}[/yellow]")
|
157
|
+
elif "positional arguments:" in section:
|
158
|
+
lines = section.split("\n")
|
159
|
+
title = lines[0]
|
160
|
+
args = "\n".join(lines[1:])
|
161
|
+
console.print(f"\n[bold green]{title}[/bold green]")
|
162
|
+
console.print(args)
|
163
|
+
elif "options:" in section:
|
164
|
+
lines = section.split("\n")
|
165
|
+
title = lines[0]
|
166
|
+
opts = "\n".join(lines[1:])
|
167
|
+
console.print(f"\n[bold green]{title}[/bold green]")
|
168
|
+
console.print(opts)
|
169
|
+
elif "examples:" in section:
|
170
|
+
lines = section.split("\n")
|
171
|
+
title = lines[0]
|
172
|
+
examples = "\n".join(lines[1:])
|
173
|
+
console.print(f"\n[bold magenta]{title}[/bold magenta]")
|
174
|
+
console.print(f"[cyan]{examples}[/cyan]")
|
175
|
+
else:
|
176
|
+
console.print(f"\n{section}")
|
177
|
+
|
178
|
+
|
179
|
+
class RichHelpAction(argparse.Action):
|
180
|
+
"""Custom help action that displays the Hippius logo and uses Rich formatting."""
|
181
|
+
|
182
|
+
def __init__(
|
183
|
+
self,
|
184
|
+
option_strings,
|
185
|
+
dest=argparse.SUPPRESS,
|
186
|
+
default=argparse.SUPPRESS,
|
187
|
+
help=None,
|
188
|
+
):
|
189
|
+
super().__init__(
|
190
|
+
option_strings=option_strings,
|
191
|
+
dest=dest,
|
192
|
+
default=default,
|
193
|
+
nargs=0,
|
194
|
+
help=help,
|
195
|
+
)
|
196
|
+
|
197
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
198
|
+
# Display the Hippius logo banner when help is requested
|
199
|
+
from hippius_sdk.cli_assets import HERO_TITLE
|
200
|
+
|
201
|
+
console.print(HERO_TITLE, style="bold cyan")
|
202
|
+
|
203
|
+
# Use our print_help_text function instead of the default formatter
|
204
|
+
print_help_text(parser)
|
205
|
+
parser.exit()
|
206
|
+
|
207
|
+
|
208
|
+
class ProgressTracker:
|
209
|
+
"""Helper class for tracking progress in async operations with Rich progress bars."""
|
210
|
+
|
211
|
+
def __init__(self, description: str, total: int):
|
212
|
+
"""Initialize a progress tracker.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
description: Description for the progress bar
|
216
|
+
total: Total number of items to process
|
217
|
+
"""
|
218
|
+
self.progress = create_progress()
|
219
|
+
self.task_id = None
|
220
|
+
self.description = description
|
221
|
+
self.total = total
|
222
|
+
self.completed = 0
|
223
|
+
|
224
|
+
def __enter__(self):
|
225
|
+
"""Context manager entry that initializes the progress bar."""
|
226
|
+
self.progress.__enter__()
|
227
|
+
self.task_id = self.progress.add_task(self.description, total=self.total)
|
228
|
+
return self
|
229
|
+
|
230
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
231
|
+
"""Context manager exit that properly closes the progress bar."""
|
232
|
+
self.progress.__exit__(exc_type, exc_val, exc_tb)
|
233
|
+
|
234
|
+
def update(self, advance: int = 1):
|
235
|
+
"""Update the progress bar.
|
236
|
+
|
237
|
+
Args:
|
238
|
+
advance: Number of steps to advance by (default: 1)
|
239
|
+
"""
|
240
|
+
self.completed += advance
|
241
|
+
self.progress.update(self.task_id, completed=self.completed)
|
242
|
+
|
243
|
+
def set_description(self, description: str):
|
244
|
+
"""Update the progress bar description.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
description: New description text
|
248
|
+
"""
|
249
|
+
self.progress.update(self.task_id, description=description)
|
250
|
+
|
251
|
+
def finish(self):
|
252
|
+
"""Mark the progress as complete."""
|
253
|
+
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(
|
@@ -201,7 +203,7 @@ class HippiusClient:
|
|
201
203
|
cid, max_display_bytes, format_output, decrypt=decrypt
|
202
204
|
)
|
203
205
|
|
204
|
-
def exists(self, cid: str) -> Dict[str, Any]:
|
206
|
+
async def exists(self, cid: str) -> Dict[str, Any]:
|
205
207
|
"""
|
206
208
|
Check if a CID exists on IPFS.
|
207
209
|
|
@@ -215,9 +217,9 @@ class HippiusClient:
|
|
215
217
|
- formatted_cid: Formatted version of the CID
|
216
218
|
- gateway_url: URL to access the content if it exists
|
217
219
|
"""
|
218
|
-
return self.ipfs_client.exists(cid)
|
220
|
+
return await self.ipfs_client.exists(cid)
|
219
221
|
|
220
|
-
def pin(self, cid: str) -> Dict[str, Any]:
|
222
|
+
async def pin(self, cid: str) -> Dict[str, Any]:
|
221
223
|
"""
|
222
224
|
Pin a CID to IPFS to keep it available.
|
223
225
|
|
@@ -231,7 +233,7 @@ class HippiusClient:
|
|
231
233
|
- formatted_cid: Formatted version of the CID
|
232
234
|
- message: Status message
|
233
235
|
"""
|
234
|
-
return self.ipfs_client.pin(cid)
|
236
|
+
return await self.ipfs_client.pin(cid)
|
235
237
|
|
236
238
|
def format_cid(self, cid: str) -> str:
|
237
239
|
"""
|
@@ -336,7 +338,7 @@ class HippiusClient:
|
|
336
338
|
temp_dir: str = None,
|
337
339
|
max_retries: int = 3,
|
338
340
|
verbose: bool = True,
|
339
|
-
) ->
|
341
|
+
) -> Dict:
|
340
342
|
"""
|
341
343
|
Reconstruct a file from erasure-coded chunks using its metadata.
|
342
344
|
|
@@ -348,7 +350,7 @@ class HippiusClient:
|
|
348
350
|
verbose: Whether to print progress information
|
349
351
|
|
350
352
|
Returns:
|
351
|
-
|
353
|
+
Dict: containing file reconstruction info.
|
352
354
|
|
353
355
|
Raises:
|
354
356
|
ValueError: If reconstruction fails
|
@@ -372,6 +374,7 @@ class HippiusClient:
|
|
372
374
|
miner_ids: List[str] = None,
|
373
375
|
max_retries: int = 3,
|
374
376
|
verbose: bool = True,
|
377
|
+
progress_callback: Optional[Callable[[str, int, int], None]] = None,
|
375
378
|
) -> Dict[str, Any]:
|
376
379
|
"""
|
377
380
|
Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
|
@@ -387,6 +390,8 @@ class HippiusClient:
|
|
387
390
|
miner_ids: List of specific miner IDs to use for storage
|
388
391
|
max_retries: Maximum number of retry attempts
|
389
392
|
verbose: Whether to print progress information
|
393
|
+
progress_callback: Optional callback function for progress updates
|
394
|
+
Function receives (stage_name, current, total)
|
390
395
|
|
391
396
|
Returns:
|
392
397
|
dict: Result including metadata CID and transaction hash
|
@@ -405,4 +410,47 @@ class HippiusClient:
|
|
405
410
|
substrate_client=self.substrate_client,
|
406
411
|
max_retries=max_retries,
|
407
412
|
verbose=verbose,
|
413
|
+
progress_callback=progress_callback,
|
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
|
)
|
hippius_sdk/config.py
CHANGED
@@ -23,7 +23,7 @@ CONFIG_DIR = os.path.expanduser("~/.hippius")
|
|
23
23
|
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
|
24
24
|
DEFAULT_CONFIG = {
|
25
25
|
"ipfs": {
|
26
|
-
"gateway": "https://
|
26
|
+
"gateway": "https://get.hippius.network",
|
27
27
|
"api_url": "https://store.hippius.network",
|
28
28
|
"local_ipfs": False,
|
29
29
|
},
|