def test_build_conf_with_conf_file(conf_file_conf): default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(data_dir, conf_file_name, default_conf_copy, {}) config = conf_loader.build_config() for k, v in config.items(): # Check that we have only loaded parameters that were already in the default conf. Additional params are not # loaded assert k in DEFAULT_CONF assert isinstance(v, DEFAULT_CONF[k].get("type")) # If a value is in the conf file, it will overwrite the one in the default conf if k in conf_file_conf: comp_v = conf_file_conf[k] # Check that we have kept track of what's overwritten assert k in conf_loader.overwritten_fields else: comp_v = DEFAULT_CONF[k].get("value") if DEFAULT_CONF[k].get("path"): assert v == data_dir + comp_v else: assert v == comp_v
def test_build_conf_with_command_line(): foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, cmd_data) config = conf_loader.build_config() assert foo_data_dir == config.pop("DATA_DIR") for k, v in config.items(): # Check that we have only loaded parameters that were already in the default conf. Additional params are not # loaded assert k in DEFAULT_CONF assert isinstance(v, DEFAULT_CONF[k].get("type")) # If a value is in the command line conf, it will overwrite the one in the default conf if k in COMMAND_LINE_CONF: comp_v = cmd_data[k] # Check that we have kept track of what's overwritten assert k in conf_loader.overwritten_fields else: comp_v = DEFAULT_CONF[k].get("value") if DEFAULT_CONF[k].get("path"): assert v == foo_data_dir + comp_v else: assert v == comp_v
def get_config(command_line_conf, data_dir): """ Combines the command line config with the config loaded from the file and the default config in order to construct the final config object. Args: command_line_conf (:obj:`dict`): a collection of the command line parameters. Returns: :obj:`dict`: A dictionary containing all the system's configuration parameters. Raises: UnknownConfigParam: if an unknown configuration parameter is found in the configuration file. """ config_loader = ConfigLoader(data_dir, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() # Set default RPC port if not overwritten by the user. if "BTC_RPC_PORT" not in config_loader.overwritten_fields: config["BTC_RPC_PORT"] = get_default_rpc_port( config.get("BTC_NETWORK")) return config
def test_build_conf_with_all(conf_file_conf): default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(data_dir, conf_file_name, default_conf_copy, cmd_data) config = conf_loader.build_config() assert data_dir == config.pop("DATA_DIR") for k, v in config.items(): # Check that we have only loaded parameters that were already in the default conf. Additional params are not # loaded assert k in DEFAULT_CONF assert isinstance(v, DEFAULT_CONF[k].get("type")) # The priority is: cmd, conf file, default if k in cmd_data: comp_v = cmd_data[k] elif k in conf_file_conf: comp_v = conf_file_conf[k] else: comp_v = DEFAULT_CONF[k].get("value") if DEFAULT_CONF[k].get("path"): assert v == data_dir + comp_v else: assert v == comp_v if k in cmd_data or k in conf_file_conf: # Check that we have kept track of what's overwritten assert k in conf_loader.overwritten_fields
def __init__(self, data_dir, command_line_conf): # Loads config and sets up the data folder and log file config_loader = ConfigLoader(data_dir, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() setup_data_folder(data_dir) teos_rpc_host = config.get("RPC_BIND") teos_rpc_port = config.get("RPC_PORT") self.rpc_client = RPCClient(teos_rpc_host, teos_rpc_port)
def test_create_config_dict(): # create_config_dict should create a dictionary with the config fields in ConfigLoader.config_fields as long as # the type of the field "value" matches the type in "type". The conf source does not matter here. foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, {}) config = conf_loader.create_config_dict() assert isinstance(config, dict) for k, v in config.items(): assert k in config assert isinstance(v, default_conf_copy[k].get("type"))
def test_create_config_dict_invalid_type(): # If any type does not match the expected one, we should get a ValueError foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) # Modify a field so the type does not match default_conf_copy["FOO_STR_2"]["value"] = 1234 conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, {}) with pytest.raises(ValueError): conf_loader.create_config_dict()
def test_build_conf_only_default(): foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, {}) config = conf_loader.build_config() for k, v in config.items(): assert k in DEFAULT_CONF assert isinstance(v, DEFAULT_CONF[k].get("type")) if DEFAULT_CONF[k].get("path"): assert v == foo_data_dir + DEFAULT_CONF[k].get("value") else: assert v == DEFAULT_CONF[k].get("value")
def test_init(): conf_loader = ConfigLoader(data_dir, conf_file_name, DEFAULT_CONF, COMMAND_LINE_CONF) assert conf_loader.data_dir == data_dir assert conf_loader.conf_file_path == data_dir + conf_file_name assert conf_loader.conf_fields == DEFAULT_CONF assert conf_loader.command_line_conf == COMMAND_LINE_CONF
def test_extend_paths(): # Test that only items with the path flag are extended foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, {}) conf_loader.extend_paths() for k, field in conf_loader.conf_fields.items(): if isinstance(field.get("value"), str): if field.get("path") is True: assert conf_loader.data_dir in field.get("value") else: assert conf_loader.data_dir not in field.get("value") # Check that absolute paths are not extended absolute_path = "/foo/var" conf_loader.conf_fields["ABSOLUTE_PATH"] = { "value": absolute_path, "type": str, "path": True } conf_loader.extend_paths() assert conf_loader.conf_fields["ABSOLUTE_PATH"]["value"] == absolute_path
def test_unknown_params(): # Test that the configuration dict won't be built if unknown data if found in the config file aux_conf = deepcopy(CONF_FILE_CONF) aux_conf["ADDITIONAL_FOO"] = {"value": "additional_var", "type": str} aux_conf_file_data = {k: v["value"] for k, v in aux_conf.items()} aux_conf_file_name = f"aux_{conf_file_name}" # Save config file to disk (we don't need to delete it, the fixture cleanup will) config_parser = ConfigParser() config_parser["foo_section"] = aux_conf_file_data with open(data_dir + aux_conf_file_name, "w") as fout: config_parser.write(fout) with pytest.raises(UnknownConfigParam): default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(data_dir, aux_conf_file_name, default_conf_copy, {}) conf_loader.build_config()
def init(options, configuration, plugin): """Initializes the plugin""" try: user_sk, user_id = generate_keys(DATA_DIR) plugin.log(f"Generating a new key pair for the watchtower client. Keys stored at {DATA_DIR}") except FileExistsError: plugin.log("A key file for the watchtower client already exists. Loading it") user_sk, user_id = load_keys(DATA_DIR) plugin.log(f"Plugin watchtower client initialized. User id = {user_id}") config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, {}) try: plugin.wt_client = WTClient(user_sk, user_id, config_loader.build_config()) except plyvel.IOError: error = "Cannot load towers db. Resource temporarily unavailable" plugin.log("Cannot load towers db. Resource temporarily unavailable") raise IOError(error)
def test_build_conf_only_default(): foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, {}) config = conf_loader.build_config() assert foo_data_dir == config.pop("DATA_DIR") for k, v in config.items(): assert k in DEFAULT_CONF assert isinstance(v, DEFAULT_CONF[k].get("type")) if DEFAULT_CONF[k].get("path"): assert v == foo_data_dir + DEFAULT_CONF[k].get("value") else: assert v == DEFAULT_CONF[k].get("value") # No field should have been overwritten assert not conf_loader.overwritten_fields
def run(): command_line_conf = {} commands = [ "register", "add_appointment", "get_appointment", "get_subscription_info", "help" ] try: opts, args = getopt(argv[1:], "h", ["apiconnect=", "apiport=", "help"]) for opt, arg in opts: if opt in ["--apiconnect"]: if arg: command_line_conf["API_CONNECT"] = arg if opt in ["--apiport"]: if arg: try: command_line_conf["API_PORT"] = int(arg) except ValueError: sys.exit("port must be an integer") if opt in ["-h", "--help"]: sys.exit(show_usage()) command = args.pop(0) if args else None if command in commands: config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() main(command, args, config) elif not command: sys.exit(f"No command provided.\n\n{show_usage()}") else: sys.exit(f"Unknown command.\n\n{show_usage()}") except GetoptError as e: sys.exit(f"{e}\n\n{show_usage()}") except UnknownConfigParam as e: exit(f"{e}. Check your teos_client.conf file")
def init(options, configuration, plugin): """Initializes the plugin""" clightning_conf = {} for k, v in options.items(): # c-lightning config options are lower case, hyphened prefixed by `watchtower_`. Internal options are upper # case, underscored with no prefix key = k[11:].replace("-", "_").upper() value = DEFAULT_CONF.get(key).get("value") # Avoid setting the key in clightning_conf if it matches with the defaults, since otherwise it'll rewrite the # standalone conf with defaults. if key in DEFAULT_CONF and v != value: clightning_conf[key] = v try: user_sk, user_id = generate_keys(DATA_DIR) plugin.log(f"Generating a new key pair for the watchtower client. Keys stored at {DATA_DIR}") except FileExistsError: plugin.log("A key file for the watchtower client already exists. Loading it") user_sk, user_id = load_keys(DATA_DIR) # Loads plugin config. priority is clightning conf, plugin conf, default. plugin.log(f"Plugin watchtower client initialized. User id = {user_id}") config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, clightning_conf) try: conf = config_loader.build_config() plugin.wt_client = WTClient(user_sk, user_id, conf) except plyvel.IOError: error = "Cannot load towers db. Resource temporarily unavailable" plugin.log("Cannot load towers db. Resource temporarily unavailable") raise IOError(error) except UnknownConfigParam as e: plugin.log(str(e), level="error") exit(1)
def main(command, args, command_line_conf): # Loads config and sets up the data folder and log file config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() setup_data_folder(config.get("DATA_DIR")) # Set the teos url teos_url = "{}:{}".format(config.get("API_CONNECT"), config.get("API_PORT")) # If an http or https prefix if found, leaves the server as is. Otherwise defaults to http. if not teos_url.startswith("http"): teos_url = "http://" + teos_url try: if os.path.exists(config.get("USER_PRIVATE_KEY")): logger.debug("Client id found. Loading keys") user_sk, user_id = load_keys(config.get("USER_PRIVATE_KEY")) else: logger.info("Client id not found. Generating new keys") user_sk = Cryptographer.generate_key() Cryptographer.save_key_file(user_sk.to_der(), "user_sk", config.get("DATA_DIR")) user_id = Cryptographer.get_compressed_pk(user_sk.public_key) if command == "register": if not args: raise InvalidParameter("Cannot register. No tower id was given") else: teos_id = args.pop(0) if not is_compressed_pk(teos_id): raise InvalidParameter("Cannot register. Tower id has invalid format") available_slots, subscription_expiry = register(user_id, teos_id, teos_url) logger.info("Registration succeeded. Available slots: {}".format(available_slots)) logger.info("Subscription expires at block {}".format(subscription_expiry)) teos_id_file = os.path.join(config.get("DATA_DIR"), "teos_pk") Cryptographer.save_key_file(bytes.fromhex(teos_id), teos_id_file, config.get("DATA_DIR")) if command == "add_appointment": teos_id = load_teos_id(config.get("TEOS_PUBLIC_KEY")) appointment_data = parse_add_appointment_args(args) appointment = create_appointment(appointment_data) start_block, signature = add_appointment(appointment, user_sk, teos_id, teos_url) save_appointment_receipt( appointment.to_dict(), start_block, signature, config.get("APPOINTMENTS_FOLDER_NAME") ) elif command == "get_appointment": if not args: logger.error("No arguments were given") else: arg_opt = args.pop(0) if arg_opt in ["-h", "--help"]: sys.exit(help_get_appointment()) teos_id = load_teos_id(config.get("TEOS_PUBLIC_KEY")) appointment_data = get_appointment(arg_opt, user_sk, teos_id, teos_url) if appointment_data: logger.info(json.dumps(appointment_data, indent=4)) elif command == "get_subscription_info": if args: arg_opt = args.pop(0) if arg_opt in ["-h", "--help"]: sys.exit(help_get_subscription_info()) teos_id = load_teos_id(config.get("TEOS_PUBLIC_KEY")) subscription_info = get_subscription_info(user_sk, teos_id, teos_url) if subscription_info: logger.info(json.dumps(subscription_info, indent=4)) elif command == "help": if args: command = args.pop(0) if command == "register": sys.exit(help_register()) if command == "add_appointment": sys.exit(help_add_appointment()) if command == "get_subscription_info": sys.exit(help_get_subscription_info()) elif command == "get_appointment": sys.exit(help_get_appointment()) else: logger.error("Unknown command. Use help to check the list of available commands") else: sys.exit(show_usage()) except (FileNotFoundError, IOError, ConnectionError, ValueError, BasicException,) as e: logger.error(str(e)) except Exception as e: logger.error("Unknown error occurred", error=str(e))
def main(command_line_conf): global db_manager, chain_monitor signal(SIGINT, handle_signals) signal(SIGTERM, handle_signals) signal(SIGQUIT, handle_signals) # Loads config and sets up the data folder and log file config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() setup_data_folder(DATA_DIR) setup_logging(config.get("LOG_FILE"), LOG_PREFIX) logger.info("Starting TEOS") db_manager = DBManager(config.get("DB_PATH")) bitcoind_connect_params = { k: v for k, v in config.items() if k.startswith("BTC") } bitcoind_feed_params = { k: v for k, v in config.items() if k.startswith("FEED") } if not can_connect_to_bitcoind(bitcoind_connect_params): logger.error("Can't connect to bitcoind. Shutting down") elif not in_correct_network(bitcoind_connect_params, config.get("BTC_NETWORK")): logger.error( "bitcoind is running on a different network, check conf.py and bitcoin.conf. Shutting down" ) else: try: secret_key_der = Cryptographer.load_key_file( config.get("TEOS_SECRET_KEY")) if not secret_key_der: raise IOError("TEOS private key can't be loaded") block_processor = BlockProcessor(bitcoind_connect_params) carrier = Carrier(bitcoind_connect_params) responder = Responder(db_manager, carrier, block_processor) watcher = Watcher( db_manager, block_processor, responder, secret_key_der, config.get("MAX_APPOINTMENTS"), config.get("EXPIRY_DELTA"), ) # Create the chain monitor and start monitoring the chain chain_monitor = ChainMonitor(watcher.block_queue, watcher.responder.block_queue, block_processor, bitcoind_feed_params) watcher_appointments_data = db_manager.load_watcher_appointments() responder_trackers_data = db_manager.load_responder_trackers() if len(watcher_appointments_data) == 0 and len( responder_trackers_data) == 0: logger.info("Fresh bootstrap") watcher.awake() watcher.responder.awake() else: logger.info("Bootstrapping from backed up data") # Update the Watcher backed up data if found. if len(watcher_appointments_data) != 0: watcher.appointments, watcher.locator_uuid_map = Builder.build_appointments( watcher_appointments_data) # Update the Responder with backed up data if found. if len(responder_trackers_data) != 0: watcher.responder.trackers, watcher.responder.tx_tracker_map = Builder.build_trackers( responder_trackers_data) # Awaking components so the states can be updated. watcher.awake() watcher.responder.awake() last_block_watcher = db_manager.load_last_block_hash_watcher() last_block_responder = db_manager.load_last_block_hash_responder( ) # Populate the block queues with data if they've missed some while offline. If the blocks of both match # we don't perform the search twice. # FIXME: 32-reorgs-offline dropped txs are not used at this point. last_common_ancestor_watcher, dropped_txs_watcher = block_processor.find_last_common_ancestor( last_block_watcher) missed_blocks_watcher = block_processor.get_missed_blocks( last_common_ancestor_watcher) if last_block_watcher == last_block_responder: dropped_txs_responder = dropped_txs_watcher missed_blocks_responder = missed_blocks_watcher else: last_common_ancestor_responder, dropped_txs_responder = block_processor.find_last_common_ancestor( last_block_responder) missed_blocks_responder = block_processor.get_missed_blocks( last_common_ancestor_responder) # If only one of the instances needs to be updated, it can be done separately. if len(missed_blocks_watcher ) == 0 and len(missed_blocks_responder) != 0: Builder.populate_block_queue(watcher.responder.block_queue, missed_blocks_responder) watcher.responder.block_queue.join() elif len(missed_blocks_responder ) == 0 and len(missed_blocks_watcher) != 0: Builder.populate_block_queue(watcher.block_queue, missed_blocks_watcher) watcher.block_queue.join() # Otherwise they need to be updated at the same time, block by block elif len(missed_blocks_responder) != 0 and len( missed_blocks_watcher) != 0: Builder.update_states(watcher, missed_blocks_watcher, missed_blocks_responder) # Fire the API and the ChainMonitor # FIXME: 92-block-data-during-bootstrap-db chain_monitor.monitor_chain() API(Inspector(block_processor, config.get("MIN_TO_SELF_DELAY")), watcher).start() except Exception as e: logger.error("An error occurred: {}. Shutting down".format(e)) exit(1)
def get_config(data_folder, conf_file_name, default_conf): config_loader = ConfigLoader(data_folder, conf_file_name, default_conf, {}) config = config_loader.build_config() return config
def run(): # Split the command line as follows: global options, command, command args, # where command is the first argument not starting with "-". try: command_index = next(i for i in range(len(argv)) if i > 0 and not argv[i].startswith("-")) except StopIteration: sys.exit(f"No command found\n\n{show_usage()}") command = argv[command_index] global_options = argv[1:command_index] command_args = argv[command_index + 1:] command_line_conf = {} # Process global options try: opts, args = getopt(global_options, "h", ["rpcbind=", "rpcport=", "datadir=", "help"]) data_dir = DATA_DIR for opt, arg in opts: if opt in ["--rpcbind"]: if arg: command_line_conf["RPC_BIND"] = arg if opt in ["--rpcport"]: if arg: try: command_line_conf["RPC_PORT"] = int(arg) except ValueError: sys.exit("port must be an integer") if opt in ["--datadir"]: data_dir = os.path.expanduser(arg) if opt in ["-h", "--help"]: sys.exit(show_usage()) config_loader = ConfigLoader(data_dir, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() if command in CLI.COMMANDS: cli = CLI(data_dir, config) result = cli.run(command, command_args) if result: print(result) elif not command: sys.exit( "No command provided. Use help to check the list of available commands" ) else: sys.exit( "Unknown command. Use help to check the list of available commands" ) except GetoptError as e: sys.exit(f"{e}\n\n{show_usage()}") except UnknownConfigParam as e: exit(f"{e}. Check your teos.conf file")
def main(command, args, command_line_conf): # Loads config and sets up the data folder and log file config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() setup_data_folder(DATA_DIR) setup_logging(config.get("LOG_FILE"), LOG_PREFIX) # Set the teos url teos_url = "{}:{}".format(config.get("API_CONNECT"), config.get("API_PORT")) # If an http or https prefix if found, leaves the server as is. Otherwise defaults to http. if not teos_url.startswith("http"): teos_url = "http://" + teos_url try: teos_id, user_sk, user_id = load_keys(config.get("TEOS_PUBLIC_KEY"), config.get("CLI_PRIVATE_KEY")) if command == "register": register_data = register(user_id, teos_url) logger.info("Registration succeeded. Available slots: {}".format( register_data.get("available_slots"))) if command == "add_appointment": appointment_data = parse_add_appointment_args(args) appointment, signature = add_appointment(appointment_data, user_sk, teos_id, teos_url) save_appointment_receipt(appointment.to_dict(), signature, config.get("APPOINTMENTS_FOLDER_NAME")) elif command == "get_appointment": if not args: logger.error("No arguments were given") else: arg_opt = args.pop(0) if arg_opt in ["-h", "--help"]: sys.exit(help_get_appointment()) appointment_data = get_appointment(arg_opt, user_sk, teos_id, teos_url) if appointment_data: print(appointment_data) elif command == "get_all_appointments": appointment_data = get_all_appointments(teos_url) if appointment_data: print(appointment_data) elif command == "help": if args: command = args.pop(0) if command == "register": sys.exit(help_register()) if command == "add_appointment": sys.exit(help_add_appointment()) elif command == "get_appointment": sys.exit(help_get_appointment()) elif command == "get_all_appointments": sys.exit(help_get_all_appointments()) else: logger.error( "Unknown command. Use help to check the list of available commands" ) else: sys.exit(show_usage()) except (FileNotFoundError, IOError, ConnectionError, ValueError) as e: logger.error(str(e)) except (InvalidKey, InvalidParameter, TowerResponseError, SignatureError) as e: logger.error(e.msg, **e.kwargs) except Exception as e: logger.error("Unknown error occurred", error=str(e))
def get_config(): config_loader = ConfigLoader(".", "teos_cli.conf", DEFAULT_CONF, {}) config = config_loader.build_config() return config
def main(args, command_line_conf): # Loads config and sets up the data folder and log file config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, command_line_conf) config = config_loader.build_config() setup_data_folder(DATA_DIR) setup_logging(config.get("LOG_FILE"), LOG_PREFIX) # Set the teos url teos_url = "{}:{}".format(config.get("TEOS_SERVER"), config.get("TEOS_PORT")) # If an http or https prefix if found, leaves the server as is. Otherwise defaults to http. if not teos_url.startswith("http"): teos_url = "http://" + teos_url try: if args: command = args.pop(0) if command in commands: if command == "add_appointment": add_appointment(args, teos_url, config) elif command == "get_appointment": if not args: logger.error("No arguments were given") else: arg_opt = args.pop(0) if arg_opt in ["-h", "--help"]: sys.exit(help_get_appointment()) get_appointment_endpoint = "{}/get_appointment".format( teos_url) appointment_data = get_appointment( arg_opt, get_appointment_endpoint) if appointment_data: print(appointment_data) elif command == "help": if args: command = args.pop(0) if command == "add_appointment": sys.exit(help_add_appointment()) elif command == "get_appointment": sys.exit(help_get_appointment()) else: logger.error( "Unknown command. Use help to check the list of available commands" ) else: sys.exit(show_usage()) else: logger.error( "Unknown command. Use help to check the list of available commands" ) else: logger.error( "No command provided. Use help to check the list of available commands" ) except json.JSONDecodeError: logger.error("Non-JSON encoded appointment passed as parameter")
def test_build_invalid_data(conf_file_conf): # Lets first try with only default foo_data_dir = "foo/" default_conf_copy = deepcopy(DEFAULT_CONF) default_conf_copy["FOO_INT"]["value"] = "foo" conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, {}) with pytest.raises(ValueError): conf_loader.build_config() # Set back the default value default_conf_copy["FOO_INT"]["value"] = DEFAULT_CONF["FOO_INT"]["value"] # Only conf file now conf_file_conf["FOO_INT2"] = "foo" # Save the wrong data config_parser = ConfigParser() config_parser["foo_section"] = conf_file_data with open(data_dir + conf_file_name, "w") as fout: config_parser.write(fout) conf_loader = ConfigLoader(data_dir, conf_file_name, default_conf_copy, {}) with pytest.raises(ValueError): conf_loader.build_config() # Only command line now cmd_data["FOO_INT"] = "foo" conf_loader = ConfigLoader(foo_data_dir, conf_file_name, default_conf_copy, cmd_data) with pytest.raises(ValueError): conf_loader.build_config() # All together # Set back a wrong default default_conf_copy["FOO_STR"]["value"] = 1234 conf_loader = ConfigLoader(data_dir, conf_file_name, default_conf_copy, cmd_data) with pytest.raises(ValueError): conf_loader.build_config()