From 8d7b2017b8e52cadb9d6229d58501bc78c3ef5ab Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 4 Jun 2024 14:00:29 -0700 Subject: [PATCH 1/2] Wrapper for presenting the error in a human-readable view (https://github.com/opentensor/bittensor/issues/1859) --- bittensor/extrinsics/commit_weights.py | 3 +- bittensor/extrinsics/delegation.py | 14 ++--- bittensor/extrinsics/network.py | 56 +++++++++++-------- bittensor/extrinsics/prometheus.py | 11 ++-- bittensor/extrinsics/registration.py | 30 +++++----- bittensor/extrinsics/root.py | 8 +-- bittensor/extrinsics/senate.py | 19 +++---- bittensor/extrinsics/serving.py | 33 ++++++----- bittensor/extrinsics/set_weights.py | 12 ++-- bittensor/extrinsics/staking.py | 4 +- bittensor/extrinsics/transfer.py | 6 +- bittensor/extrinsics/unstaking.py | 12 ++-- bittensor/subtensor.py | 35 ++++++------ bittensor/utils/__init__.py | 34 ++++++++--- tests/unit_tests/extrinsics/test_init.py | 49 ++++++++++++++++ .../extrinsics/test_registration.py | 2 +- tests/unit_tests/extrinsics/test_serving.py | 2 +- 17 files changed, 200 insertions(+), 130 deletions(-) create mode 100644 tests/unit_tests/extrinsics/test_init.py diff --git a/bittensor/extrinsics/commit_weights.py b/bittensor/extrinsics/commit_weights.py index 2a526f5e96..fd617f3e3a 100644 --- a/bittensor/extrinsics/commit_weights.py +++ b/bittensor/extrinsics/commit_weights.py @@ -23,6 +23,7 @@ from rich.prompt import Confirm import bittensor +from bittensor.utils import format_error_message def commit_weights_extrinsic( @@ -67,7 +68,7 @@ def commit_weights_extrinsic( return True, "Successfully committed weights." else: bittensor.logging.error(f"Failed to commit weights: {error_message}") - return False, error_message + return False, format_error_message(error_message) def reveal_weights_extrinsic( diff --git a/bittensor/extrinsics/delegation.py b/bittensor/extrinsics/delegation.py index 9583b80a76..54bdb5273c 100644 --- a/bittensor/extrinsics/delegation.py +++ b/bittensor/extrinsics/delegation.py @@ -69,7 +69,7 @@ def nominate_extrinsic( wait_for_finalization=wait_for_finalization, ) - if success == True: + if success is True: bittensor.__console__.print( ":white_heavy_check_mark: [green]Finalized[/green]" ) @@ -138,7 +138,7 @@ def delegate_extrinsic( ) # Convert to bittensor.Balance - if amount == None: + if amount is None: # Stake it all. staking_balance = bittensor.Balance.from_tao(my_prev_coldkey_balance.tao) elif not isinstance(amount, bittensor.Balance): @@ -184,7 +184,7 @@ def delegate_extrinsic( wait_for_finalization=wait_for_finalization, ) - if staking_response == True: # If we successfully staked. + if staking_response is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -273,7 +273,7 @@ def undelegate_extrinsic( ) # Convert to bittensor.Balance - if amount == None: + if amount is None: # Stake it all. unstaking_balance = bittensor.Balance.from_tao(my_prev_delegated_stake.tao) @@ -315,7 +315,7 @@ def undelegate_extrinsic( wait_for_finalization=wait_for_finalization, ) - if staking_response == True: # If we successfully staked. + if staking_response is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -403,7 +403,7 @@ def decrease_take_extrinsic( wait_for_finalization=wait_for_finalization, ) - if success == True: + if success is True: bittensor.__console__.print( ":white_heavy_check_mark: [green]Finalized[/green]" ) @@ -463,7 +463,7 @@ def increase_take_extrinsic( wait_for_finalization=wait_for_finalization, ) - if success == True: + if success is True: bittensor.__console__.print( ":white_heavy_check_mark: [green]Finalized[/green]" ) diff --git a/bittensor/extrinsics/network.py b/bittensor/extrinsics/network.py index 3e0c3d8661..c03e5cf77b 100644 --- a/bittensor/extrinsics/network.py +++ b/bittensor/extrinsics/network.py @@ -15,11 +15,39 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. + import time -import bittensor +import substrateinterface from rich.prompt import Confirm +import bittensor +from bittensor.utils import format_error_message +from ..commands.network import HYPERPARAMS + + +def _find_event_attributes_in_extrinsic_receipt( + response: "substrateinterface.base.ExtrinsicReceipt", event_name: str +) -> list: + """ + Searches for the attributes of a specified event within an extrinsic receipt. + + Args: + response (substrateinterface.base.ExtrinsicReceipt): The receipt of the extrinsic to be searched. + event_name (str): The name of the event to search for. + + Returns: + list: A list of attributes for the specified event. Returns [-1] if the event is not found. + """ + for event in response.triggered_events: + # Access the event details + event_details = event.value["event"] + # Check if the event_id is 'NetworkAdded' + if event_details["event_id"] == event_name: + # Once found, you can access the attributes of the event_name + return event_details["attributes"] + return [-1] + def register_subnetwork_extrinsic( subtensor: "bittensor.subtensor", @@ -86,15 +114,13 @@ def register_subnetwork_extrinsic( response.process_events() if not response.is_success: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format( - response.error_message - ) + f":cross_mark: [red]Failed[/red]: {format_error_message(response.error_message)}" ) time.sleep(0.5) # Successful registration, final check for membership else: - attributes = find_event_attributes_in_extrinsic_receipt( + attributes = _find_event_attributes_in_extrinsic_receipt( response, "NetworkAdded" ) bittensor.__console__.print( @@ -103,20 +129,6 @@ def register_subnetwork_extrinsic( return True -def find_event_attributes_in_extrinsic_receipt(response, event_name) -> list: - for event in response.triggered_events: - # Access the event details - event_details = event.value["event"] - # Check if the event_id is 'NetworkAdded' - if event_details["event_id"] == event_name: - # Once found, you can access the attributes of the event_name - return event_details["attributes"] - return [-1] - - -from ..commands.network import HYPERPARAMS - - def set_hyperparameter_extrinsic( subtensor: "bittensor.subtensor", wallet: "bittensor.wallet", @@ -158,7 +170,7 @@ def set_hyperparameter_extrinsic( wallet.coldkey # unlock coldkey extrinsic = HYPERPARAMS.get(parameter) - if extrinsic == None: + if extrinsic is None: bittensor.__console__.print( ":cross_mark: [red]Invalid hyperparameter specified.[/red]" ) @@ -198,9 +210,7 @@ def set_hyperparameter_extrinsic( response.process_events() if not response.is_success: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format( - response.error_message - ) + f":cross_mark: [red]Failed[/red]: {format_error_message(response.error_message)}" ) time.sleep(0.5) diff --git a/bittensor/extrinsics/prometheus.py b/bittensor/extrinsics/prometheus.py index 350817e11f..97f7c17714 100644 --- a/bittensor/extrinsics/prometheus.py +++ b/bittensor/extrinsics/prometheus.py @@ -15,6 +15,7 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. + import bittensor import json @@ -54,7 +55,7 @@ def prometheus_extrinsic( """ # ---- Get external ip ---- - if ip == None: + if ip is None: try: external_ip = net.get_external_ip() bittensor.__console__.print( @@ -125,7 +126,7 @@ def prometheus_extrinsic( ) if wait_for_inclusion or wait_for_finalization: - if success == True: + if success is True: bittensor.__console__.print( ":white_heavy_check_mark: [green]Served prometheus[/green]\n [bold white]{}[/bold white]".format( json.dumps(call_params, indent=4, sort_keys=True) @@ -133,11 +134,7 @@ def prometheus_extrinsic( ) return True else: - bittensor.__console__.print( - ":cross_mark: [green]Failed to serve prometheus[/green] error: {}".format( - err - ) - ) + bittensor.__console__.print(f":cross_mark: [red]Failed[/red]: {err}") return False else: return True diff --git a/bittensor/extrinsics/registration.py b/bittensor/extrinsics/registration.py index 879214ad92..c7534c131a 100644 --- a/bittensor/extrinsics/registration.py +++ b/bittensor/extrinsics/registration.py @@ -16,10 +16,13 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -import bittensor import time -from rich.prompt import Confirm from typing import List, Union, Optional, Tuple + +from rich.prompt import Confirm + +import bittensor +from bittensor.utils import format_error_message from bittensor.utils.registration import ( POWSolution, create_pow, @@ -171,16 +174,17 @@ def register_extrinsic( ) success, err_msg = result - if success != True or success == False: - if "key is already registered" in err_msg: - # Error meant that the key is already registered. + if not success: + # Look error here + # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs + if "HotKeyAlreadyRegisteredInSubNet" in err_msg: bittensor.__console__.print( f":white_heavy_check_mark: [green]Already Registered on [bold]subnet:{netuid}[/bold][/green]" ) return True bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(err_msg) + f":cross_mark: [red]Failed[/red]: {err_msg}" ) time.sleep(0.5) @@ -290,10 +294,8 @@ def burned_register_extrinsic( wait_for_finalization=wait_for_finalization, ) - if success != True or success == False: - bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(err_msg) - ) + if not success: + bittensor.__console__.print(f":cross_mark: [red]Failed[/red]: {err_msg}") time.sleep(0.5) return False # Successful registration, final check for neuron and pubkey @@ -454,7 +456,7 @@ def run_faucet_extrinsic( response.process_events() if not response.is_success: bittensor.__console__.print( - f":cross_mark: [red]Failed[/red]: Error: {response.error_message}" + f":cross_mark: [red]Failed[/red]: {format_error_message(response.error_message)}" ) if attempts == max_allowed_attempts: raise MaxAttemptsException @@ -506,10 +508,8 @@ def swap_hotkey_extrinsic( wait_for_finalization=wait_for_finalization, ) - if success != True or success == False: - bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(err_msg) - ) + if not success: + bittensor.__console__.print(f":cross_mark: [red]Failed[/red]: {err_msg}") time.sleep(0.5) return False diff --git a/bittensor/extrinsics/root.py b/bittensor/extrinsics/root.py index 2cb11bbd69..2e1e4d3dab 100644 --- a/bittensor/extrinsics/root.py +++ b/bittensor/extrinsics/root.py @@ -77,10 +77,8 @@ def root_register_extrinsic( wait_for_finalization=wait_for_finalization, ) - if success != True or success == False: - bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(err_msg) - ) + if not success: + bittensor.__console__.print(f":cross_mark: [red]Failed[/red]: {err_msg}") time.sleep(0.5) # Successful registration, final check for neuron and pubkey @@ -205,7 +203,7 @@ def set_root_weights_extrinsic( return True else: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(error_message) + f":cross_mark: [red]Failed[/red]: {error_message}" ) bittensor.logging.warning( prefix="Set weights", diff --git a/bittensor/extrinsics/senate.py b/bittensor/extrinsics/senate.py index 233a78d614..043233996c 100644 --- a/bittensor/extrinsics/senate.py +++ b/bittensor/extrinsics/senate.py @@ -16,12 +16,13 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -# Imports -import bittensor - import time + from rich.prompt import Confirm +import bittensor +from bittensor.utils import format_error_message + def register_senate_extrinsic( subtensor: "bittensor.subtensor", @@ -78,9 +79,7 @@ def register_senate_extrinsic( response.process_events() if not response.is_success: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format( - response.error_message - ) + f":cross_mark: [red]Failed[/red]:{format_error_message(response.error_message)}" ) time.sleep(0.5) @@ -155,9 +154,7 @@ def leave_senate_extrinsic( response.process_events() if not response.is_success: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format( - response.error_message - ) + f":cross_mark: [red]Failed[/red]: {format_error_message(response.error_message)}" ) time.sleep(0.5) @@ -240,9 +237,7 @@ def vote_senate_extrinsic( response.process_events() if not response.is_success: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format( - response.error_message - ) + f":cross_mark: [red]Failed[/red]: {format_error_message(response.error_message)}" ) time.sleep(0.5) diff --git a/bittensor/extrinsics/serving.py b/bittensor/extrinsics/serving.py index 1aefa091ad..bba5367de1 100644 --- a/bittensor/extrinsics/serving.py +++ b/bittensor/extrinsics/serving.py @@ -15,10 +15,16 @@ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. + import json +from typing import Optional + +from retry import retry +from rich.prompt import Confirm + import bittensor import bittensor.utils.networking as net -from rich.prompt import Confirm +from bittensor.utils import format_error_message from ..errors import MetadataError @@ -123,15 +129,13 @@ def serve_extrinsic( ) if wait_for_inclusion or wait_for_finalization: - if success == True: + if success is True: bittensor.logging.debug( f"Axon served with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) on {subtensor.network}:{netuid} " ) return True else: - bittensor.logging.debug( - f"Axon failed to served with error: {error_message} " - ) + bittensor.logging.error(f"Failed: {error_message}") return False else: return True @@ -167,7 +171,7 @@ def serve_axon_extrinsic( external_port = axon.external_port # ---- Get external ip ---- - if axon.external_ip == None: + if axon.external_ip is None: try: external_ip = net.get_external_ip() bittensor.__console__.print( @@ -204,7 +208,7 @@ def publish_metadata( subtensor: "bittensor.subtensor", wallet: "bittensor.wallet", netuid: int, - type: str, + data_type: str, data: bytes, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, @@ -219,7 +223,7 @@ def publish_metadata( The wallet object used for authentication in the transaction. netuid (int): Network UID on which the metadata is to be published. - type (str): + data_type (str): The data type of the information being submitted. It should be one of the following: ``'Sha256'``, ``'Blake256'``, ``'Keccak256'``, or ``'Raw0-128'``. This specifies the format or hashing algorithm used for the data. data (str): The actual metadata content to be published. This should be formatted or hashed according to the ``type`` specified. (Note: max ``str`` length is 128 bytes) @@ -243,7 +247,10 @@ def publish_metadata( call = substrate.compose_call( call_module="Commitments", call_function="set_commitment", - call_params={"netuid": netuid, "info": {"fields": [[{f"{type}": data}]]}}, + call_params={ + "netuid": netuid, + "info": {"fields": [[{f"{data_type}": data}]]}, + }, ) extrinsic = substrate.create_signed_extrinsic(call=call, keypair=wallet.hotkey) @@ -259,11 +266,7 @@ def publish_metadata( if response.is_success: return True else: - raise MetadataError(response.error_message) - - -from retry import retry -from typing import Optional + raise MetadataError(format_error_message(response.error_message)) def get_metadata(self, netuid: int, hotkey: str, block: Optional[int] = None) -> str: @@ -274,7 +277,7 @@ def make_substrate_call_with_retry(): module="Commitments", storage_function="CommitmentOf", params=[netuid, hotkey], - block_hash=None if block == None else substrate.get_block_hash(block), + block_hash=None if block is None else substrate.get_block_hash(block), ) commit_data = make_substrate_call_with_retry() diff --git a/bittensor/extrinsics/set_weights.py b/bittensor/extrinsics/set_weights.py index 5db0a1a7a9..dc3052d0a0 100644 --- a/bittensor/extrinsics/set_weights.py +++ b/bittensor/extrinsics/set_weights.py @@ -44,7 +44,7 @@ def set_weights_extrinsic( r"""Sets the given weights and values on chain for wallet hotkey account. Args: - subtensor_endpoint (bittensor.subtensor): + subtensor (bittensor.subtensor): Subtensor endpoint to use. wallet (bittensor.wallet): Bittensor wallet object. @@ -109,7 +109,7 @@ def set_weights_extrinsic( if not wait_for_finalization and not wait_for_inclusion: return True, "Not waiting for finalization or inclusion." - if success == True: + if success is True: bittensor.__console__.print( ":white_heavy_check_mark: [green]Finalized[/green]" ) @@ -119,12 +119,10 @@ def set_weights_extrinsic( ) return True, "Successfully set weights and Finalized." else: - bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(error_message) - ) - bittensor.logging.warning( + bittensor.logging.error( + msg=error_message, prefix="Set weights", - suffix="Failed: " + str(error_message), + suffix="Failed: ", ) return False, error_message diff --git a/bittensor/extrinsics/staking.py b/bittensor/extrinsics/staking.py index f3249a8b1c..44a509eae8 100644 --- a/bittensor/extrinsics/staking.py +++ b/bittensor/extrinsics/staking.py @@ -92,7 +92,7 @@ def add_stake_extrinsic( ) # Convert to bittensor.Balance - if amount == None: + if amount is None: # Stake it all. staking_balance = bittensor.Balance.from_tao(old_balance.tao) elif not isinstance(amount, bittensor.Balance): @@ -148,7 +148,7 @@ def add_stake_extrinsic( wait_for_finalization=wait_for_finalization, ) - if staking_response == True: # If we successfully staked. + if staking_response is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True diff --git a/bittensor/extrinsics/transfer.py b/bittensor/extrinsics/transfer.py index ae09803199..91ef3237eb 100644 --- a/bittensor/extrinsics/transfer.py +++ b/bittensor/extrinsics/transfer.py @@ -130,7 +130,7 @@ def transfer_extrinsic( explorer_urls = bittensor.utils.get_explorer_url_for_network( subtensor.network, block_hash, bittensor.__network_explorer_map__ ) - if explorer_urls != {}: + if explorer_urls != {} and explorer_urls: bittensor.__console__.print( "[green]Opentensor Explorer Link: {}[/green]".format( explorer_urls.get("opentensor") @@ -142,9 +142,7 @@ def transfer_extrinsic( ) ) else: - bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: error:{}".format(err_msg) - ) + bittensor.__console__.print(f":cross_mark: [red]Failed[/red]: {err_msg}") if success: with bittensor.__console__.status(":satellite: Checking Balance..."): diff --git a/bittensor/extrinsics/unstaking.py b/bittensor/extrinsics/unstaking.py index 6046124f40..cf47b07928 100644 --- a/bittensor/extrinsics/unstaking.py +++ b/bittensor/extrinsics/unstaking.py @@ -142,7 +142,7 @@ def unstake_extrinsic( ) # Convert to bittensor.Balance - if amount == None: + if amount is None: # Unstake it all. unstaking_balance = old_stake elif not isinstance(amount, bittensor.Balance): @@ -189,7 +189,7 @@ def unstake_extrinsic( wait_for_finalization=wait_for_finalization, ) - if staking_response == True: # If we successfully unstaked. + if staking_response is True: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -221,7 +221,7 @@ def unstake_extrinsic( return True else: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: Error unknown." + ":cross_mark: [red]Failed[/red]: Unknown Error." ) return False @@ -318,7 +318,7 @@ def unstake_multiple_extrinsic( zip(hotkey_ss58s, amounts, old_stakes) ): # Covert to bittensor.Balance - if amount == None: + if amount is None: # Unstake it all. unstaking_balance = old_stake elif not isinstance(amount, bittensor.Balance): @@ -365,7 +365,7 @@ def unstake_multiple_extrinsic( wait_for_finalization=wait_for_finalization, ) - if staking_response == True: # If we successfully unstaked. + if staking_response is True: # If we successfully unstaked. # We only wait here if we expect finalization. if idx < len(hotkey_ss58s) - 1: @@ -405,7 +405,7 @@ def unstake_multiple_extrinsic( successful_unstakes += 1 else: bittensor.__console__.print( - ":cross_mark: [red]Failed[/red]: Error unknown." + ":cross_mark: [red]Failed[/red]: Unknown Error." ) continue diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 1b422ad276..d309a79504 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -41,7 +41,7 @@ import bittensor from bittensor.btlogging import logging as _logger -from bittensor.utils import torch, weight_utils +from bittensor.utils import torch, weight_utils, format_error_message from .chain_data import ( NeuronInfo, DelegateInfo, @@ -774,6 +774,7 @@ def send_extrinsic( ############### # Set Weights # ############### + # TODO: still needed? Can't find any usage of this method. def set_weights( self, wallet: "bittensor.wallet", @@ -900,7 +901,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True, "Successfully set weights." else: - return False, response.error_message + return False, format_error_message(response.error_message) return make_substrate_call_with_retry() @@ -1170,7 +1171,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True, None else: - return False, response.error_message + return False, format_error_message(response.error_message) return make_substrate_call_with_retry() @@ -1425,7 +1426,7 @@ def make_substrate_call_with_retry(): # process if registration successful, try again if pow is still valid response.process_events() if not response.is_success: - return False, response.error_message + return False, format_error_message(response.error_message) # Successful registration else: return True, None @@ -1482,7 +1483,7 @@ def make_substrate_call_with_retry(): # process if registration successful, try again if pow is still valid response.process_events() if not response.is_success: - return False, response.error_message + return False, format_error_message(response.error_message) # Successful registration else: return True, None @@ -1538,7 +1539,7 @@ def make_substrate_call_with_retry(): # process if registration successful, try again if pow is still valid response.process_events() if not response.is_success: - return False, response.error_message + return False, format_error_message(response.error_message) # Successful registration else: return True, None @@ -1691,7 +1692,7 @@ def make_substrate_call_with_retry(): block_hash = response.block_hash return True, block_hash, None else: - return False, None, response.error_message + return False, None, format_error_message(response.error_message) return make_substrate_call_with_retry() @@ -1922,7 +1923,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True, None else: - return False, response.error_message + return False, format_error_message(response.error_message) else: return True, None @@ -1984,7 +1985,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True, None else: - return False, response.error_message + return False, format_error_message(response.error_message) else: return True, None @@ -2165,7 +2166,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise StakeError(response.error_message) + raise StakeError(format_error_message(response.error_message)) return make_substrate_call_with_retry() @@ -2292,7 +2293,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise StakeError(response.error_message) + raise StakeError(format_error_message(response.error_message)) return make_substrate_call_with_retry() @@ -2608,7 +2609,7 @@ def make_substrate_call_with_retry(): # process if registration successful, try again if pow is still valid response.process_events() if not response.is_success: - return False, response.error_message + return False, format_error_message(response.error_message) # Successful registration else: return True, None @@ -5093,7 +5094,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise StakeError(response.error_message) + raise StakeError(format_error_message(response.error_message)) return make_substrate_call_with_retry() @@ -5147,7 +5148,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise StakeError(response.error_message) + raise StakeError(format_error_message(response.error_message)) return make_substrate_call_with_retry() @@ -5194,7 +5195,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise NominationError(response.error_message) + raise NominationError(format_error_message(response.error_message)) return make_substrate_call_with_retry() @@ -5249,7 +5250,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise TakeError(response.error_message) + raise TakeError(format_error_message(response.error_message)) return make_substrate_call_with_retry() @@ -5304,7 +5305,7 @@ def make_substrate_call_with_retry(): if response.is_success: return True else: - raise TakeError(response.error_message) + raise TakeError(format_error_message(response.error_message)) return make_substrate_call_with_retry() diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 175094da87..700a656131 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -1,7 +1,6 @@ # The MIT License (MIT) # Copyright © 2022 Opentensor Foundation # Copyright © 2023 Opentensor Technologies Inc -import os # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated # documentation files (the “Software”), to deal in the Software without restriction, including without limitation @@ -17,17 +16,16 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +import hashlib from typing import Callable, List, Dict, Literal, Tuple -import bittensor -import hashlib -import requests -import scalecodec import numpy as np +import scalecodec -from .wallet_utils import * # noqa F401 -from .version import version_checking, check_version, VersionCheckError +import bittensor from .registration import torch, use_torch +from .version import version_checking, check_version, VersionCheckError +from .wallet_utils import * # noqa F401 RAOPERTAO = 1e9 U16_MAX = 65535 @@ -260,3 +258,25 @@ def hash(content, encoding="utf-8"): # Produce the hash return sha3.hexdigest() + + +def format_error_message(error_message: dict) -> str: + """ + Formats an error message from the Subtensor error information to using in extrinsics. + + Args: + error_message (dict): A dictionary containing the error information from Subtensor. + + Returns: + str: A formatted error message string. + """ + err_type = "UnknownType" + err_name = "UnknownError" + err_description = "Unknown Description" + + if isinstance(error_message, dict): + err_type = error_message.get("type", err_type) + err_name = error_message.get("name", err_name) + err_docs = error_message.get("docs", []) + err_description = err_docs[0] if len(err_docs) > 0 else err_description + return f"Subtensor returned `{err_name} ({err_type})` error. This means: `{err_description}`" diff --git a/tests/unit_tests/extrinsics/test_init.py b/tests/unit_tests/extrinsics/test_init.py new file mode 100644 index 0000000000..8e3caaf900 --- /dev/null +++ b/tests/unit_tests/extrinsics/test_init.py @@ -0,0 +1,49 @@ +"""Tests for bittensor/extrinsics/__ini__ module.""" + +from bittensor.utils import format_error_message + + +def test_format_error_message_with_right_error_message(): + # Prep + fake_error_message = { + "type": "SomeType", + "name": "SomeErrorName", + "docs": ["Some error description."], + } + + # Call + result = format_error_message(fake_error_message) + + # Assertions + + assert "SomeType" in result + assert "SomeErrorName" in result + assert "Some error description." in result + + +def test_format_error_message_with_empty_error_message(): + # Prep + fake_error_message = {} + + # Call + result = format_error_message(fake_error_message) + + # Assertions + + assert "UnknownType" in result + assert "UnknownError" in result + assert "Unknown Description" in result + + +def test_format_error_message_with_wrong_type_error_message(): + # Prep + fake_error_message = None + + # Call + result = format_error_message(fake_error_message) + + # Assertions + + assert "UnknownType" in result + assert "UnknownError" in result + assert "Unknown Description" in result diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index 5a4d32dff6..49805f0cf4 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -369,7 +369,7 @@ def test_register_extrinsic_with_pow( ), patch.object( mock_subtensor, "_do_pow_register", - return_value=(registration_success, "key is already registered"), + return_value=(registration_success, "HotKeyAlreadyRegisteredInSubNet"), ), patch("torch.cuda.is_available", return_value=cuda): # Act if pow_success: diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index bf975e195a..7aa3ebf5b4 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -365,7 +365,7 @@ def test_publish_metadata( subtensor=mock_subtensor, wallet=mock_wallet, netuid=net_uid, - type=type_u, + data_type=type_u, data=data, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, From 2b2ed667d84579f0b02bfe649a8339944e060da8 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 4 Jun 2024 17:26:46 -0700 Subject: [PATCH 2/2] Set test timeout in 'config.yaml' --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7ea959790e..a4d07de74b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -108,9 +108,9 @@ jobs: . .venv/bin/activate ./scripts/create_wallet.sh - # TODO: Update test durations on different runs - run: name: Unit Tests + no_output_timeout: 20m command: | . .venv/bin/activate export PYTHONUNBUFFERED=1 @@ -122,6 +122,7 @@ jobs: - run: name: Integration Tests + no_output_timeout: 30m command: | . .venv/bin/activate export PYTHONUNBUFFERED=1