Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

btcli/bt.config: various improvements #2223

Open
wants to merge 11 commits into
base: staging
Choose a base branch
from
33 changes: 11 additions & 22 deletions bittensor/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
}
COMMANDS = {
"subnets": {
"name": "subnets",
"aliases": ["s", "subnet"],
"help": "Commands for managing and viewing subnetworks.",
"commands": {
Expand All @@ -120,7 +119,6 @@
},
},
"root": {
"name": "root",
"aliases": ["r", "roots"],
"help": "Commands for managing and viewing the root network.",
"commands": {
Expand All @@ -142,7 +140,6 @@
},
},
"wallet": {
"name": "wallet",
"aliases": ["w", "wallets"],
"help": "Commands for managing and viewing wallets.",
"commands": {
Expand All @@ -167,7 +164,6 @@
},
},
"stake": {
"name": "stake",
"aliases": ["st", "stakes"],
"help": "Commands for staking and removing stake and setting child hotkey accounts.",
"commands": {
Expand All @@ -182,7 +178,6 @@
},
},
"weights": {
"name": "weights",
"aliases": ["wt", "weight"],
"help": "Commands for managing weight for subnets.",
"commands": {
Expand All @@ -191,7 +186,6 @@
},
},
"sudo": {
"name": "sudo",
"aliases": ["su", "sudos"],
"help": "Commands for subnet management",
"commands": {
Expand All @@ -201,7 +195,6 @@
},
},
"legacy": {
"name": "legacy",
"aliases": ["l"],
"help": "Miscellaneous commands.",
"commands": {
Expand All @@ -210,7 +203,6 @@
},
},
"info": {
"name": "info",
"aliases": ["i"],
"help": "Instructions for enabling autocompletion for the CLI.",
"commands": {
Expand Down Expand Up @@ -304,21 +296,18 @@ def __create_parser__() -> "argparse.ArgumentParser":
# Add arguments for each sub-command.
cmd_parsers = parser.add_subparsers(dest="command")
# Add argument parsers for all available commands.
for command in COMMANDS.values():
if isinstance(command, dict):
subcmd_parser = cmd_parsers.add_parser(
name=command["name"],
aliases=command["aliases"],
help=command["help"],
)
subparser = subcmd_parser.add_subparsers(
help=command["help"], dest="subcommand", required=True
)
for name, command in COMMANDS.items():
subcmd_parser = cmd_parsers.add_parser(
name=name,
aliases=command["aliases"],
help=command["help"],
)
subparser = subcmd_parser.add_subparsers(
help=command["help"], dest="subcommand", required=True
)

for subcommand in command["commands"].values():
subcommand.add_args(subparser)
else:
command.add_args(cmd_parsers)
for subcommand in command["commands"].values():
subcommand.add_args(subparser)

return parser

Expand Down
59 changes: 39 additions & 20 deletions bittensor/commands/stake.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,25 @@


def get_netuid(
cli: "bittensor.cli", subtensor: "bittensor.subtensor"
cli: "bittensor.cli", subtensor: "bittensor.subtensor", prompt: bool = True
) -> Tuple[bool, int]:
"""Retrieve and validate the netuid from the user or configuration."""
console = Console()
if not cli.config.is_set("netuid"):
try:
cli.config.netuid = int(Prompt.ask("Enter netuid"))
except ValueError:
console.print(
"[red]Invalid input. Please enter a valid integer for netuid.[/red]"
)
return False, -1
if not cli.config.is_set("netuid") and prompt:
cli.config.netuid = Prompt.ask("Enter netuid")
try:
cli.config.netuid = int(cli.config.netuid)
except ValueError:
console.print(
"[red]Invalid input. Please enter a valid integer for netuid.[/red]"
)
return False, -1
netuid = cli.config.netuid
if netuid < 0 or netuid > 65535:
console.print(
"[red]Invalid input. Please enter a valid integer for netuid in subnet range.[/red]"
)
return False, -1
if not subtensor.subnet_exists(netuid=netuid):
console.print(
"[red]Network with netuid {} does not exist. Please try again.[/red]".format(
Expand Down Expand Up @@ -1136,10 +1142,27 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
wallet = bittensor.wallet(config=cli.config)

# check all
if not cli.config.is_set("all"):
exists, netuid = get_netuid(cli, subtensor)
if not exists:
return
if cli.config.is_set("all"):
cli.config.netuid = None
cli.config.all = True
elif cli.config.is_set("netuid"):
if cli.config.netuid == "all":
cli.config.all = True
else:
cli.config.netuid = int(cli.config.netuid)
exists, netuid = get_netuid(cli, subtensor)
if not exists:
return
else:
netuid_input = Prompt.ask("Enter netuid or 'all'", default="all")
if netuid_input == "all":
cli.config.netuid = None
cli.config.all = True
else:
cli.config.netuid = int(netuid_input)
exists, netuid = get_netuid(cli, subtensor, False)
if not exists:
return

# get parent hotkey
hotkey = get_hotkey(wallet, cli.config)
Expand All @@ -1148,11 +1171,7 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
return

try:
netuids = (
subtensor.get_all_subnet_netuids()
if cli.config.is_set("all")
else [netuid]
)
netuids = subtensor.get_all_subnet_netuids() if cli.config.all else [netuid]
hotkey_stake = GetChildrenCommand.get_parent_stake_info(
console, subtensor, hotkey
)
Expand Down Expand Up @@ -1236,7 +1255,7 @@ def add_args(parser: argparse.ArgumentParser):
parser = parser.add_parser(
"get_children", help="""Get child hotkeys on subnet."""
)
parser.add_argument("--netuid", dest="netuid", type=int, required=False)
parser.add_argument("--netuid", dest="netuid", type=str, required=False)
parser.add_argument("--hotkey", dest="hotkey", type=str, required=False)
parser.add_argument(
"--all",
Expand Down Expand Up @@ -1294,7 +1313,7 @@ def render_table(

# Add columns to the table with specific styles
table.add_column("Index", style="bold yellow", no_wrap=True, justify="center")
table.add_column("ChildHotkey", style="bold green")
table.add_column("Child Hotkey", style="bold green")
table.add_column("Proportion", style="bold cyan", no_wrap=True, justify="right")
table.add_column(
"Childkey Take", style="bold blue", no_wrap=True, justify="right"
Expand Down
154 changes: 81 additions & 73 deletions bittensor/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import copy
from copy import deepcopy
from munch import DefaultMunch
from typing import List, Optional, Dict, Any, TypeVar, Type
from typing import List, Optional, Dict, Any, TypeVar, Type, Callable
import argparse


Expand Down Expand Up @@ -64,7 +64,7 @@ def __init__(
self,
parser: argparse.ArgumentParser = None,
args: Optional[List[str]] = None,
strict: bool = False,
strict: bool = True,
default: Optional[Any] = None,
) -> None:
super().__init__(default)
Expand All @@ -74,50 +74,7 @@ def __init__(
if parser == None:
return None

# Optionally add config specific arguments
try:
parser.add_argument(
"--config",
type=str,
help="If set, defaults are overridden by passed file.",
)
except:
# this can fail if --config has already been added.
pass

try:
parser.add_argument(
"--strict",
action="store_true",
help="""If flagged, config will check that only exact arguments have been set.""",
default=False,
)
except:
# this can fail if --strict has already been added.
pass

try:
parser.add_argument(
"--no_version_checking",
action="store_true",
help="Set ``true`` to stop cli version checking.",
default=False,
)
except:
# this can fail if --no_version_checking has already been added.
pass

try:
parser.add_argument(
"--no_prompt",
dest="no_prompt",
action="store_true",
help="Set ``true`` to stop cli from prompting the user.",
default=False,
)
except:
# this can fail if --no_version_checking has already been added.
pass
config.apply_to_parser_recursive(parser, config.add_args)

# Get args from argv if not passed in.
if args == None:
Expand Down Expand Up @@ -182,37 +139,21 @@ def __init__(
default_param_args = [_config.get("command"), _config.get("subcommand")]

## Get all args by name
default_params = parser.parse_args(args=default_param_args)
parser_no_required = copy.deepcopy(parser)
for i in range(len(parser_no_required._actions)):
parser_no_required._actions[i].required = False
default_params = parser_no_required.parse_args(args=default_param_args)

all_default_args = default_params.__dict__.keys() | []
## Make a dict with keys as args and values as argparse.SUPPRESS
defaults_as_suppress = {key: argparse.SUPPRESS for key in all_default_args}
## Set the defaults to argparse.SUPPRESS, should remove them from the namespace
parser_no_defaults.set_defaults(**defaults_as_suppress)
parser_no_defaults._defaults.clear() # Needed for quirk of argparse

### Check for subparsers and do the same
if parser_no_defaults._subparsers != None:
for action in parser_no_defaults._subparsers._actions:
# Should only be the "command" subparser action
if isinstance(action, argparse._SubParsersAction):
# Set the defaults to argparse.SUPPRESS, should remove them from the namespace
# Each choice is the keyword for a command, we need to set the defaults for each of these
## Note: we also need to clear the _defaults dict for each, this is a quirk of argparse
cmd_parser: argparse.ArgumentParser
for cmd_parser in action.choices.values():
# If this choice is also a subparser, set defaults recursively
if cmd_parser._subparsers:
for action in cmd_parser._subparsers._actions:
# Should only be the "command" subparser action
if isinstance(action, argparse._SubParsersAction):
cmd_parser: argparse.ArgumentParser
for cmd_parser in action.choices.values():
cmd_parser.set_defaults(**defaults_as_suppress)
cmd_parser._defaults.clear() # Needed for quirk of argparse
else:
cmd_parser.set_defaults(**defaults_as_suppress)
cmd_parser._defaults.clear() # Needed for quirk of argparse

def local_set_defaults(local_parser: argparse.ArgumentParser):
## Set the defaults to argparse.SUPPRESS, should remove them from the namespace
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convert this to a dostring. Also, please make the function name l_set_defaults a little more descriptive. Example: load_set_defaults.
Also, add type annotation.

local_parser.set_defaults(**defaults_as_suppress)
local_parser._defaults.clear() # Needed for quirk of argparse

config.apply_to_parser_recursive(parser_no_defaults, local_set_defaults)

## Reparse the args, but this time with the defaults as argparse.SUPPRESS
params_no_defaults = config.__parse_args__(
Expand All @@ -231,6 +172,73 @@ def __init__(
]
}

@staticmethod
def apply_to_parser_recursive(
parser: argparse.ArgumentParser,
callback: Callable[[argparse.ArgumentParser], None],
depth: int = 0,
):
"""
Recursively apply callback() to parser and its subparsers.
"""
callback(parser)
if not parser._subparsers:
return
for action in parser._subparsers._actions:
if not isinstance(action, argparse._SubParsersAction):
continue
for cmd_parser in action.choices.values():
config.apply_to_parser_recursive(cmd_parser, callback, depth=depth + 1)

@staticmethod
def add_args(parser: argparse.ArgumentParser):
"""
Add standard arguments to argument parser.
"""
# Optionally add config specific arguments
try:
parser.add_argument(
"--config",
type=str,
help="If set, defaults are overridden by passed file.",
)
except:
# this can fail if --config has already been added.
pass

try:
parser.add_argument(
"--strict",
action="store_true",
help="If flagged, config will check that only exact arguments have been set.",
default=False,
)
except:
# this can fail if --strict has already been added.
pass

try:
parser.add_argument(
"--no_version_checking",
action="store_true",
help="Set ``true`` to stop cli version checking.",
default=False,
)
except:
# this can fail if --no_version_checking has already been added.
pass

try:
parser.add_argument(
"--no_prompt",
action="store_true",
help="Set ``true`` to stop cli from prompting the user.",
default=False,
)
except Exception as e:
# this can fail if --no_prompt has already been added.
pass

@staticmethod
def __split_params__(params: argparse.Namespace, _config: "config"):
# Splits params on dot syntax i.e neuron.axon_port and adds to _config
Expand Down
2 changes: 0 additions & 2 deletions tests/integration_tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2506,8 +2506,6 @@ def test_delegate(self, _):
"delegate",
"--subtensor.network",
"mock", # Mock network
"--wallet.name",
"mock",
"--delegate_ss58key",
delegate_wallet.hotkey.ss58_address,
"--amount",
Expand Down
Loading
Loading