def fingerprint_package(package_dir: Path, package_type: Union[str, PackageType]) -> None: """ Fingerprint components of an item. :param ctx: the context. :param item_type: the item type. :param item_public_id: the item public id. :return: None """ package_type = PackageType(package_type) item_type = str(package_type) default_config_file_name = _get_default_configuration_file_name_from_type( item_type) config_loader = ConfigLoader.from_configuration_type(item_type) config_file_path = Path(package_dir, default_config_file_name) config = config_loader.load(open_file(config_file_path)) if not package_dir.exists(): # we only permit non-vendorized packages to be fingerprinted raise ValueError("Package not found at path {}".format(package_dir)) fingerprints_dict = _compute_fingerprint( package_dir, ignore_patterns=config.fingerprint_ignore_patterns ) # type: Dict[str, str] # Load item specification yaml file and add fingerprints config.fingerprint = fingerprints_dict config_loader.dump(config, open_file(config_file_path, "w"))
def is_allowed_diff_in_agent_config( path_to_fetched_aea: str, path_to_manually_created_aea: str ) -> Tuple[bool, Dict[str, str], Dict[str, str]]: with open_file( os.path.join(path_to_fetched_aea, "aea-config.yaml"), "r" ) as file: content1 = list(yaml.safe_load_all(file))[0] # only load first page with open_file( os.path.join(path_to_manually_created_aea, "aea-config.yaml"), "r" ) as file: content2 = list(yaml.safe_load_all(file))[0] content1c = copy.deepcopy(content1) for key, value in content1c.items(): if content2[key] == value: content1.pop(key) content2.pop(key) allowed_diff_keys = [ "aea_version", "author", "description", "version", "registry_path", ] result = all([key in allowed_diff_keys for key in content1.keys()]) result = result and all( [key in allowed_diff_keys for key in content2.keys()] ) if result: return result, {}, {} return result, content1, content2
def _create_agent_config(ctx: Context, agent_name: str, set_author: str) -> AgentConfig: """ Create agent config. :param ctx: context object. :param agent_name: agent name. :param set_author: author name to set. :return: AgentConfig object. """ agent_config = AgentConfig( agent_name=agent_name, aea_version=compute_specifier_from_version(get_current_aea_version()), author=set_author, version=DEFAULT_VERSION, license_=DEFAULT_LICENSE, registry_path=os.path.join("..", DEFAULT_REGISTRY_PATH), description="", default_ledger=DEFAULT_LEDGER, default_connection=None, ) with open_file(os.path.join(agent_name, DEFAULT_AEA_CONFIG_FILE), "w") as config_file: ctx.agent_loader.dump(agent_config, config_file) return agent_config
def _generate_full_mode( ctx: Context, protocol_generator: ProtocolGenerator, protocol_spec: ProtocolSpecification, existing_id_list: Set[PublicId], language: str, ) -> None: """Generate a protocol in 'full' mode, and add it to the configuration file and agent.""" try: warning_message = protocol_generator.generate(protobuf_only=False, language=language) if warning_message is not None: click.echo(warning_message) # Add the item to the configurations logger.debug("Registering the {} into {}".format( PROTOCOL, DEFAULT_AEA_CONFIG_FILE)) existing_id_list.add( PublicId(protocol_spec.author, protocol_spec.name, protocol_spec.version)) ctx.agent_loader.dump( ctx.agent_config, open_file(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w"), ) except FileExistsError: raise click.ClickException( # pragma: no cover "A {} with this name already exists. Please choose a different name and try again." .format(PROTOCOL)) except Exception as e: raise click.ClickException( "Protocol is NOT generated. The following error happened while generating the protocol:\n" + str(e)) fingerprint_item(ctx, PROTOCOL, protocol_spec.public_id)
def _try_execute_wasm_transaction( self, tx_signed: JSONLike, signed_tx_filename: str = "tx.signed") -> Optional[str]: """ Execute a CosmWasm Transaction. QueryMsg doesn't require signing. :param tx_signed: the signed transaction. :return: the transaction digest """ with tempfile.TemporaryDirectory() as tmpdirname: with open_file(os.path.join(tmpdirname, signed_tx_filename), "w") as f: f.write(json.dumps(tx_signed)) command = [ self.cli_command, "tx", "broadcast", os.path.join(tmpdirname, signed_tx_filename), ] tx_digest_json = json.loads(self._execute_shell_command(command)) hash_ = cast(str, tx_digest_json["txhash"]) return hash_
def _is_text(file_path: str) -> bool: """Check if a file can be read as text or not.""" try: with open_file(file_path, "r") as f: f.read() return True except UnicodeDecodeError: return False
def dump_config(self) -> None: """Save agent config on the disc.""" config_data = self.json self.agent_config.validate_config_data( config_data, env_vars_friendly=self.env_vars_friendly) with open_file(self.agent_config_file_path, "w") as file_pointer: ConfigLoader.from_configuration_type(PackageType.AGENT).dump( self.agent_config, file_pointer)
def _save_state(self) -> None: """ Save MultiAgentManager state. :return: None. """ with open_file(self._save_path, "w") as f: json.dump(self.dict_state, f, indent=4, sort_keys=True)
def fetch_agent_locally( ctx: Context, public_id: PublicId, alias: Optional[str] = None, target_dir: Optional[str] = None, ) -> None: """ Fetch Agent from local packages. :param ctx: a Context object. :param public_id: public ID of agent to be fetched. :param alias: an optional alias. :param target_dir: the target directory to which the agent is fetched. :return: None """ packages_path = ( DEFAULT_REGISTRY_NAME if ctx.registry_path is None else ctx.registry_path ) source_path = try_get_item_source_path( packages_path, public_id.author, AGENTS, public_id.name ) enforce( ctx.config.get("is_local") is True or ctx.config.get("is_mixed") is True, "Please use `ctx.set_config('is_local', True)` or `ctx.set_config('is_mixed', True)` to fetch agent and all components locally.", ) try_to_load_agent_config(ctx, agent_src_path=source_path) if not _is_version_correct(ctx, public_id): raise click.ClickException( "Wrong agent version in public ID: specified {}, found {}.".format( public_id.version, ctx.agent_config.version ) ) folder_name = target_dir or (public_id.name if alias is None else alias) target_path = os.path.join(ctx.cwd, folder_name) if os.path.exists(target_path): path = Path(target_path) raise click.ClickException( f'Item "{path.name}" already exists in target folder "{path.parent}".' ) if target_dir is not None: os.makedirs(target_path) # pragma: nocover ctx.clean_paths.append(target_path) copy_tree(source_path, target_path) ctx.cwd = target_path try_to_load_agent_config(ctx) if alias is not None: ctx.agent_config.agent_name = alias ctx.agent_loader.dump( ctx.agent_config, open_file(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w"), ) _fetch_agent_deps(ctx) click.echo("Agent {} successfully fetched.".format(public_id.name))
def _run_interaction_channel() -> None: loader = ConfigLoader.from_configuration_type(PackageType.AGENT) agent_configuration = loader.load(open_file(DEFAULT_AEA_CONFIG_FILE)) agent_name = agent_configuration.name identity_stub = Identity(agent_name + "_interact", "interact") _load_packages(identity_stub) # load agent configuration file from packages.fetchai.connections.stub.connection import ( # noqa: F811 # pylint: disable=import-outside-toplevel DEFAULT_INPUT_FILE_NAME, DEFAULT_OUTPUT_FILE_NAME, StubConnection, ) from packages.fetchai.protocols.default.dialogues import ( # noqa: F811 # pylint: disable=import-outside-toplevel DefaultDialogue, DefaultDialogues, ) from packages.fetchai.protocols.default.message import ( # noqa: F811 # pylint: disable=import-outside-toplevel DefaultMessage, ) # load stub connection configuration = ConnectionConfig( input_file=DEFAULT_OUTPUT_FILE_NAME, output_file=DEFAULT_INPUT_FILE_NAME, connection_id=StubConnection.connection_id, ) stub_connection = StubConnection(configuration=configuration, data_dir=os.getcwd(), identity=identity_stub) multiplexer = Multiplexer([stub_connection]) inbox = InBox(multiplexer) outbox = OutBox(multiplexer) def role_from_first_message( # pylint: disable=unused-argument message: Message, receiver_address: Address) -> BaseDialogue.Role: """Infer the role of the agent from an incoming/outgoing first message :param message: an incoming/outgoing first message :param receiver_address: the address of the receiving agent :return: The role of the agent """ return DefaultDialogue.Role.AGENT dialogues = DefaultDialogues(identity_stub.name, role_from_first_message) try: multiplexer.connect() while True: # pragma: no cover _process_envelopes(agent_name, inbox, outbox, dialogues, DefaultMessage) except KeyboardInterrupt: click.echo("Interaction interrupted!") except BaseException as e: # pylint: disable=broad-except # pragma: no cover click.echo(e) finally: multiplexer.disconnect()
def _init_cli_config() -> None: """ Create cli config folder and file. :return: None """ conf_dir = os.path.dirname(CLI_CONFIG_PATH) if not os.path.exists(conf_dir): os.makedirs(conf_dir) with open_file(CLI_CONFIG_PATH, "w+") as f: yaml.dump({}, f, default_flow_style=False)
def update_cli_config(dict_conf: Dict) -> None: """ Update CLI config and write to yaml file. :param dict_conf: dict config to write. :return: None """ config = get_or_create_cli_config() config.update(dict_conf) with open_file(CLI_CONFIG_PATH, "w") as f: yaml.dump(config, f, default_flow_style=False)
def _try_add_key( ctx: Context, type_: str, filepath: str, connection: bool = False ) -> None: try: if connection: ctx.agent_config.connection_private_key_paths.create(type_, filepath) else: ctx.agent_config.private_key_paths.create(type_, filepath) except ValueError as e: # pragma: no cover raise click.ClickException(str(e)) with open_file(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w") as fp: ctx.agent_loader.dump(ctx.agent_config, fp)
def load_private_key_from_path(cls, file_name: str) -> SigningKey: """ Load a private key in hex format from a file. :param file_name: the path to the hex file. :return: the Entity. """ path = Path(file_name) with open_file(path, "r") as key: data = key.read() signing_key = SigningKey.from_string(bytes.fromhex(data), curve=SECP256k1) return signing_key
def load_protocol_specification( specification_path: str) -> ProtocolSpecification: """ Load a protocol specification. :param specification_path: path to the protocol specification yaml file. :return: A ProtocolSpecification object """ config_loader = ConfigLoader("protocol-specification_schema.json", ProtocolSpecification) protocol_spec = config_loader.load_protocol_specification( open_file(specification_path)) return protocol_spec
def load_contract_interface(cls, file_path: Path) -> Dict[str, str]: """ Load contract interface. :param file_path: the file path to the interface :return: the interface """ with open_file(file_path, "r") as interface_file_ethereum: contract_interface = json.load(interface_file_ethereum) for key in [_ABI, _BYTECODE]: if key not in contract_interface: # pragma: nocover raise ValueError(f"Contract {file_path} missing key {key}.") return contract_interface
def load_private_key_from_path(cls, file_name: str) -> Account: """ Load a private key in hex format from a file. :param file_name: the path to the hex file. :return: the Entity. """ path = Path(file_name) with open_file(path, "r") as key: data = key.read() account = Account.from_key( # pylint: disable=no-value-for-parameter private_key=data) return account
def find_item_locally( ctx: Context, item_type: str, item_public_id: PublicId) -> Tuple[Path, ComponentConfiguration]: """ Find an item in the local registry. :param ctx: the CLI context. :param item_type: the type of the item to load. One of: protocols, connections, skills :param item_public_id: the public id of the item to find. :return: tuple of path to the package directory (either in registry or in aea directory) and component configuration :raises SystemExit: if the search fails. """ item_type_plural = item_type + "s" item_name = item_public_id.name # check in registry registry_path = (os.path.join(ctx.cwd, ctx.agent_config.registry_path) if ctx.registry_path is None else ctx.registry_path) package_path = Path(registry_path, item_public_id.author, item_type_plural, item_name) config_file_name = _get_default_configuration_file_name_from_type( item_type) item_configuration_filepath = package_path / config_file_name if not item_configuration_filepath.exists(): raise click.ClickException("Cannot find {}: '{}'.".format( item_type, item_public_id)) # try to load the item configuration file try: item_configuration_loader = ConfigLoader.from_configuration_type( PackageType(item_type)) with open_file(item_configuration_filepath) as fp: item_configuration = item_configuration_loader.load(fp) except ValidationError as e: raise click.ClickException( "{} configuration file not valid: {}".format( item_type.capitalize(), str(e))) # check that the configuration file of the found package matches the expected author and version. version = item_configuration.version author = item_configuration.author if item_public_id.author != author or ( not item_public_id.package_version.is_latest and item_public_id.version != version): raise click.ClickException( "Cannot find {} with author and version specified.".format( item_type)) return package_path, item_configuration
def nested_set_config(dotted_path: str, value: Any, author: str = DEFAULT_AUTHOR) -> None: """ Set an AEA config with nested values. Run from agent's directory. Allowed dotted_path: 'agent.an_attribute_name' 'protocols.my_protocol.an_attribute_name' 'connections.my_connection.an_attribute_name' 'contracts.my_contract.an_attribute_name' 'skills.my_skill.an_attribute_name' 'vendor.author.[protocols|connections|skills].package_name.attribute_name :param dotted_path: dotted path to a setting. :param value: a value to assign. Must be of yaml serializable type. :param author: the author name, used to parse the dotted path. :return: None. """ settings_keys, config_file_path, config_loader, _ = handle_dotted_path( dotted_path, author) with open_file(config_file_path) as fp: config = config_loader.load(fp) _nested_set(config, settings_keys, value) if config.package_type == PackageType.AGENT: json_data = config.ordered_json component_configurations = json_data.pop("component_configurations") with open_file(config_file_path, "w") as fp: yaml_dump_all([json_data] + component_configurations, fp) else: with open_file(config_file_path, "w") as fp: yaml_dump(config.ordered_json, fp)
def _try_remove_key(ctx: Context, type_: str, connection: bool = False) -> None: private_keys = (ctx.agent_config.connection_private_key_paths if connection else ctx.agent_config.private_key_paths) existing_keys = private_keys.keys() if type_ not in existing_keys: raise click.ClickException( f"There is no {'connection ' if connection else ''}key registered with id {type_}." ) private_keys.delete(type_) ctx.agent_loader.dump( ctx.agent_config, open_file(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w"))
def load_agent_config(cls, agent_name: str) -> AgentConfig: """Load agent configuration.""" if agent_name not in cls.agents: raise AEATestingException( f"Cannot find agent '{agent_name}' in the current test case." ) loader = ConfigLoaders.from_package_type(PackageType.AGENT) config_file_name = _get_default_configuration_file_name_from_type( PackageType.AGENT ) configuration_file_path = Path(cls.t, agent_name, config_file_name) with open_file(configuration_file_path) as file_input: agent_config = loader.load(file_input) return agent_config
def retrieve_details(name: str, loader: ConfigLoader, config_filepath: str) -> Dict: """Return description of a protocol, skill, connection.""" with open_file(str(config_filepath)) as fp: config = loader.load(fp) item_name = config.agent_name if isinstance(config, AgentConfig) else config.name enforce(item_name == name, "Item names do not match!") return { "public_id": str(config.public_id), "name": item_name, "author": config.author, "description": config.description, "version": config.version, }
def load_yaml(filepath: str) -> Dict: """ Read content from yaml file. :param filepath: str path to yaml file. :return: dict YAML content """ with open_file(filepath, "r") as f: try: result = yaml.safe_load(f) return result if result is not None else {} except yaml.YAMLError as e: raise ClickException( "Loading yaml config from {} failed: {}".format(filepath, e))
def replace_private_key_in_file( cls, private_key: str, private_key_filepath: str = DEFAULT_PRIVATE_KEY_FILE ) -> None: """ Replace the private key in the provided file with the provided key. :param private_key: the private key :param private_key_filepath: the filepath to the private key file :return: None :raises: exception if file does not exist """ with cd(cls._get_cwd()): # pragma: nocover with open_file(private_key_filepath, "wt") as f: f.write(private_key)
def load_item_config(item_type: str, package_path: Path) -> PackageConfiguration: """ Load item configuration. :param item_type: type of item. :param package_path: path to package from which config should be loaded. :return: configuration object. """ configuration_file_name = _get_default_configuration_file_name_from_type(item_type) configuration_path = package_path / configuration_file_name configuration_loader = ConfigLoader.from_configuration_type(PackageType(item_type)) with open_file(configuration_path) as file_input: item_config = configuration_loader.load(file_input) return item_config
def _create_protocol_file(path_to_protocol_package: str, file_name: str, file_content: str) -> None: """ Create a file in the generated protocol package. :param path_to_protocol_package: path to the file :param file_name: the name of the file :param file_content: the content of the file :return: None """ pathname = os.path.join(path_to_protocol_package, file_name) with open_file(pathname, "w") as file: file.write(file_content)
def update_item_public_id_in_init(item_type: str, package_path: Path, item_id: PublicId) -> None: """ Update item config and item config file. :param item_type: type of item. :param package_path: path to a package folder. :param item_id: public_id :return: None """ if item_type != SKILL: return init_filepath = os.path.join(package_path, "__init__.py") with open_file(init_filepath, "r") as f: file_content = f.readlines() with open_file(init_filepath, "w") as f: for line in file_content: if PACKAGE_PUBLIC_ID_VAR_NAME in line: f.write( f'{PACKAGE_PUBLIC_ID_VAR_NAME} = PublicId.from_str("{str(item_id)}")' ) else: f.write(line)
def _scaffold_non_package_item(ctx: Context, item_type: str, type_name: str, class_name: str, aea_dir: str) -> None: """ Scaffold a non-package item (e.g. decision maker handler, or error handler). :param ctx: the CLI context. :param item_type: the item type (e.g. 'decision_maker_handler') :param type_name: the type name (e.g. "decision maker") :param class_name: the class name (e.g. "DecisionMakerHandler") :param aea_dir: the AEA directory that contains the scaffold module :return: None """ existing_item = getattr(ctx.agent_config, item_type) if existing_item != {}: raise click.ClickException( f"A {type_name} specification already exists. Aborting...") dest = Path(f"{item_type}.py") agent_name = ctx.agent_config.agent_name click.echo(f"Adding {type_name} scaffold to the agent '{agent_name}'...") # create the file name dotted_path = f".{item_type}{DOTTED_PATH_MODULE_ELEMENT_SEPARATOR}{class_name}" try: # copy the item package into the agent project. src = Path(os.path.join(AEA_DIR, aea_dir, "scaffold.py")) logger.debug(f"Copying {type_name}. src={src} dst={dest}") shutil.copyfile(src, dest) # add the item to the configurations. logger.debug( f"Registering the {type_name} into {DEFAULT_AEA_CONFIG_FILE}") setattr( ctx.agent_config, item_type, { "dotted_path": str(dotted_path), "file_path": str(os.path.join(".", dest)), }, ) ctx.agent_loader.dump( ctx.agent_config, open_file(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w"), ) except Exception as e: os.remove(dest) raise click.ClickException(str(e))
def setup(cls, **kwargs: Any) -> None: """Set up the skill test case.""" identity = Identity("test_agent_name", "test_agent_address") cls._multiplexer = AsyncMultiplexer() cls._multiplexer._out_queue = ( # pylint: disable=protected-access asyncio.Queue() ) cls._outbox = OutBox(cast(Multiplexer, cls._multiplexer)) _shared_state = cast(Dict[str, Any], kwargs.pop("shared_state", dict())) _skill_config_overrides = cast( Dict[str, Any], kwargs.pop("config_overrides", dict()) ) agent_context = AgentContext( identity=identity, connection_status=cls._multiplexer.connection_status, outbox=cls._outbox, decision_maker_message_queue=Queue(), decision_maker_handler_context=SimpleNamespace(), task_manager=TaskManager(), default_ledger_id=identity.default_address_key, currency_denominations=DEFAULT_CURRENCY_DENOMINATIONS, default_connection=None, default_routing={}, search_service_address="dummy_search_service_address", decision_maker_address="dummy_decision_maker_address", data_dir=os.getcwd(), ) # This enables pre-populating the 'shared_state' prior to loading the skill if _shared_state != dict(): for key, value in _shared_state.items(): agent_context.shared_state[key] = value skill_configuration_file_path: Path = Path(cls.path_to_skill, "skill.yaml") loader = ConfigLoaders.from_package_type(PackageType.SKILL) with open_file(skill_configuration_file_path) as fp: skill_config: SkillConfig = loader.load(fp) # This enables overriding the skill's config prior to loading if _skill_config_overrides != {}: skill_config.update(_skill_config_overrides) skill_config.directory = cls.path_to_skill cls._skill = Skill.from_config(skill_config, agent_context)
def register_item(ctx: Context, item_type: str, item_public_id: PublicId) -> None: """ Register item in agent configuration. :param ctx: click context object. :param item_type: type of item. :param item_public_id: PublicId of item. :return: None. """ logger.debug("Registering the {} into {}".format(item_type, DEFAULT_AEA_CONFIG_FILE)) supported_items = get_items(ctx.agent_config, item_type) supported_items.add(item_public_id) with open_file(os.path.join(ctx.cwd, DEFAULT_AEA_CONFIG_FILE), "w") as fp: ctx.agent_loader.dump(ctx.agent_config, fp)