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.
@@ -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("ipfs", "gateway", "https://ipfs.io")
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
- if substrate_seed_phrase is None:
66
- substrate_seed_phrase = get_config_value("substrate", "seed_phrase")
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
- try:
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
- )
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
- ) -> str:
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
- str: Path to the reconstructed file
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
  )