From ac9cd1cdc57b39ff4ccded228b8c728956f8e7c6 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 28 Aug 2024 23:02:16 +0200 Subject: [PATCH 1/3] [WIP] --- bittensor_cli/cli.py | 22 +++- bittensor_cli/src/commands/root.py | 138 +++++++++++++++-------- bittensor_cli/src/subtensor_interface.py | 7 +- tests/e2e_tests/conftest.py | 4 +- 4 files changed, 119 insertions(+), 52 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 386841a6..cfd4af6e 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1796,6 +1796,10 @@ def root_get_weights( self, network: Optional[str] = Options.network, chain: Optional[str] = Options.chain, + limit_min_col: int = typer.Option(0), + limit_max_col: int = typer.Option(-1), + reuse_last: bool = Options.reuse_last, + html_output: bool = Options.html_output, ): """ # root get-weights @@ -1844,8 +1848,24 @@ def root_get_weights( network. It offers transparency into how network rewards and responsibilities are allocated across different subnets. """ + if (reuse_last or html_output) and self.config.get("no_cache") is True: + err_console.print( + "Unable to use `--reuse-last` or `--html` when config no-cache is set." + ) + raise typer.Exit() + if not reuse_last: + subtensor = self.initialize_chain(network, chain) + else: + subtensor = None return self._run_command( - root.get_weights(self.initialize_chain(network, chain)) + root.get_weights( + subtensor, + limit_min_col, + limit_max_col, + reuse_last, + html_output, + self.config.get("no_cache", False), + ) ) def root_boost( diff --git a/bittensor_cli/src/commands/root.py b/bittensor_cli/src/commands/root.py index 93482dc8..ccc8a203 100644 --- a/bittensor_cli/src/commands/root.py +++ b/bittensor_cli/src/commands/root.py @@ -1,4 +1,5 @@ import asyncio +import json from typing import TypedDict, Optional from rich import box import numpy as np @@ -30,6 +31,10 @@ get_delegates_details_from_github, convert_weight_uids_and_vals_to_tensor, ss58_to_vec_u8, + create_table, + render_table, + update_metadata_table, + get_metadata_table, ) from bittensor_cli.src import Constants @@ -818,68 +823,103 @@ async def set_weights( ) -async def get_weights(subtensor: SubtensorInterface): +async def get_weights( + subtensor: SubtensorInterface, + limit_min_col: int, + limit_max_col: int, + reuse_last: bool, + html_output: bool, + no_cache: bool, +): """Get weights for root network.""" - with console.status(":satellite: Synchronizing with chain..."): - weights = await subtensor.weights(0) + if not reuse_last: + with console.status(":satellite: Synchronizing with chain..."): + weights = await subtensor.weights(0) - uid_to_weights: dict[int, dict] = {} - netuids = set() - for matrix in weights: - [uid, weights_data] = matrix - - if not len(weights_data): - uid_to_weights[uid] = {} - normalized_weights = [] - else: - normalized_weights = np.array(weights_data)[:, 1] / max( - np.sum(weights_data, axis=0)[1], 1 - ) + uid_to_weights: dict[int, dict] = {} + netuids = set() + for matrix in weights: + [uid, weights_data] = matrix - for weight_data, normalized_weight in zip(weights_data, normalized_weights): - [netuid, _] = weight_data - netuids.add(netuid) - if uid not in uid_to_weights: + if not len(weights_data): uid_to_weights[uid] = {} + normalized_weights = [] + else: + normalized_weights = np.array(weights_data)[:, 1] / max( + np.sum(weights_data, axis=0)[1], 1 + ) - uid_to_weights[uid][netuid] = normalized_weight - - table = Table( - show_footer=True, - box=None, - pad_edge=False, - width=None, - title="[white]Root Network Weights", - ) - table.add_column( - "[white]UID", - header_style="overline white", - footer_style="overline white", - style="rgb(50,163,219)", - no_wrap=True, - ) - for netuid in netuids: + for weight_data, normalized_weight in zip(weights_data, normalized_weights): + [netuid, _] = weight_data + netuids.add(netuid) + if uid not in uid_to_weights: + uid_to_weights[uid] = {} + + uid_to_weights[uid][netuid] = normalized_weight + rows: list[list[str]] = [] + for uid in uid_to_weights: + row = [str(uid)] + + uid_weights = uid_to_weights[uid] + for netuid in netuids: + if netuid in uid_weights: + row.append("{:0.2f}%".format(uid_weights[netuid] * 100)) + else: + row.append("~") + + if not no_cache: + db_cols = [("UID", "INTEGER")] + for netuid in netuids: + db_cols.append((f"_{netuid}", "TEXT")) + create_table("rootgetweights", db_cols, rows) + update_metadata_table( + "rootgetweights", + {"rows": json.dumps(rows), "netuids": json.dumps(netuids)}, + ) + else: + metadata = get_metadata_table("rootgetweights") + rows = json.loads(metadata["rows"]) + netuids = json.loads(metadata["netuids"]) + + if not html_output: + table = Table( + show_footer=True, + box=None, + pad_edge=False, + width=None, + title="[white]Root Network Weights", + ) table.add_column( - f"[white]{netuid}", + "[white]UID", header_style="overline white", footer_style="overline white", - justify="right", - style="green", + style="rgb(50,163,219)", no_wrap=True, ) + for netuid in netuids: + table.add_column( + f"[white]{netuid}", + header_style="overline white", + footer_style="overline white", + justify="right", + style="green", + no_wrap=True, + ) - for uid in uid_to_weights: - row = [str(uid)] + for row in rows: + table.add_row(*row) - uid_weights = uid_to_weights[uid] - for netuid in netuids: - if netuid in uid_weights: - row.append("{:0.2f}%".format(uid_weights[netuid] * 100)) - else: - row.append("~") - table.add_row(*row) + return console.print(table) - return console.print(table) + else: + html_cols = [{"title": "UID", "field": "UID"}] + for netuid in netuids: + html_cols.append({"title": str(netuid), "field": str(netuid)}) + render_table( + "rootgetweights", + "Root Network Weights", + html_cols, + ) async def _get_my_weights( diff --git a/bittensor_cli/src/subtensor_interface.py b/bittensor_cli/src/subtensor_interface.py index eb546bfc..10e3a700 100644 --- a/bittensor_cli/src/subtensor_interface.py +++ b/bittensor_cli/src/subtensor_interface.py @@ -24,7 +24,12 @@ ) from bittensor_cli.src.bittensor.balances import Balance from bittensor_cli.src import Constants, defaults, TYPE_REGISTRY -from bittensor_cli.src.utils import ss58_to_vec_u8, format_error_message, console, err_console +from bittensor_cli.src.utils import ( + ss58_to_vec_u8, + format_error_message, + console, + err_console, +) class ParamWithTypes(TypedDict): diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index dcf6de94..b2d3cbc4 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -8,7 +8,9 @@ import pytest -from bittensor_cli.src.bittensor.async_substrate_interface import AsyncSubstrateInterface +from bittensor_cli.src.bittensor.async_substrate_interface import ( + AsyncSubstrateInterface, +) # Fixture for setting up and tearing down a localnet.sh chain between tests From 97bd5a027aee34bb0331e9f0a4180bc912f6d1bf Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 28 Aug 2024 23:14:26 +0200 Subject: [PATCH 2/3] Removed `bin/btcli`. No need for it. --- bin/btcli | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 bin/btcli diff --git a/bin/btcli b/bin/btcli deleted file mode 100644 index b21c3e32..00000000 --- a/bin/btcli +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -import logging -from bittensor_cli.cli import CLIManager - - -def main(): - try: - manager = CLIManager() - manager.run() - except KeyboardInterrupt: - print('KeyboardInterrupt') - except RuntimeError as e: - logging.error(f'RuntimeError: {e}') - - -if __name__ == '__main__': - main() From d41e4149297b66eb23bf69e7f39881d6ea398d0d Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Thu, 29 Aug 2024 14:14:54 +0200 Subject: [PATCH 3/3] Slicing working for `root get-weights` --- bittensor_cli/cli.py | 14 +++++++++-- bittensor_cli/src/commands/root.py | 27 +++++++++++++++------ bittensor_cli/src/commands/wallets.py | 6 ++++- tests/e2e_tests/test_staking_sudo.py | 12 +++++---- tests/e2e_tests/test_wallet_interactions.py | 8 +++--- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index cfd4af6e..1999b9f2 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1796,8 +1796,18 @@ def root_get_weights( self, network: Optional[str] = Options.network, chain: Optional[str] = Options.chain, - limit_min_col: int = typer.Option(0), - limit_max_col: int = typer.Option(-1), + limit_min_col: Optional[int] = typer.Option( + None, + "--limit-min-col", + "--min", + help="Limit left display of the table to this column.", + ), + limit_max_col: Optional[int] = typer.Option( + None, + "--limit-max-col", + "--max", + help="Limit right display of the table to this column.", + ), reuse_last: bool = Options.reuse_last, html_output: bool = Options.html_output, ): diff --git a/bittensor_cli/src/commands/root.py b/bittensor_cli/src/commands/root.py index ccc8a203..fb989ea8 100644 --- a/bittensor_cli/src/commands/root.py +++ b/bittensor_cli/src/commands/root.py @@ -1,6 +1,6 @@ import asyncio import json -from typing import TypedDict, Optional +from typing import TypedDict, Optional, cast from rich import box import numpy as np from numpy.typing import NDArray @@ -825,8 +825,8 @@ async def set_weights( async def get_weights( subtensor: SubtensorInterface, - limit_min_col: int, - limit_max_col: int, + limit_min_col: Optional[int], + limit_max_col: Optional[int], reuse_last: bool, html_output: bool, no_cache: bool, @@ -866,12 +866,14 @@ async def get_weights( row.append("{:0.2f}%".format(uid_weights[netuid] * 100)) else: row.append("~") + rows.append(row) if not no_cache: db_cols = [("UID", "INTEGER")] for netuid in netuids: db_cols.append((f"_{netuid}", "TEXT")) create_table("rootgetweights", db_cols, rows) + netuids = list(netuids) update_metadata_table( "rootgetweights", {"rows": json.dumps(rows), "netuids": json.dumps(netuids)}, @@ -881,6 +883,14 @@ async def get_weights( rows = json.loads(metadata["rows"]) netuids = json.loads(metadata["netuids"]) + _min_lim = limit_min_col if limit_min_col is not None else 0 + _max_lim = limit_max_col + 1 if limit_max_col is not None else len(netuids) + _max_lim = min(_max_lim, len(netuids)) + + if _min_lim is not None and _min_lim > len(netuids): + err_console.print("Minimum limit greater than number of netuids") + return + if not html_output: table = Table( show_footer=True, @@ -896,7 +906,8 @@ async def get_weights( style="rgb(50,163,219)", no_wrap=True, ) - for netuid in netuids: + + for netuid in cast(list, netuids)[_min_lim:_max_lim]: table.add_column( f"[white]{netuid}", header_style="overline white", @@ -906,15 +917,17 @@ async def get_weights( no_wrap=True, ) + # Adding rows for row in rows: - table.add_row(*row) + new_row = [row[0]] + row[_min_lim:_max_lim] + table.add_row(*new_row) return console.print(table) else: html_cols = [{"title": "UID", "field": "UID"}] - for netuid in netuids: - html_cols.append({"title": str(netuid), "field": str(netuid)}) + for netuid in netuids[_min_lim:_max_lim]: + html_cols.append({"title": str(netuid), "field": f"_{netuid}"}) render_table( "rootgetweights", "Root Network Weights", diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index fd11d355..12d2c7b3 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -1151,7 +1151,11 @@ async def _filter_stake_info(stake_info: StakeInfo) -> bool: async def transfer( - wallet: Wallet, subtensor: SubtensorInterface, destination: str, amount: float, prompt: bool + wallet: Wallet, + subtensor: SubtensorInterface, + destination: str, + amount: float, + prompt: bool, ): """Transfer token of amount to destination.""" await transfer_extrinsic( diff --git a/tests/e2e_tests/test_staking_sudo.py b/tests/e2e_tests/test_staking_sudo.py index c78f656b..77db131f 100644 --- a/tests/e2e_tests/test_staking_sudo.py +++ b/tests/e2e_tests/test_staking_sudo.py @@ -55,7 +55,7 @@ def test_staking(local_chain): wallet_alice.name, "--network", "local", - "--no-prompt" + "--no-prompt", ], ) assert f"✅ Registered subnetwork with netuid: {netuid}" in result.stdout @@ -77,7 +77,7 @@ def test_staking(local_chain): netuid, "--chain", "ws://127.0.0.1:9945", - "--no-prompt" + "--no-prompt", ], ) assert "✅ Registered" in register_subnet.stdout @@ -99,7 +99,7 @@ def test_staking(local_chain): "ws://127.0.0.1:9945", "--amount", "100", - "--no-prompt" + "--no-prompt", ], ) assert "✅ Finalized" in add_stake.stdout @@ -120,7 +120,9 @@ def test_staking(local_chain): ], ) # Assert correct stake is added - cleaned_stake = [re.sub(r'\s+', ' ', line) for line in show_stake.stdout.splitlines()] + cleaned_stake = [ + re.sub(r"\s+", " ", line) for line in show_stake.stdout.splitlines() + ] stake_added = cleaned_stake[3].split()[4].strip("τ") assert Balance.from_tao(100) == Balance.from_tao(float(stake_added)) @@ -146,7 +148,7 @@ def test_staking(local_chain): "ws://127.0.0.1:9945", "--amount", "100", - "--no-prompt" + "--no-prompt", ], ) assert "✅ Finalized" in remove_stake.stdout diff --git a/tests/e2e_tests/test_wallet_interactions.py b/tests/e2e_tests/test_wallet_interactions.py index 0d28c5cb..fe1eb954 100644 --- a/tests/e2e_tests/test_wallet_interactions.py +++ b/tests/e2e_tests/test_wallet_interactions.py @@ -54,7 +54,7 @@ def test_wallet_overview_inspect(local_chain): wallet.name, "--network", "local", - "--no-prompt" + "--no-prompt", ], ) assert f"✅ Registered subnetwork with netuid: {netuid}" in result.stdout @@ -91,7 +91,7 @@ def test_wallet_overview_inspect(local_chain): "1", "--chain", "ws://127.0.0.1:9945", - "--no-prompt" + "--no-prompt", ], ) assert "✅ Registered" in register_subnet.stdout @@ -263,7 +263,7 @@ def test_wallet_transfer(local_chain): "local", "--amount", "100", - "--no-prompt" + "--no-prompt", ], ) @@ -350,7 +350,7 @@ def test_wallet_transfer(local_chain): "local", "--amount", "100", - "--no-prompt" + "--no-prompt", ], )