From 2b5a39e97ed755d37ccbdbb5f80b265e631838bf Mon Sep 17 00:00:00 2001 From: Kieran Prasch Date: Tue, 12 May 2020 13:02:06 -0700 Subject: [PATCH] Nearly 100% of emitter messages from CLI actions are in literature.py --- nucypher/cli/actions/auth.py | 23 +++-- nucypher/cli/actions/config.py | 14 +-- nucypher/cli/actions/confirm.py | 2 +- nucypher/cli/actions/network.py | 15 ++- nucypher/cli/actions/select.py | 44 ++++++--- nucypher/cli/actions/utils.py | 16 ++- nucypher/cli/literature.py | 126 +++++++++++++++++++----- nucypher/cli/painting.py | 2 +- nucypher/config/base.py | 8 +- nucypher/config/characters.py | 10 +- tests/config/test_base_configuration.py | 6 +- 11 files changed, 191 insertions(+), 75 deletions(-) diff --git a/nucypher/cli/actions/auth.py b/nucypher/cli/actions/auth.py index e276845f9..1a11b9b09 100644 --- a/nucypher/cli/actions/auth.py +++ b/nucypher/cli/actions/auth.py @@ -15,18 +15,25 @@ You should have received a copy of the GNU Affero General Public License along with nucypher. If not, see . """ + + import click import os from constant_sorrow.constants import NO_PASSWORD from nacl.exceptions import CryptoError from nucypher.blockchain.eth.decorators import validate_checksum_address -from nucypher.cli.literature import COLLECT_ETH_PASSWORD, COLLECT_NUCYPHER_PASSWORD +from nucypher.cli.literature import ( + COLLECT_ETH_PASSWORD, + COLLECT_NUCYPHER_PASSWORD, + GENERIC_PASSWORD_PROMPT, + DECRYPTING_CHARACTER_KEYRING +) from nucypher.config.constants import NUCYPHER_ENVVAR_KEYRING_PASSWORD from nucypher.config.node import CharacterConfiguration -def get_password_from_prompt(prompt: str = "Enter password", envvar: str = '', confirm: bool = False) -> str: +def get_password_from_prompt(prompt: str = GENERIC_PASSWORD_PROMPT, envvar: str = '', confirm: bool = False) -> str: password = os.environ.get(envvar, NO_PASSWORD) if password is NO_PASSWORD: # Collect password, prefer env var password = click.prompt(prompt, confirmation_prompt=confirm, hide_input=True) @@ -50,14 +57,18 @@ def get_nucypher_password(confirm: bool = False, envvar=NUCYPHER_ENVVAR_KEYRING_ return keyring_password -def unlock_nucypher_keyring(emitter, password: str, character_configuration: CharacterConfiguration): - emitter.message(f'Decrypting {character_configuration._NAME} keyring...', color='yellow') +def unlock_nucypher_keyring(emitter, password: str, character_configuration: CharacterConfiguration) -> bool: + emitter.message(DECRYPTING_CHARACTER_KEYRING.format(name=character_configuration.NAME), color='yellow') + + # precondition if character_configuration.dev_mode: return True # Dev accounts are always unlocked - # NuCypher + # unlock try: character_configuration.attach_keyring() character_configuration.keyring.unlock(password=password) # Takes ~3 seconds, ~1GB Ram except CryptoError: - raise character_configuration.keyring.AuthenticationFailed \ No newline at end of file + raise character_configuration.keyring.AuthenticationFailed + else: + return True diff --git a/nucypher/cli/actions/config.py b/nucypher/cli/actions/config.py index d4990ceff..2b8883cf3 100644 --- a/nucypher/cli/actions/config.py +++ b/nucypher/cli/actions/config.py @@ -17,8 +17,9 @@ along with nucypher. If not, see . """ -import click import json + +import click from json.decoder import JSONDecodeError from nucypher.blockchain.eth.clients import NuCypherGethGoerliProcess @@ -26,7 +27,8 @@ from nucypher.cli.literature import ( CHARACTER_DESTRUCTION, SUCCESSFUL_DESTRUCTION, CONFIRM_FORGET_NODES, - SUCCESSFUL_FORGET_NODES + SUCCESSFUL_FORGET_NODES, INVALID_CONFIGURATION_FILE_WARNING, INVALID_JSON_IN_CONFIGURATION_WARNING, + SUCCESSFUL_UPDATE_CONFIGURATION_VALUES ) @@ -36,18 +38,18 @@ def get_or_update_configuration(emitter, config_class, filepath: str, config_opt config = config_class.from_configuration_file(filepath=filepath) except config_class.ConfigurationError: # Issue warning for invalid configuration... - emitter.message(f"Invalid Configuration at {filepath}.") + emitter.message(INVALID_CONFIGURATION_FILE_WARNING.format(filepath=filepath)) try: # ... but try to display it anyways response = config_class._read_configuration_file(filepath=filepath) return emitter.echo(json.dumps(response, indent=4)) except JSONDecodeError: # ... sorry - return emitter.message(f"Invalid JSON in Configuration File at {filepath}.") + return emitter.message(INVALID_JSON_IN_CONFIGURATION_WARNING.format(filepath=filepath)) else: updates = config_options.get_updates() if updates: - emitter.message(f"Updated configuration values: {', '.join(updates)}", color='yellow') + emitter.message(SUCCESSFUL_UPDATE_CONFIGURATION_VALUES.format(fields=', '.join(updates)), color='yellow') config.update(**updates) return emitter.echo(config.serialize()) @@ -57,7 +59,7 @@ def destroy_configuration(emitter, character_config, force: bool = False) -> Non try: database = character_config.db_filepath except AttributeError: - database = "No database found" + database = "No database found" # FIXME: This cannot be right..... click.confirm(CHARACTER_DESTRUCTION.format(name=character_config._NAME, root=character_config.config_root, diff --git a/nucypher/cli/actions/confirm.py b/nucypher/cli/actions/confirm.py index 8c7ead41d..9a060b736 100644 --- a/nucypher/cli/actions/confirm.py +++ b/nucypher/cli/actions/confirm.py @@ -17,8 +17,8 @@ along with nucypher. If not, see . """ import click - from constant_sorrow.constants import UNKNOWN_DEVELOPMENT_CHAIN_ID + from nucypher.blockchain.eth.token import NU from nucypher.cli.literature import ( RESTAKING_LOCK_AGREEMENT, diff --git a/nucypher/cli/actions/network.py b/nucypher/cli/actions/network.py index 85a46eb8f..2e76d83e9 100644 --- a/nucypher/cli/actions/network.py +++ b/nucypher/cli/actions/network.py @@ -26,8 +26,15 @@ from json.decoder import JSONDecodeError from typing import Set, Optional, Dict, List from nucypher.blockchain.eth.registry import BaseContractRegistry -from nucypher.cli.literature import CONFIRM_URSULA_IPV4_ADDRESS, COLLECT_URSULA_IPV4_ADDRESS, \ - FORCE_DETECT_URSULA_IP_WARNING, NO_DOMAIN_PEERS, SEEDNODE_NOT_STAKING_WARNING +from nucypher.cli.literature import ( + CONFIRM_URSULA_IPV4_ADDRESS, + COLLECT_URSULA_IPV4_ADDRESS, + FORCE_DETECT_URSULA_IP_WARNING, + NO_DOMAIN_PEERS, + SEEDNODE_NOT_STAKING_WARNING, + START_LOADING_SEEDNODES, + UNREADABLE_SEEDNODE_ADVISORY +) from nucypher.cli.types import IPV4_ADDRESS from nucypher.config.constants import DEFAULT_CONFIG_ROOT from nucypher.network.exceptions import NodeSeemsToBeDown @@ -100,7 +107,7 @@ def load_seednodes(emitter, """ # Heads up - emitter.message("Connecting to preferred teacher nodes...", color='yellow') + emitter.message(START_LOADING_SEEDNODES, color='yellow') from nucypher.characters.lawful import Ursula # Aggregate URIs (Ordered by Priority) @@ -119,7 +126,7 @@ def load_seednodes(emitter, network_middleware=network_middleware, registry=registry) except NodeSeemsToBeDown: - emitter.message(f"Failed to connect to teacher: {uri}") + emitter.message(UNREADABLE_SEEDNODE_ADVISORY.format(uri=uri)) continue except Teacher.NotStaking: emitter.message(SEEDNODE_NOT_STAKING_WARNING.format(uri=uri)) diff --git a/nucypher/cli/actions/select.py b/nucypher/cli/actions/select.py index 5d1a82906..c18542842 100644 --- a/nucypher/cli/actions/select.py +++ b/nucypher/cli/actions/select.py @@ -32,6 +32,19 @@ from nucypher.blockchain.eth.registry import InMemoryContractRegistry, Individua from nucypher.blockchain.eth.signers import Signer from nucypher.blockchain.eth.token import Stake, NU from nucypher.cli.config import extract_checksum_address_from_filepath +from nucypher.cli.literature import ( + NO_CONFIGURATIONS_ON_DISK, + SELECT_NETWORK, + IS_THIS_CORRECT, + PREALLOCATION_STAKE_ADVISORY, + SELECT_STAKING_ACCOUNT_INDEX, + GENERIC_SELECT_ACCOUNT, + NO_ETH_ACCOUNTS, + SELECT_STAKE, + NO_DIVISIBLE_STAKES, + ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE, + NO_STAKES_FOUND +) from nucypher.cli.painting import paint_stakes from nucypher.config.constants import DEFAULT_CONFIG_ROOT, NUCYPHER_ENVVAR_WORKER_ADDRESS @@ -43,19 +56,19 @@ def select_stake(stakeholder, emitter, divisible: bool = False, staker_address: else: stakes = stakeholder.all_stakes if not stakes: - emitter.echo(f"No stakes found.", color='red') + emitter.echo(NO_STAKES_FOUND, color='red') raise click.Abort stakes = sorted((stake for stake in stakes if stake.is_active), key=lambda s: s.address_index_ordering_key) if divisible: - emitter.echo("NOTE: Showing divisible stakes only", color='yellow') + emitter.echo(ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE, color='yellow') stakes = list(filter(lambda s: bool(s.value >= stakeholder.economics.minimum_allowed_locked*2), stakes)) # TODO: Move to method on Stake if not stakes: - emitter.echo(f"No divisible stakes found.", color='red') + emitter.echo(NO_DIVISIBLE_STAKES, color='red') raise click.Abort enumerated_stakes = dict(enumerate(stakes)) paint_stakes(stakeholder=stakeholder, emitter=emitter, staker_address=staker_address) - choice = click.prompt("Select Stake", type=click.IntRange(min=0, max=len(enumerated_stakes)-1)) + choice = click.prompt(SELECT_STAKE, type=click.IntRange(min=0, max=len(enumerated_stakes)-1)) chosen_stake = enumerated_stakes[choice] return chosen_stake @@ -97,7 +110,7 @@ def select_client_account(emitter, wallet_accounts = wallet.accounts enumerated_accounts = dict(enumerate(wallet_accounts)) if len(enumerated_accounts) < 1: - emitter.echo("No ETH accounts were found.", color='red', bold=True) + emitter.echo(NO_ETH_ACCOUNTS, color='red', bold=True) raise click.Abort() # Display account info @@ -127,7 +140,7 @@ def select_client_account(emitter, emitter.echo(tabulate(rows, headers=headers, showindex='always')) # Prompt the user for selection, and return - prompt = prompt or "Select index of account" + prompt = prompt or GENERIC_SELECT_ACCOUNT account_range = click.IntRange(min=0, max=len(enumerated_accounts)-1) choice = click.prompt(prompt, type=account_range, default=default) chosen_account = enumerated_accounts[choice] @@ -154,16 +167,15 @@ def handle_client_account_for_staking(emitter, if individual_allocation: client_account = individual_allocation.beneficiary_address staking_address = individual_allocation.contract_address - - message = f"Beneficiary {client_account} will use preallocation contract {staking_address} to stake." + message = PREALLOCATION_STAKE_ADVISORY.format(client_account=client_account, staking_address=staking_address) emitter.echo(message, color='yellow', verbosity=1) if not force: - click.confirm("Is this correct?", abort=True) + click.confirm(IS_THIS_CORRECT, abort=True) else: if staking_address: client_account = staking_address else: - client_account = select_client_account(prompt="Select index of staking account", + client_account = select_client_account(prompt=SELECT_STAKING_ACCOUNT_INDEX, emitter=emitter, registry=stakeholder.registry, network=stakeholder.network, @@ -177,7 +189,7 @@ def select_network(emitter) -> str: headers = ["Network"] rows = [[n] for n in NetworksInventory.NETWORKS] emitter.echo(tabulate(rows, headers=headers, showindex='always')) - choice = click.prompt("Select Network", default=0, type=click.IntRange(0, len(NetworksInventory.NETWORKS)-1)) + choice = click.prompt(SELECT_NETWORK, default=0, type=click.IntRange(0, len(NetworksInventory.NETWORKS)-1)) network = NetworksInventory.NETWORKS[choice] return network @@ -194,12 +206,12 @@ def select_config_file(emitter, config_root = config_root or DEFAULT_CONFIG_ROOT default_config_file = glob.glob(config_class.default_filepath(config_root=config_root)) - glob_pattern = f'{config_root}/{config_class._NAME}-0x*.{config_class._CONFIG_FILE_EXTENSION}' + glob_pattern = f'{config_root}/{config_class.NAME}-0x*.{config_class._CONFIG_FILE_EXTENSION}' secondary_config_files = glob.glob(glob_pattern) config_files = [*default_config_file, *secondary_config_files] if not config_files: - emitter.message(f"No {config_class._NAME.capitalize()} configurations found. " - f"run 'nucypher {config_class._NAME} init' then try again.", color='red') + emitter.message(NO_CONFIGURATIONS_ON_DISK.format(name=config_class.NAME.capitalize(), + command=config_class.NAME), color='red') raise click.Abort() checksum_address = checksum_address or os.environ.get(NUCYPHER_ENVVAR_WORKER_ADDRESS, None) # TODO: Deprecate worker_address in favor of checksum_address @@ -213,7 +225,7 @@ def select_config_file(emitter, try: config_file = parsed_addresses[checksum_address] except KeyError: - raise ValueError(f"'{checksum_address}' is not a known {config_class._NAME} configuration account.") + raise ValueError(f"'{checksum_address}' is not a known {config_class.NAME} configuration account.") elif len(config_files) > 1: @@ -228,7 +240,7 @@ def select_config_file(emitter, emitter.echo(tabulate(parsed_addresses, headers=headers, showindex='always')) # Prompt the user for selection, and return - prompt = f"Select {config_class._NAME} configuration" + prompt = f"Select {config_class.NAME} configuration" account_range = click.IntRange(min=0, max=len(config_files) - 1) choice = click.prompt(prompt, type=account_range, default=0) config_file = config_files[choice] diff --git a/nucypher/cli/actions/utils.py b/nucypher/cli/actions/utils.py index 8e5c02e2f..222ef59ee 100644 --- a/nucypher/cli/actions/utils.py +++ b/nucypher/cli/actions/utils.py @@ -20,13 +20,19 @@ along with nucypher. If not, see . import click import os import shutil +from constant_sorrow.constants import NO_CONTROL_PROTOCOL from nacl.exceptions import CryptoError -from constant_sorrow.constants import NO_CONTROL_PROTOCOL from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory from nucypher.blockchain.eth.registry import BaseContractRegistry, InMemoryContractRegistry, LocalContractRegistry from nucypher.cli.actions.auth import unlock_nucypher_keyring, get_nucypher_password from nucypher.cli.actions.network import load_seednodes +from nucypher.cli.literature import ( + FEDERATED_WARNING, + PRODUCTION_REGISTRY_ADVISORY, + LOCAL_REGISTRY_ADVISORY, + CONNECTING_TO_BLOCKCHAIN +) from nucypher.config.constants import DEFAULT_CONFIG_ROOT @@ -82,7 +88,7 @@ def make_cli_character(character_config, # Federated if character_config.federated_only: - emitter.message("WARNING: Running in Federated mode", color='yellow') + emitter.message(FEDERATED_WARNING, color='yellow') return CHARACTER @@ -97,7 +103,7 @@ def establish_deployer_registry(emitter, if download_registry: registry = InMemoryContractRegistry.from_latest_publication() - emitter.message(f"Using latest published registry from {registry.source}") + emitter.message(PRODUCTION_REGISTRY_ADVISORY.format(source=registry.source)) return registry # Establish a contract registry from disk if specified @@ -120,7 +126,7 @@ def establish_deployer_registry(emitter, # All Done. registry = LocalContractRegistry(filepath=registry_filepath) - emitter.message(f"Configured to registry filepath {registry_filepath}") + emitter.message(LOCAL_REGISTRY_ADVISORY.format(registry_filepath=registry_filepath)) return registry @@ -142,7 +148,7 @@ def connect_to_blockchain(provider_uri, emitter, debug: bool = False, light: boo sync=False, emitter=emitter) blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri) - emitter.echo(message="Reading Latest Chaindata...") + emitter.echo(message=CONNECTING_TO_BLOCKCHAIN) blockchain.connect() return blockchain except Exception as e: diff --git a/nucypher/cli/literature.py b/nucypher/cli/literature.py index 096e3c9d5..2663d5dc8 100644 --- a/nucypher/cli/literature.py +++ b/nucypher/cli/literature.py @@ -1,30 +1,48 @@ +""" +This file is part of nucypher. + +nucypher is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +nucypher is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with nucypher. If not, see . + +""" + +"""Text blobs that are implemented as part of nucypher CLI emitter messages.""" + + +# +# Common +# + +IS_THIS_CORRECT = "Is this correct?" + + +# +# Blockchain +# + +CONNECTING_TO_BLOCKCHAIN = "Reading Latest Chaindata..." + +PRODUCTION_REGISTRY_ADVISORY = "Using latest published registry from {source}" + +LOCAL_REGISTRY_ADVISORY = "Configured to registry filepath {registry_filepath}" + +FEDERATED_WARNING = "WARNING: Running in Federated mode" + # # Staking # -RESTAKING_LOCK_AGREEMENT = """ -By enabling the re-staking lock for {staking_address}, you are committing to automatically -re-stake all rewards until a future period. You will not be able to disable re-staking until {release_period}. -""" - -RESTAKING_AGREEMENT = "By enabling the re-staking for {staking_address}, all staking rewards will be automatically added to your existing stake." - -WINDING_DOWN_AGREEMENT = """ -Over time, as the locked stake duration decreases -i.e. `winds down`, you will receive decreasing inflationary rewards. - -Instead, by disabling `wind down` (default) the locked stake duration -can remain constant until you specify that `wind down` should begin. By -keeping the locked stake duration constant, it ensures that you will -receive maximum inflation compensation. - -If `wind down` was previously disabled, you can enable it at any point -and the locked duration will decrease after each period. - -For more information see https://docs.nucypher.com/en/latest/architecture/sub_stakes.html#winding-down. -""" - CONFIRM_STAGED_STAKE = """ * Ursula Node Operator Notice * ------------------------------- @@ -49,6 +67,29 @@ paid out in ethers retro-actively and on-demand. Accept ursula node operator obligation?""" +WINDING_DOWN_AGREEMENT = """ +Over time, as the locked stake duration decreases +i.e. `winds down`, you will receive decreasing inflationary rewards. + +Instead, by disabling `wind down` (default) the locked stake duration +can remain constant until you specify that `wind down` should begin. By +keeping the locked stake duration constant, it ensures that you will +receive maximum inflation compensation. + +If `wind down` was previously disabled, you can enable it at any point +and the locked duration will decrease after each period. + +For more information see https://docs.nucypher.com/en/latest/architecture/sub_stakes.html#winding-down. +""" + +RESTAKING_LOCK_AGREEMENT = """ +By enabling the re-staking lock for {staking_address}, you are committing to automatically +re-stake all rewards until a future period. You will not be able to disable re-staking until {release_period}. +""" + +RESTAKING_AGREEMENT = "By enabling the re-staking for {staking_address}," \ + " all staking rewards will be automatically added to your existing stake." + CONFIRM_RESTAKING_LOCK = "Confirm enable re-staking lock for staker {staking_address} until {release_period}?" CONFIRM_ENABLE_RESTAKING = "Confirm enable automatic re-staking for staker {staking_address}?" @@ -59,10 +100,37 @@ CONFIRM_LARGE_STAKE_VALUE = "Wow, {value} - That's a lot of NU - Are you sure th CONFIRM_LARGE_STAKE_DURATION = "Woah, {lock_periods} is a long time - Are you sure this is correct?" +PREALLOCATION_STAKE_ADVISORY = "Beneficiary {client_account} will use preallocation contract {staking_address} to stake." + +SELECT_STAKING_ACCOUNT_INDEX = "Select index of staking account" + +SELECT_STAKE = "Select Stake" + +ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE = "NOTE: Showing divisible stakes only" + +NO_DIVISIBLE_STAKES = "No divisible stakes found." + +NO_STAKES_FOUND = "No stakes found." + + # # Configuration # +SELECT_NETWORK = "Select Network" + +NO_CONFIGURATIONS_ON_DISK = "No {name} configurations found. run 'nucypher {command} init' then try again." + +SUCCESSFUL_UPDATE_CONFIGURATION_VALUES = "Updated configuration values: {fields}" + +INVALID_JSON_IN_CONFIGURATION_WARNING = "Invalid JSON in Configuration File at {filepath}." + +INVALID_CONFIGURATION_FILE_WARNING = "Invalid Configuration at {filepath}." + +NO_ETH_ACCOUNTS = "No ETH accounts were found." + +GENERIC_SELECT_ACCOUNT = "Select index of account" + CHARACTER_DESTRUCTION = ''' Delete all {name} character files including: - Private and Public Keys ({keystore}) @@ -74,7 +142,6 @@ Are you sure?''' SUCCESSFUL_DESTRUCTION = "Successfully destroyed NuCypher configuration" - CONFIRM_FORGET_NODES = "Permanently delete all known node data?" SUCCESSFUL_FORGET_NODES = "Removed all stored known nodes metadata and certificates" @@ -88,18 +155,29 @@ COLLECT_ETH_PASSWORD = "Enter password to unlock account {checksum_address}" COLLECT_NUCYPHER_PASSWORD = "Enter NuCypher keyring password" +GENERIC_PASSWORD_PROMPT = "Enter password" + +DECRYPTING_CHARACTER_KEYRING = 'Decrypting {name} keyring...' + + # -# Ursula Networking +# Networking # + CONFIRM_URSULA_IPV4_ADDRESS = "Is this the public-facing IPv4 address ({rest_host}) you want to use for Ursula?" COLLECT_URSULA_IPV4_ADDRESS = "Please enter Ursula's public-facing IPv4 address here:" + # # Seednodes # +START_LOADING_SEEDNODES = "Connecting to preferred teacher nodes..." + +UNREADABLE_SEEDNODE_ADVISORY = "Failed to connect to teacher: {uri}" + FORCE_DETECT_URSULA_IP_WARNING = "WARNING: --force is set, using auto-detected IP '{rest_host}'" NO_DOMAIN_PEERS = "WARNING - No Peers Available for domains: {domains}" diff --git a/nucypher/cli/painting.py b/nucypher/cli/painting.py index 9b61b4f7b..30ab9f840 100644 --- a/nucypher/cli/painting.py +++ b/nucypher/cli/painting.py @@ -55,7 +55,7 @@ def echo_version(ctx, param, value): def paint_new_installation_help(emitter, new_configuration): character_config_class = new_configuration.__class__ - character_name = character_config_class._NAME.lower() + character_name = character_config_class.NAME.lower() emitter.message("Generated keyring {}".format(new_configuration.keyring_root), color='green') emitter.message("Saved configuration file {}".format(new_configuration.config_file_location), color='green') diff --git a/nucypher/config/base.py b/nucypher/config/base.py index 7127e5495..81a11ba0a 100644 --- a/nucypher/config/base.py +++ b/nucypher/config/base.py @@ -65,7 +65,7 @@ class BaseConfiguration(ABC): return filepath """ - _NAME = NotImplemented + NAME = NotImplemented _CONFIG_FILE_EXTENSION = 'json' INDENTATION = 2 @@ -90,8 +90,8 @@ class BaseConfiguration(ABC): filepath: str = None, *args, **kwargs): - if self._NAME is NotImplemented: - error = f'_NAME must be implemented on BaseConfiguration subclass {self.__class__.__name__}' + if self.NAME is NotImplemented: + error = f'NAME must be implemented on BaseConfiguration subclass {self.__class__.__name__}' raise TypeError(error) self.config_root = config_root or self.DEFAULT_CONFIG_ROOT @@ -137,7 +137,7 @@ class BaseConfiguration(ABC): :param modifier: String to modify default filename with. :return: The generated filepath string. """ - name = cls._NAME.lower() + name = cls.NAME.lower() if modifier: name += f'-{modifier}' filename = f'{name}.{cls._CONFIG_FILE_EXTENSION.lower()}' diff --git a/nucypher/config/characters.py b/nucypher/config/characters.py index 4047c75f4..5c9837dc4 100644 --- a/nucypher/config/characters.py +++ b/nucypher/config/characters.py @@ -37,7 +37,7 @@ class UrsulaConfiguration(CharacterConfiguration): from nucypher.characters.lawful import Ursula CHARACTER_CLASS = Ursula - _NAME = CHARACTER_CLASS.__name__.lower() + NAME = CHARACTER_CLASS.__name__.lower() DEFAULT_REST_HOST = '127.0.0.1' DEFAULT_REST_PORT = 9151 @@ -144,7 +144,7 @@ class AliceConfiguration(CharacterConfiguration): from nucypher.characters.lawful import Alice CHARACTER_CLASS = Alice - _NAME = CHARACTER_CLASS.__name__.lower() + NAME = CHARACTER_CLASS.__name__.lower() DEFAULT_CONTROLLER_PORT = 8151 @@ -187,7 +187,7 @@ class BobConfiguration(CharacterConfiguration): from nucypher.characters.lawful import Bob CHARACTER_CLASS = Bob - _NAME = CHARACTER_CLASS.__name__.lower() + NAME = CHARACTER_CLASS.__name__.lower() DEFAULT_CONTROLLER_PORT = 7151 @@ -203,7 +203,7 @@ class FelixConfiguration(CharacterConfiguration): # Character CHARACTER_CLASS = Felix - _NAME = CHARACTER_CLASS.__name__.lower() + NAME = CHARACTER_CLASS.__name__.lower() DEFAULT_DB_NAME = '{}.db'.format(_NAME) DEFAULT_DB_FILEPATH = os.path.join(DEFAULT_CONFIG_ROOT, DEFAULT_DB_NAME) @@ -248,7 +248,7 @@ class FelixConfiguration(CharacterConfiguration): class StakeHolderConfiguration(CharacterConfiguration): - _NAME = 'stakeholder' + NAME = 'stakeholder' CHARACTER_CLASS = StakeHolder def __init__(self, checksum_addresses: set = None, *args, **kwargs): diff --git a/tests/config/test_base_configuration.py b/tests/config/test_base_configuration.py index 50e9e6bd2..7302ef585 100644 --- a/tests/config/test_base_configuration.py +++ b/tests/config/test_base_configuration.py @@ -39,7 +39,7 @@ def expected_configuration_filepaths(): class RestorableTestItem(BaseConfiguration): - _NAME = 'something' + NAME = 'something' DEFAULT_CONFIG_ROOT = '/tmp' VERSION = 1 @@ -55,7 +55,7 @@ class RestorableTestItem(BaseConfiguration): def test_base_configuration_defaults(): assert BaseConfiguration.DEFAULT_CONFIG_ROOT == DEFAULT_CONFIG_ROOT - assert BaseConfiguration._NAME == NotImplemented + assert BaseConfiguration.NAME == NotImplemented assert BaseConfiguration._CONFIG_FILE_EXTENSION == expected_extension @@ -87,7 +87,7 @@ def test_configuration_implementation(): # Correct minimum viable implementation class BareMinimumConfigurableItem(BaseConfiguration): - _NAME = 'bare-minimum' + NAME = 'bare-minimum' VERSION = 2 def static_payload(self) -> dict: