From 70409dca55a7c3d2a72b1b19e389a02ccd3bcda9 Mon Sep 17 00:00:00 2001 From: smartgoo Date: Sat, 2 May 2026 09:51:14 -0400 Subject: [PATCH] add repr method to pertinent classes --- docs/CHANGELOG.md | 4 +- docs/gen_ref_pages.py | 2 + python/kaspa/__init__.pyi | 237 +++++++++++++++++++++- src/address.rs | 8 + src/consensus/client/input.rs | 15 ++ src/consensus/client/outpoint.rs | 12 ++ src/consensus/client/output.rs | 13 ++ src/consensus/client/transaction.rs | 16 ++ src/consensus/client/utxo.rs | 52 +++++ src/consensus/core/network.rs | 8 + src/consensus/core/script_public_key.rs | 12 ++ src/crypto/txscript/builder.rs | 9 + src/rpc/notification.rs | 25 ++- src/rpc/wrpc/client.rs | 42 ++++ src/rpc/wrpc/resolver.rs | 9 + src/types.rs | 14 +- src/wallet/core/tx/fees.rs | 13 ++ src/wallet/core/tx/generator/generator.rs | 47 +++++ src/wallet/core/tx/generator/pending.rs | 20 ++ src/wallet/core/tx/generator/summary.rs | 19 ++ src/wallet/core/tx/payment.rs | 12 ++ src/wallet/core/utxo/balance.rs | 15 ++ src/wallet/core/utxo/context.rs | 12 ++ src/wallet/core/utxo/processor.rs | 45 ++++ src/wallet/core/wallet.rs | 19 ++ src/wallet/keys/derivation.rs | 8 + src/wallet/keys/pubkeygen.rs | 11 + src/wallet/keys/publickey.rs | 16 ++ src/wallet/keys/xpub.rs | 18 ++ 29 files changed, 725 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b3570636..dd9c6b56 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -27,8 +27,8 @@ - Integration tests now default to `mainnet` (overridable via `--network-id` / `--rpc-url`). - `build-dev` script builds with `--strip` for smaller artifacts. - `pyproject.toml`: set `python-source = "python"` and moved the package stub tree under `python/kaspa/` (`kaspa.pyi` → `python/kaspa/__init__.pyi`). -- `Hash` accepts `str` in addition to `Hash` instances wherever it is used as an argument, and gained `to_hex()` and `__repr__` methods. -- Added `Balance` `__repr__` method. +- `Hash` accepts `str` in addition to `Hash` instances wherever it is used as an argument, and gained a `to_hex()` method. +- Added `__repr__` methods to: `Hash`, `Balance`, `Binary`, `Address`, `NetworkId`, `ScriptPublicKey`, `TransactionOutpoint`, `Transaction`, `TransactionInput`, `TransactionOutput`, `UtxoEntry`, `UtxoEntries` (consensus and generator helper), `UtxoEntryReference`, `ScriptBuilder`, `Wallet`, `Fees`, `PaymentOutput`, `Outputs`, `Generator`, `GeneratorSummary`, `PendingTransaction`, `BalanceStrings`, `UtxoProcessorEvent`, `UtxoProcessor`, `UtxoContext`, `Notification`, `Resolver`, `NotificationEvent`, `RpcClient`, `DerivationPath`, `XPub`, `PublicKey`, `XOnlyPublicKey`, `PublicKeyGenerator`. - Documentation site reorganization ### Fixed diff --git a/docs/gen_ref_pages.py b/docs/gen_ref_pages.py index 0a662c93..a18c89bc 100644 --- a/docs/gen_ref_pages.py +++ b/docs/gen_ref_pages.py @@ -159,6 +159,8 @@ def category_to_nav_path(category: str) -> tuple: module = objects[name].get("module", "kaspa") with mkdocs_gen_files.open(doc_path, "w") as f: + if category == "Exceptions": + f.write("---\nsearch:\n boost: 0.3\n---\n\n") f.write(f'# `{name}` ({type_label})\n\n') f.write(f"::: {module}.{name}\n") f.write(" options:\n") diff --git a/python/kaspa/__init__.pyi b/python/kaspa/__init__.pyi index abc4076f..730a7f12 100644 --- a/python/kaspa/__init__.pyi +++ b/python/kaspa/__init__.pyi @@ -266,6 +266,13 @@ class Address: Returns: str: The address as a string """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Address as a repr string. + """ @typing.final class Balance: @@ -325,6 +332,13 @@ class BalanceStrings: r""" Pending balance formatted as a string (if any). """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The BalanceStrings as a repr string. + """ @typing.final class Binary: @@ -340,7 +354,13 @@ class Binary: - bytes: Python bytes object. - list[int]: A list of byte values (0-255). """ - ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Binary as a repr string. + """ @typing.final class DerivationPath: @@ -402,6 +422,13 @@ class DerivationPath: Returns: str: The path as a string (e.g., "m/44'/111111'/0'"). """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The DerivationPath as a repr string. + """ @typing.final class Fees: @@ -423,6 +450,13 @@ class Fees: Returns: Fees: A new Fees instance. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Fees as a repr string. + """ @typing.final class Generator: @@ -471,6 +505,13 @@ class Generator: Returns: GeneratorSummary: The generation summary with fees and transaction details. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Generator as a repr string. + """ def __iter__(self) -> Generator: r""" Return self as an iterator. @@ -560,6 +601,13 @@ class GeneratorSummary: Returns: bool: True if both summaries serialize to identical bytes. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The GeneratorSummary as a repr string. + """ @typing.final class Hash: @@ -870,6 +918,13 @@ class NetworkId: Returns: str: The NetworkId as a string """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The NetworkId as a repr string. + """ @typing.final class Notification: @@ -892,7 +947,13 @@ class Notification: - SinkBlueScoreChanged: The sink blue score changed. - VirtualChainChanged: The virtual chain changed. """ - ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Notification as a repr string. + """ @typing.final class Outputs: @@ -907,7 +968,13 @@ class Outputs: list[PaymentOutput]: A list of PaymentOutput objects. list[dict]: A list of dicts with `address` and `amount` keys. """ - ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Outputs as a repr string. + """ @typing.final class PaymentOutput: @@ -935,6 +1002,13 @@ class PaymentOutput: Returns: bool: True if both outputs have identical address and amount. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The PaymentOutput as a repr string. + """ @typing.final class PendingTransaction: @@ -1070,6 +1144,13 @@ class PendingTransaction: Raises: Exception: If submission fails. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The PendingTransaction as a repr string. + """ @typing.final class PrivateKey: @@ -1331,6 +1412,13 @@ class PublicKey: Returns: str | None: The fingerprint as hex, or None if unavailable. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The PublicKey as a repr string. + """ @typing.final class PublicKeyGenerator: @@ -1605,6 +1693,13 @@ class PublicKeyGenerator: Returns: str: The generator info string. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The PublicKeyGenerator as a repr string. + """ @typing.final class Resolver: @@ -1661,6 +1756,13 @@ class Resolver: Raises: Exception: If no node is available or resolution fails. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Resolver as a repr string. + """ @typing.final class RpcClient: @@ -1811,6 +1913,13 @@ class RpcClient: r""" Remove all registered event listeners. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The RpcClient as a repr string. + """ def subscribe_utxos_changed(self, addresses: typing.Sequence[Address]) -> None: r""" Subscribe to UTXO changes for specific addresses (async). @@ -2068,6 +2177,13 @@ class ScriptBuilder: Returns: bool: True if both builders have produced identical scripts. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The ScriptBuilder as a repr string. + """ @typing.final class ScriptPublicKey: @@ -2113,6 +2229,13 @@ class ScriptPublicKey: Returns: bytes: The raw script bytes. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The ScriptPublicKey as a repr string. + """ @typing.final class Transaction: @@ -2311,6 +2434,13 @@ class Transaction: ValueError: If values are invalid. """ def __eq__(self, other: Transaction) -> builtins.bool: ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Transaction as a repr string. + """ @typing.final class TransactionInput: @@ -2419,6 +2549,13 @@ class TransactionInput: ValueError: If values are invalid. """ def __eq__(self, other: TransactionInput) -> builtins.bool: ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The TransactionInput as a repr string. + """ @typing.final class TransactionOutpoint: @@ -2490,6 +2627,13 @@ class TransactionOutpoint: Returns: bool: True if both outpoints reference the same transaction id and index. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The TransactionOutpoint as a repr string. + """ @typing.final class TransactionOutput: @@ -2571,6 +2715,13 @@ class TransactionOutput: Returns: bool: True if both outputs have identical value and script. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The TransactionOutput as a repr string. + """ @typing.final class UtxoContext: @@ -2629,6 +2780,13 @@ class UtxoContext: r""" Return pending UTXO entries. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoContext as a repr string. + """ @typing.final class UtxoEntries: @@ -2679,6 +2837,13 @@ class UtxoEntries: Returns: bool: True if both collections contain identical entries in the same order. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoEntries as a repr string. + """ @typing.final class UtxoEntries: @@ -2693,7 +2858,13 @@ class UtxoEntries: list[UtxoEntryReference]: A list of UtxoEntryReference objects. list[dict]: A list of dicts with UtxoEntryReference-compatible keys. """ - ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoEntries as a repr string. + """ @typing.final class UtxoEntry: @@ -2772,6 +2943,13 @@ class UtxoEntry: Returns: bool: True if both UtxoEntries have identical fields. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoEntry as a repr string. + """ @typing.final class UtxoEntryReference: @@ -2851,6 +3029,13 @@ class UtxoEntryReference: KeyError: If required keys are missing. ValueError: If values are invalid. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoEntryReference as a repr string. + """ @typing.final class UtxoProcessor: @@ -2945,6 +3130,13 @@ class UtxoProcessor: Returns: None """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoProcessor as a repr string. + """ @typing.final class Wallet: @@ -3059,6 +3251,13 @@ class Wallet: Exception: If the wRPC client is currently connected. Disconnect before changing networks. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The Wallet as a repr string. + """ def wallet_enumerate(self) -> list[WalletDescriptor]: r""" Enumerate all wallet files in the local store. @@ -3623,6 +3822,13 @@ class XOnlyPublicKey: Raises: Exception: If extraction fails. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The XOnlyPublicKey as a repr string. + """ @typing.final class XPrv: @@ -3849,6 +4055,13 @@ class XPub: Returns: PublicKey: The public key. """ + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The XPub as a repr string. + """ @typing.final class NotificationEvent(enum.Enum): @@ -3885,6 +4098,14 @@ class NotificationEvent(enum.Enum): Connect = ... Disconnect = ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The NotificationEvent as a repr string. + """ + @typing.final class AccountsDiscoveryKind(enum.Enum): r""" @@ -4345,6 +4566,14 @@ class UtxoProcessorEvent(enum.Enum): Balance = ... Error = ... + def __repr__(self) -> builtins.str: + r""" + The detailed string representation. + + Returns: + str: The UtxoProcessorEvent as a repr string. + """ + def address_from_script_public_key(script_public_key: ScriptPublicKey, network: str | NetworkType) -> Address: r""" Extract the address from a script public key. diff --git a/src/address.rs b/src/address.rs index d1201738..74a76026 100644 --- a/src/address.rs +++ b/src/address.rs @@ -151,6 +151,14 @@ impl PyAddress { pub fn __str__(&self) -> String { self.0.address_to_string() } + + /// The detailed string representation. + /// + /// Returns: + /// str: The Address as a repr string. + pub fn __repr__(&self) -> String { + format!("Address('{}')", self.0.address_to_string()) + } } impl From
for PyAddress { diff --git a/src/consensus/client/input.rs b/src/consensus/client/input.rs index 022d6fbf..d41bd6bb 100644 --- a/src/consensus/client/input.rs +++ b/src/consensus/client/input.rs @@ -162,6 +162,21 @@ impl PyTransactionInput { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The TransactionInput as a repr string. + fn __repr__(&self) -> String { + let inner = self.0.inner(); + format!( + "TransactionInput(previous_outpoint=TransactionOutpoint(transaction_id='{}', index={}), sequence={}, sig_op_count={})", + inner.previous_outpoint.inner().transaction_id, + inner.previous_outpoint.inner().index, + inner.sequence, + inner.sig_op_count + ) + } } impl From for PyTransactionInput { diff --git a/src/consensus/client/outpoint.rs b/src/consensus/client/outpoint.rs index 27e7b450..ee7c4dc2 100644 --- a/src/consensus/client/outpoint.rs +++ b/src/consensus/client/outpoint.rs @@ -98,6 +98,18 @@ impl PyTransactionOutpoint { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The TransactionOutpoint as a repr string. + fn __repr__(&self) -> String { + format!( + "TransactionOutpoint(transaction_id='{}', index={})", + self.0.inner().transaction_id, + self.0.inner().index + ) + } } impl From for TransactionOutpoint { diff --git a/src/consensus/client/output.rs b/src/consensus/client/output.rs index f63784cf..ce7b3cf3 100644 --- a/src/consensus/client/output.rs +++ b/src/consensus/client/output.rs @@ -107,6 +107,19 @@ impl PyTransactionOutput { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The TransactionOutput as a repr string. + fn __repr__(&self) -> String { + let inner = self.0.inner(); + format!( + "TransactionOutput(value={}, script_public_key='{}')", + inner.value, + inner.script_public_key.script_as_hex() + ) + } } impl From for PyTransactionOutput { diff --git a/src/consensus/client/transaction.rs b/src/consensus/client/transaction.rs index a1f8e0e5..7de73b77 100644 --- a/src/consensus/client/transaction.rs +++ b/src/consensus/client/transaction.rs @@ -326,6 +326,22 @@ impl PyTransaction { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The Transaction as a repr string. + fn __repr__(&self) -> String { + let inner = self.0.inner(); + format!( + "Transaction(id='{}', version={}, inputs={}, outputs={}, lock_time={})", + inner.id, + inner.version, + inner.inputs.len(), + inner.outputs.len(), + inner.lock_time + ) + } } impl From for PyTransaction { diff --git a/src/consensus/client/utxo.rs b/src/consensus/client/utxo.rs index 0e7b18bb..e41ea4ea 100644 --- a/src/consensus/client/utxo.rs +++ b/src/consensus/client/utxo.rs @@ -106,6 +106,26 @@ impl PyUtxoEntry { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoEntry as a repr string. + fn __repr__(&self) -> String { + let address = match &self.0.address { + Some(addr) => format!("'{}'", addr.address_to_string()), + None => "None".to_string(), + }; + format!( + "UtxoEntry(address={}, outpoint=TransactionOutpoint(transaction_id='{}', index={}), amount={}, block_daa_score={}, is_coinbase={})", + address, + self.0.outpoint.inner().transaction_id, + self.0.outpoint.inner().index, + self.0.amount, + self.0.block_daa_score, + self.0.is_coinbase + ) + } } impl From for UtxoEntry { @@ -266,6 +286,18 @@ impl PyUtxoEntries { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoEntries as a repr string. + fn __repr__(&self) -> String { + format!( + "UtxoEntries(items={}, amount={})", + self.0.len(), + self.0.iter().map(|e| e.amount()).sum::() + ) + } } /// A reference to a UTXO entry. @@ -357,6 +389,26 @@ impl PyUtxoEntryReference { fn from_dict(_cls: &Bound<'_, PyType>, dict: &Bound<'_, PyDict>) -> PyResult { Self::try_from(dict) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoEntryReference as a repr string. + fn __repr__(&self) -> String { + let address = match &self.0.utxo.address { + Some(addr) => format!("'{}'", addr.address_to_string()), + None => "None".to_string(), + }; + format!( + "UtxoEntryReference(address={}, outpoint=TransactionOutpoint(transaction_id='{}', index={}), amount={}, block_daa_score={}, is_coinbase={})", + address, + self.0.utxo.outpoint.inner().transaction_id, + self.0.utxo.outpoint.inner().index, + self.0.utxo.amount, + self.0.utxo.block_daa_score, + self.0.utxo.is_coinbase + ) + } } impl From for UtxoEntryReference { diff --git a/src/consensus/core/network.rs b/src/consensus/core/network.rs index 2380adfc..fc72d992 100644 --- a/src/consensus/core/network.rs +++ b/src/consensus/core/network.rs @@ -184,6 +184,14 @@ impl PyNetworkId { pub fn __str__(&self) -> String { self.0.to_string() } + + /// The detailed string representation. + /// + /// Returns: + /// str: The NetworkId as a repr string. + pub fn __repr__(&self) -> String { + format!("NetworkId('{}')", self.0) + } } impl From for NetworkId { diff --git a/src/consensus/core/script_public_key.rs b/src/consensus/core/script_public_key.rs index 11c7b9ff..72a00599 100644 --- a/src/consensus/core/script_public_key.rs +++ b/src/consensus/core/script_public_key.rs @@ -58,6 +58,18 @@ impl PyScriptPublicKey { pub fn __bytes__<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> { PyBytes::new(py, self.0.script()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The ScriptPublicKey as a repr string. + pub fn __repr__(&self) -> String { + format!( + "ScriptPublicKey(version={}, script='{}')", + self.0.version(), + self.0.script_as_hex() + ) + } } impl From for ScriptPublicKey { diff --git a/src/crypto/txscript/builder.rs b/src/crypto/txscript/builder.rs index 7ee85ae1..d4b2a15b 100644 --- a/src/crypto/txscript/builder.rs +++ b/src/crypto/txscript/builder.rs @@ -272,6 +272,15 @@ impl PyScriptBuilder { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The ScriptBuilder as a repr string. + fn __repr__(&self) -> String { + let inner = self.inner(); + format!("ScriptBuilder(script='{}')", inner.script().to_hex()) + } } // TODO change to PyOpcode struct and handle similar to PyBinary? diff --git a/src/rpc/notification.rs b/src/rpc/notification.rs index 17913a95..4172c15d 100644 --- a/src/rpc/notification.rs +++ b/src/rpc/notification.rs @@ -1,6 +1,6 @@ use kaspa_rpc_core::api::notifications::Notification; use pyo3::prelude::*; -use pyo3_stub_gen::derive::gen_stub_pyclass; +use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods}; use serde_pyobject::to_pyobject; /// RPC notification wrapper for event callbacks. @@ -24,6 +24,29 @@ use serde_pyobject::to_pyobject; #[pyclass(name = "Notification")] pub struct PyNotification(pub Notification); +#[gen_stub_pymethods] +#[pymethods] +impl PyNotification { + /// The detailed string representation. + /// + /// Returns: + /// str: The Notification as a repr string. + pub fn __repr__(&self) -> String { + let variant = match &self.0 { + Notification::BlockAdded(_) => "BlockAdded", + Notification::FinalityConflict(_) => "FinalityConflict", + Notification::FinalityConflictResolved(_) => "FinalityConflictResolved", + Notification::NewBlockTemplate(_) => "NewBlockTemplate", + Notification::PruningPointUtxoSetOverride(_) => "PruningPointUtxoSetOverride", + Notification::UtxosChanged(_) => "UtxosChanged", + Notification::VirtualDaaScoreChanged(_) => "VirtualDaaScoreChanged", + Notification::SinkBlueScoreChanged(_) => "SinkBlueScoreChanged", + Notification::VirtualChainChanged(_) => "VirtualChainChanged", + }; + format!("Notification(type='{}')", variant) + } +} + impl PyNotification { pub fn to_pyobject(&self, py: Python) -> PyResult> { let bound_obj = match &self.0 { diff --git a/src/rpc/wrpc/client.rs b/src/rpc/wrpc/client.rs index b8415336..8e98a9e1 100644 --- a/src/rpc/wrpc/client.rs +++ b/src/rpc/wrpc/client.rs @@ -99,6 +99,32 @@ impl<'py> FromPyObject<'_, 'py> for PyNotificationEvent { } } +#[gen_stub_pymethods] +#[pymethods] +impl PyNotificationEvent { + /// The detailed string representation. + /// + /// Returns: + /// str: The NotificationEvent as a repr string. + pub fn __repr__(&self) -> String { + let variant = match self { + PyNotificationEvent::All => "All", + PyNotificationEvent::BlockAdded => "BlockAdded", + PyNotificationEvent::VirtualChainChanged => "VirtualChainChanged", + PyNotificationEvent::FinalityConflict => "FinalityConflict", + PyNotificationEvent::FinalityConflictResolved => "FinalityConflictResolved", + PyNotificationEvent::UtxosChanged => "UtxosChanged", + PyNotificationEvent::SinkBlueScoreChanged => "SinkBlueScoreChanged", + PyNotificationEvent::VirtualDaaScoreChanged => "VirtualDaaScoreChanged", + PyNotificationEvent::PruningPointUtxoSetOverride => "PruningPointUtxoSetOverride", + PyNotificationEvent::NewBlockTemplate => "NewBlockTemplate", + PyNotificationEvent::Connect => "Connect", + PyNotificationEvent::Disconnect => "Disconnect", + }; + format!("NotificationEvent.{}", variant) + } +} + #[derive(Clone, Debug, Eq, PartialEq, Hash)] enum NotificationEvent { All, @@ -573,6 +599,22 @@ impl PyRpcClient { *self.0.callbacks.lock().unwrap() = Default::default(); Ok(()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The RpcClient as a repr string. + fn __repr__(&self) -> String { + let url = match self.0.client.url() { + Some(u) => format!("'{}'", u), + None => "None".to_string(), + }; + format!( + "RpcClient(url={}, is_connected={})", + url, + self.0.client.is_connected() + ) + } } impl PyRpcClient { diff --git a/src/rpc/wrpc/resolver.rs b/src/rpc/wrpc/resolver.rs index 59bdc43e..6192600d 100644 --- a/src/rpc/wrpc/resolver.rs +++ b/src/rpc/wrpc/resolver.rs @@ -123,6 +123,15 @@ impl PyResolver { } // fn connect() TODO + + /// The detailed string representation. + /// + /// Returns: + /// str: The Resolver as a repr string. + fn __repr__(&self) -> String { + let urls = self.0.urls().map(|u| u.len()).unwrap_or(0); + format!("Resolver(urls={})", urls) + } } impl From for Resolver { diff --git a/src/types.rs b/src/types.rs index b6051279..777b05dc 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ use pyo3::exceptions::PyException; use pyo3::prelude::*; use pyo3::types::{PyBytes, PyList}; -use pyo3_stub_gen::derive::gen_stub_pyclass; +use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods}; /// Binary data type for flexible input handling. /// @@ -19,6 +19,18 @@ pub struct PyBinary { pub data: Vec, } +#[gen_stub_pymethods] +#[pymethods] +impl PyBinary { + /// The detailed string representation. + /// + /// Returns: + /// str: The Binary as a repr string. + pub fn __repr__(&self) -> String { + format!("Binary({} bytes)", self.data.len()) + } +} + impl<'py> FromPyObject<'_, 'py> for PyBinary { type Error = PyErr; diff --git a/src/wallet/core/tx/fees.rs b/src/wallet/core/tx/fees.rs index 3b2da8d6..c708fe78 100644 --- a/src/wallet/core/tx/fees.rs +++ b/src/wallet/core/tx/fees.rs @@ -72,6 +72,19 @@ impl PyFees { pub fn new(amount: u64, source: Option) -> Self { Self { amount, source } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The Fees as a repr string. + pub fn __repr__(&self) -> String { + let source = match &self.source { + Some(PyFeeSource::SenderPays) => "FeeSource.SenderPays", + Some(PyFeeSource::ReceiverPays) => "FeeSource.ReceiverPays", + None => "None", + }; + format!("Fees(amount={}, source={})", self.amount, source) + } } impl<'py> FromPyObject<'_, 'py> for PyFees { diff --git a/src/wallet/core/tx/generator/generator.rs b/src/wallet/core/tx/generator/generator.rs index 5b98b387..3b4ba87b 100644 --- a/src/wallet/core/tx/generator/generator.rs +++ b/src/wallet/core/tx/generator/generator.rs @@ -30,6 +30,22 @@ pub struct PyUtxoEntries { pub entries: Vec, } +#[gen_stub_pymethods] +#[pymethods] +impl PyUtxoEntries { + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoEntries as a repr string. + fn __repr__(&self) -> String { + format!( + "UtxoEntries(items={}, amount={})", + self.entries.len(), + self.entries.iter().map(|e| e.amount()).sum::() + ) + } +} + impl<'py> FromPyObject<'_, 'py> for PyUtxoEntries { type Error = PyErr; @@ -72,6 +88,22 @@ pub struct PyOutputs { pub outputs: Vec, } +#[gen_stub_pymethods] +#[pymethods] +impl PyOutputs { + /// The detailed string representation. + /// + /// Returns: + /// str: The Outputs as a repr string. + fn __repr__(&self) -> String { + format!( + "Outputs(items={}, amount={})", + self.outputs.len(), + self.outputs.iter().map(|o| o.amount).sum::() + ) + } +} + impl<'py> FromPyObject<'_, 'py> for PyOutputs { type Error = PyErr; @@ -243,6 +275,21 @@ impl PyGenerator { pub fn summary(&self) -> PyGeneratorSummary { self.0.summary().into() } + + /// The detailed string representation. + /// + /// Returns: + /// str: The Generator as a repr string. + pub fn __repr__(&self) -> String { + let summary = self.0.summary(); + format!( + "Generator(network_id='{}', transactions={}, utxos={}, fees={})", + summary.network_id(), + summary.number_of_generated_transactions(), + summary.aggregated_utxos(), + summary.aggregate_fees() + ) + } } impl PyGenerator { diff --git a/src/wallet/core/tx/generator/pending.rs b/src/wallet/core/tx/generator/pending.rs index f2b02034..e2327555 100644 --- a/src/wallet/core/tx/generator/pending.rs +++ b/src/wallet/core/tx/generator/pending.rs @@ -243,6 +243,26 @@ impl PendingTransaction { fn get_transaction(&self) -> PyResult { Ok(Transaction::from_cctx_transaction(&self.0.transaction(), self.0.utxo_entries()).into()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The PendingTransaction as a repr string. + fn __repr__(&self) -> String { + let transaction_type = if self.0.is_batch() { "batch" } else { "final" }; + format!( + "PendingTransaction(id='{}', type='{}', payment_amount={}, change_amount={}, fee_amount={}, mass={})", + self.0.id(), + transaction_type, + match self.0.payment_value() { + Some(v) => v.to_string(), + None => "None".to_string(), + }, + self.0.change_value(), + self.0.fees(), + self.0.mass() + ) + } } impl From for PendingTransaction { diff --git a/src/wallet/core/tx/generator/summary.rs b/src/wallet/core/tx/generator/summary.rs index 4bb17ae5..7e7dde97 100644 --- a/src/wallet/core/tx/generator/summary.rs +++ b/src/wallet/core/tx/generator/summary.rs @@ -94,6 +94,25 @@ impl PyGeneratorSummary { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The GeneratorSummary as a repr string. + fn __repr__(&self) -> String { + format!( + "GeneratorSummary(network_id='{}', transactions={}, utxos={}, mass={}, fees={}, final_amount={})", + self.0.network_id(), + self.0.number_of_generated_transactions(), + self.0.aggregated_utxos(), + self.0.aggregate_mass(), + self.0.aggregate_fees(), + match self.0.final_transaction_amount() { + Some(v) => v.to_string(), + None => "None".to_string(), + } + ) + } } impl From for PyGeneratorSummary { diff --git a/src/wallet/core/tx/payment.rs b/src/wallet/core/tx/payment.rs index 9781ed1a..6bdc05f0 100644 --- a/src/wallet/core/tx/payment.rs +++ b/src/wallet/core/tx/payment.rs @@ -44,6 +44,18 @@ impl PyPaymentOutput { _ => false, } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The PaymentOutput as a repr string. + fn __repr__(&self) -> String { + format!( + "PaymentOutput(address='{}', amount={})", + self.0.address.address_to_string(), + self.0.amount + ) + } } impl From for PaymentOutput { diff --git a/src/wallet/core/utxo/balance.rs b/src/wallet/core/utxo/balance.rs index f5673d78..8089a3b1 100644 --- a/src/wallet/core/utxo/balance.rs +++ b/src/wallet/core/utxo/balance.rs @@ -96,6 +96,21 @@ impl PyBalanceStrings { pub fn get_pending(&self) -> Option { self.0.pending.clone() } + + /// The detailed string representation. + /// + /// Returns: + /// str: The BalanceStrings as a repr string. + pub fn __repr__(&self) -> String { + let pending = match &self.0.pending { + Some(p) => format!("'{}'", p), + None => "None".to_string(), + }; + format!( + "BalanceStrings(mature='{}', pending={})", + self.0.mature, pending + ) + } } impl From for PyBalanceStrings { diff --git a/src/wallet/core/utxo/context.rs b/src/wallet/core/utxo/context.rs index 48ced415..21c6d016 100644 --- a/src/wallet/core/utxo/context.rs +++ b/src/wallet/core/utxo/context.rs @@ -201,6 +201,18 @@ impl PyUtxoContext { Ok(None) } } + + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoContext as a repr string. + fn __repr__(&self) -> String { + format!( + "UtxoContext(id='{}', mature_length={})", + self.0.id(), + self.0.mature_utxo_size() + ) + } } impl From for UtxoContext { diff --git a/src/wallet/core/utxo/processor.rs b/src/wallet/core/utxo/processor.rs index cc73fc9a..a271bfa5 100644 --- a/src/wallet/core/utxo/processor.rs +++ b/src/wallet/core/utxo/processor.rs @@ -51,6 +51,37 @@ pub enum PyUtxoProcessorEvent { Error, } +#[gen_stub_pymethods] +#[pymethods] +impl PyUtxoProcessorEvent { + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoProcessorEvent as a repr string. + pub fn __repr__(&self) -> String { + let variant = match self { + PyUtxoProcessorEvent::All => "All", + PyUtxoProcessorEvent::Connect => "Connect", + PyUtxoProcessorEvent::Disconnect => "Disconnect", + PyUtxoProcessorEvent::UtxoIndexNotEnabled => "UtxoIndexNotEnabled", + PyUtxoProcessorEvent::SyncState => "SyncState", + PyUtxoProcessorEvent::ServerStatus => "ServerStatus", + PyUtxoProcessorEvent::UtxoProcStart => "UtxoProcStart", + PyUtxoProcessorEvent::UtxoProcStop => "UtxoProcStop", + PyUtxoProcessorEvent::UtxoProcError => "UtxoProcError", + PyUtxoProcessorEvent::DaaScoreChange => "DaaScoreChange", + PyUtxoProcessorEvent::Pending => "Pending", + PyUtxoProcessorEvent::Reorg => "Reorg", + PyUtxoProcessorEvent::Stasis => "Stasis", + PyUtxoProcessorEvent::Maturity => "Maturity", + PyUtxoProcessorEvent::Discovery => "Discovery", + PyUtxoProcessorEvent::Balance => "Balance", + PyUtxoProcessorEvent::Error => "Error", + }; + format!("UtxoProcessorEvent.{}", variant) + } +} + impl<'py> FromPyObject<'_, 'py> for PyUtxoProcessorEvent { type Error = PyErr; @@ -481,6 +512,20 @@ impl PyUtxoProcessor { self.callbacks.lock().unwrap().clear(); Ok(()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The UtxoProcessor as a repr string. + fn __repr__(&self) -> String { + let network_id = self + .processor + .network_id() + .ok() + .map(|n| format!("'{}'", n)) + .unwrap_or_else(|| "None".to_string()); + format!("UtxoProcessor(network_id={})", network_id) + } } fn parse_event_targets(value: Bound<'_, PyAny>) -> PyResult> { diff --git a/src/wallet/core/wallet.rs b/src/wallet/core/wallet.rs index 21043e62..20e1e08f 100644 --- a/src/wallet/core/wallet.rs +++ b/src/wallet/core/wallet.rs @@ -356,6 +356,25 @@ impl PyWallet { self.inner.wallet.set_network_id(&(network_id.into()))?; Ok(()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The Wallet as a repr string. + fn __repr__(&self) -> String { + let wallet = self.wallet(); + let network_id = wallet + .network_id() + .ok() + .map(|n| format!("'{}'", n)) + .unwrap_or_else(|| "None".to_string()); + format!( + "Wallet(network_id={}, is_open={}, is_synced={})", + network_id, + wallet.is_open(), + wallet.is_synced() + ) + } } impl PyWallet { diff --git a/src/wallet/keys/derivation.rs b/src/wallet/keys/derivation.rs index 00a0a374..b6b71e68 100644 --- a/src/wallet/keys/derivation.rs +++ b/src/wallet/keys/derivation.rs @@ -80,6 +80,14 @@ impl PyDerivationPath { pub fn to_str(&self) -> String { self.0.to_string() } + + /// The detailed string representation. + /// + /// Returns: + /// str: The DerivationPath as a repr string. + pub fn __repr__(&self) -> String { + format!("DerivationPath('{}')", self.0) + } } impl From for kaspa_bip32::DerivationPath { diff --git a/src/wallet/keys/pubkeygen.rs b/src/wallet/keys/pubkeygen.rs index bb4be7e7..a8706444 100644 --- a/src/wallet/keys/pubkeygen.rs +++ b/src/wallet/keys/pubkeygen.rs @@ -553,4 +553,15 @@ impl PyPublicKeyGenerator { pub fn to_string(&self) -> PyResult { Ok(self.hd_wallet.to_string(None).to_string()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The PublicKeyGenerator as a repr string. + pub fn __repr__(&self) -> String { + format!( + "PublicKeyGenerator('{}')", + self.hd_wallet.to_string(None).as_str() + ) + } } diff --git a/src/wallet/keys/publickey.rs b/src/wallet/keys/publickey.rs index ff15f832..9dbb7820 100644 --- a/src/wallet/keys/publickey.rs +++ b/src/wallet/keys/publickey.rs @@ -111,6 +111,14 @@ impl PyPublicKey { // } self.0.fingerprint().map(|v| String::try_from(v).unwrap()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The PublicKey as a repr string. + pub fn __repr__(&self) -> String { + format!("PublicKey('{}')", self.to_string_impl()) + } } impl From for PyPublicKey { @@ -217,6 +225,14 @@ impl PyXOnlyPublicKey { // .map_err(|err| PyException::new_err(format!("{}", err)))?; Ok(xonly_public_key.into()) } + + /// The detailed string representation. + /// + /// Returns: + /// str: The XOnlyPublicKey as a repr string. + pub fn __repr__(&self) -> String { + format!("XOnlyPublicKey('{}')", self.0.inner) + } } impl From for PyXOnlyPublicKey { diff --git a/src/wallet/keys/xpub.rs b/src/wallet/keys/xpub.rs index c9bac7ef..0772a942 100644 --- a/src/wallet/keys/xpub.rs +++ b/src/wallet/keys/xpub.rs @@ -159,4 +159,22 @@ impl PyXPub { pub fn get_chain_code(&self) -> String { self.0.inner().attrs().chain_code.to_vec().to_hex() } + + /// The detailed string representation. + /// + /// Returns: + /// str: The XPub as a repr string. + pub fn __repr__(&self) -> String { + let xpub = self + .0 + .inner() + .to_extended_key("kpub".try_into().unwrap()) + .to_string(); + format!( + "XPub(xpub='{}', depth={}, child_number={})", + xpub, + self.0.inner().attrs().depth, + u32::from(self.0.inner().attrs().child_number) + ) + } }