def _bump_protocol_specification_id(package_path: Path,
                                    configuration: ProtocolConfig) -> None:
    """Bump spec id version."""
    spec_id: PublicId = configuration.protocol_specification_id  # type: ignore
    old_version = semver.VersionInfo.parse(spec_id.version)
    new_version = str(old_version.bump_minor())
    new_spec_id = PublicId(spec_id.author, spec_id.name, new_version)
    configuration.protocol_specification_id = new_spec_id
    with (package_path /
          DEFAULT_PROTOCOL_CONFIG_FILE).open("w") as file_output:
        ConfigLoaders.from_package_type(configuration.package_type).dump(
            configuration, file_output)
Example #2
0
    def test_update_method_raises_error_if_we_try_to_change_classname_of_skill_component(
        self, ):
        """Test that we raise error if we try to change the 'class_name' field of a skill component configuration."""
        skill_config_path = Path(
            ROOT_DIR,
            "packages",
            "fetchai",
            "skills",
            "error",
            DEFAULT_SKILL_CONFIG_FILE,
        )
        loader = ConfigLoaders.from_package_type(PackageType.SKILL)
        skill_config = loader.load(skill_config_path.open())
        new_configurations = {
            "handlers": {
                "error_handler": {
                    "class_name": "SomeClass",
                    "args": {}
                }
            },
        }

        with pytest.raises(
                ValueError,
                match=
                "These fields of skill component configuration 'error_handler' of skill 'fetchai/error:0.9.0' are not allowed to change: {'class_name'}.",
        ):
            skill_config.update(new_configurations)
Example #3
0
    def setup(self):
        """Set up the tests."""
        self.aea_config_path = Path(CUR_PATH, "data", "dummy_aea",
                                    DEFAULT_AEA_CONFIG_FILE)
        self.loader = ConfigLoaders.from_package_type(PackageType.AGENT)
        self.aea_config: AgentConfig = self.loader.load(
            self.aea_config_path.open())
        self.dummy_skill_component_id = ComponentId(ComponentType.SKILL,
                                                    DUMMY_SKILL_PUBLIC_ID)

        self.new_dummy_skill_config = {
            "behaviours": {
                "dummy": {
                    "args": dict(behaviour_arg_1=42)
                }
            },
            "handlers": {
                "dummy": {
                    "args": dict(handler_arg_1=42)
                }
            },
            "models": {
                "dummy": {
                    "args": dict(model_arg_1=42)
                }
            },
        }
Example #4
0
def _validate_config_consistency(ctx: Context):
    """
    Validate fingerprints for every agent component.

    :param ctx: the context
    :raise ValueError: if there is a missing configuration file.
                       or if the configuration file is not valid.
                       or if the fingerprints do not match
    """
    packages_public_ids_to_types = dict([
        *map(lambda x: (x, PackageType.PROTOCOL), ctx.agent_config.protocols),
        *map(
            lambda x: (x, PackageType.CONNECTION),
            ctx.agent_config.connections,
        ),
        *map(lambda x: (x, PackageType.SKILL), ctx.agent_config.skills),
        *map(lambda x: (x, PackageType.CONTRACT), ctx.agent_config.contracts),
    ])  # type: Dict[PublicId, PackageType]

    for public_id, item_type in packages_public_ids_to_types.items():

        # find the configuration file.
        try:
            # either in vendor/ or in personal packages.
            # we give precedence to custom agent components (i.e. not vendorized).
            package_directory = Path(item_type.to_plural(), public_id.name)
            is_vendor = False
            if not package_directory.exists():
                package_directory = Path("vendor", public_id.author,
                                         item_type.to_plural(), public_id.name)
                is_vendor = True
            # we fail if none of the two alternative works.
            enforce(package_directory.exists(),
                    "Package directory does not exist!")

            loader = ConfigLoaders.from_package_type(item_type)
            config_file_name = _get_default_configuration_file_name_from_type(
                item_type)
            configuration_file_path = package_directory / config_file_name
            enforce(
                configuration_file_path.exists(),
                "Configuration file path does not exist!",
            )
        except Exception:
            raise ValueError("Cannot find {}: '{}'".format(
                item_type.value, public_id))

        # load the configuration file.
        try:
            package_configuration = loader.load(
                configuration_file_path.open("r"))
        except ValidationError as e:
            raise ValueError("{} configuration file not valid: {}".format(
                item_type.value.capitalize(), str(e)))

        _check_aea_version(package_configuration)
        _compare_fingerprints(package_configuration, package_directory,
                              is_vendor, item_type)
Example #5
0
def verify_or_create_private_keys(
    aea_project_path: Path,
    exit_on_error: bool = True,
) -> AgentConfig:
    """
    Verify or create private keys.

    :param aea_project_path: path to an AEA project.
    :param exit_on_error: whether we should exit the program on error.
    :return: the agent configuration.
    """
    path_to_aea_config = aea_project_path / DEFAULT_AEA_CONFIG_FILE
    agent_loader = ConfigLoaders.from_package_type(PackageType.AGENT)
    fp = path_to_aea_config.open(mode="r", encoding="utf-8")
    aea_conf = agent_loader.load(fp)

    for identifier, _ in aea_conf.private_key_paths.read_all():
        if identifier not in crypto_registry.supported_ids:  # pragma: nocover
            raise ValueError(
                "Unsupported identifier `{}` in private key paths. Supported identifiers: {}."
                .format(identifier, sorted(crypto_registry.supported_ids)))

    for identifier in crypto_registry.supported_ids:
        config_private_key_path = aea_conf.private_key_paths.read(identifier)
        if config_private_key_path is None:
            private_key_path = PRIVATE_KEY_PATH_SCHEMA.format(identifier)
            if identifier == aea_conf.default_ledger:  # pragma: nocover
                create_private_key(
                    identifier,
                    private_key_file=str(aea_project_path / private_key_path),
                )
                aea_conf.private_key_paths.update(identifier, private_key_path)
        else:
            try:
                try_validate_private_key_path(
                    identifier,
                    str(aea_project_path / config_private_key_path),
                    exit_on_error=exit_on_error,
                )
            except FileNotFoundError:  # pragma: no cover
                raise ValueError(
                    "File {} for private key {} not found.".format(
                        repr(config_private_key_path),
                        identifier,
                    ))

    # update aea config
    fp = path_to_aea_config.open(mode="w", encoding="utf-8")
    agent_loader.dump(aea_conf, fp)
    return aea_conf
Example #6
0
 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
Example #7
0
def test_agent_configuration_loading_multipage_when_type_not_found():
    """Test agent configuration loading, multi-page case, when type not found in some component."""
    # remove type field manually
    file = Path(CUR_PATH, "data", "aea-config.example_multipage.yaml").open()
    jsons = list(yaml.safe_load_all(file))
    jsons[1].pop("type")
    modified_file = io.StringIO()
    yaml.safe_dump_all(jsons, modified_file)
    modified_file.seek(0)

    with pytest.raises(
            ValueError,
            match="There are missing fields in component id 1: {'type'}."):
        loader = ConfigLoaders.from_package_type(PackageType.AGENT)
        loader.load(modified_file)
Example #8
0
    def test_update_method(self):
        """Test the update method."""
        skill_config_path = Path(DUMMY_SKILL_PATH)
        loader = ConfigLoaders.from_package_type(PackageType.SKILL)
        skill_config = loader.load(skill_config_path.open())

        dummy_behaviour = skill_config.behaviours.read("dummy")
        expected_dummy_behaviour_args = copy(dummy_behaviour.args)
        expected_dummy_behaviour_args["behaviour_arg_1"] = 42

        dummy_handler = skill_config.handlers.read("dummy")
        expected_dummy_handler_args = copy(dummy_handler.args)
        expected_dummy_handler_args["handler_arg_1"] = 42

        dummy_model = skill_config.models.read("dummy")
        expected_dummy_model_args = copy(dummy_model.args)
        expected_dummy_model_args["model_arg_1"] = 42

        new_configurations = {
            "behaviours": {
                "dummy": {
                    "args": dict(behaviour_arg_1=42)
                }
            },
            "handlers": {
                "dummy": {
                    "args": dict(handler_arg_1=42)
                }
            },
            "models": {
                "dummy": {
                    "args": dict(model_arg_1=42)
                }
            },
        }
        directory = "test_directory"
        skill_config.directory = directory
        skill_config.update(new_configurations)

        assert skill_config.directory == directory

        assert (expected_dummy_behaviour_args == skill_config.behaviours.read(
            "dummy").args)
        assert expected_dummy_handler_args == skill_config.handlers.read(
            "dummy").args
        assert expected_dummy_model_args == skill_config.models.read(
            "dummy").args
        assert len(skill_config.package_dependencies)
Example #9
0
def test_agent_configuration_loading_multipage():
    """Test agent configuration loading, multi-page case."""
    loader = ConfigLoaders.from_package_type(PackageType.AGENT)
    agent_config = loader.load(
        Path(CUR_PATH, "data", "aea-config.example_multipage.yaml").open())

    # test main agent configuration loaded correctly
    assert agent_config.agent_name == "myagent"
    assert agent_config.author == "fetchai"

    # test component configurations loaded correctly
    assert len(agent_config.component_configurations) == 1
    keys = list(agent_config.component_configurations)
    dummy_skill_public_id = PublicId.from_str("dummy_author/dummy:0.1.0")
    expected_component_id = ComponentId("skill", dummy_skill_public_id)
    assert keys[0] == expected_component_id
Example #10
0
    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)
Example #11
0
def test_agent_configuration_loading_multipage_when_same_id():
    """Test agent configuration loading, multi-page case, when there are two components with the same id."""
    file = Path(CUR_PATH, "data", "aea-config.example_multipage.yaml").open()
    jsons = list(yaml.safe_load_all(file))
    # add twice the last component
    jsons.append(jsons[-1])
    modified_file = io.StringIO()
    yaml.safe_dump_all(jsons, modified_file)
    modified_file.seek(0)

    with pytest.raises(
            ValueError,
            match=
            r"Configuration of component \(skill, dummy_author/dummy:0.1.0\) occurs more than once.",
    ):
        loader = ConfigLoaders.from_package_type(PackageType.AGENT)
        loader.load(modified_file)
Example #12
0
def validate_item_config(item_type: str, package_path: Path) -> None:
    """
    Validate item configuration.

    :param item_type: type of item.
    :param package_path: path to a package folder.

    :return: None
    :raises AEAConfigException: if something is wrong with item configuration.
    """
    item_config = load_item_config(item_type, package_path)
    loader = ConfigLoaders.from_package_type(item_type)
    for field_name in loader.required_fields:
        if not getattr(item_config, field_name):
            raise AEAConfigException(
                "Parameter '{}' is missing from {} config.".format(
                    field_name, item_type))
Example #13
0
def test_agent_configuration_loading_multipage_validation_error():
    """Test agent configuration loading, multi-page case, when the configuration is invalid."""
    file = Path(CUR_PATH, "data", "aea-config.example_multipage.yaml").open()
    jsons = list(yaml.safe_load_all(file))
    # make invalid the last component configuration
    jsons[-1]["invalid_attribute"] = "foo"
    modified_file = io.StringIO()
    yaml.safe_dump_all(jsons, modified_file)
    modified_file.seek(0)

    with pytest.raises(
            ValueError,
            match=
            r"Configuration of component \(skill, dummy_author/dummy:0.1.0\) is not valid.",
    ):
        loader = ConfigLoaders.from_package_type(PackageType.AGENT)
        loader.load(modified_file)
def load_configuration(package_type: PackageType,
                       package_path: Path) -> PackageConfiguration:
    """
    Load a configuration, knowing the type and the path to the package root.

    :param package_type: the package type.
    :param package_path: the path to the package root.
    :return: the configuration object.
    """
    configuration_class = type_to_class_config[package_type]
    configuration_filepath = (
        package_path / configuration_class.default_configuration_filename)

    loader = ConfigLoaders.from_package_type(package_type)
    with configuration_filepath.open() as fp:
        configuration_obj = loader.load(fp)
    configuration_obj._directory = package_path  # pylint: disable=protected-access
    return cast(PackageConfiguration, configuration_obj)
Example #15
0
    def _make_config(
        self,
        project_path: str,
        agent_overrides: Optional[dict] = None,
        component_overrides: Optional[List[dict]] = None,
    ) -> List[dict]:
        """Make new config based on project's config with overrides applied."""
        agent_overrides = agent_overrides or {}
        component_overrides = component_overrides or []

        if any([key in agent_overrides for key in self.AGENT_DO_NOT_OVERRIDE_VALUES]):
            raise ValueError(
                'Do not override any of {" ".join(self.AGENT_DO_NOT_OVERRIDE_VALUES)}'
            )

        agent_configuration_file_path: Path = AEABuilder.get_configuration_file_path(
            project_path
        )
        loader = ConfigLoaders.from_package_type(PackageType.AGENT)
        with agent_configuration_file_path.open() as fp:
            agent_config: AgentConfig = loader.load(fp)

        # prepare configuration overrides
        # - agent part
        agent_update_dictionary: Dict = dict(**agent_overrides)
        # - components part
        components_configs: Dict[ComponentId, Dict] = {}
        for obj in component_overrides:
            obj = copy.copy(obj)
            author, name, version = (
                obj.pop("author"),
                obj.pop("name"),
                obj.pop("version"),
            )
            component_id = ComponentId(obj.pop("type"), PublicId(author, name, version))
            components_configs[component_id] = obj
        agent_update_dictionary["component_configurations"] = components_configs
        # do the override (and valiation)
        agent_config.update(agent_update_dictionary)

        # return the multi-paged JSON object.
        json_data = agent_config.ordered_json
        result: List[Dict] = [json_data] + json_data.pop("component_configurations")
        return result
Example #16
0
def update_item_config(item_type: str, package_path: Path, **kwargs) -> None:
    """
    Update item config and item config file.

    :param item_type: type of item.
    :param package_path: path to a package folder.
    :param kwargs: pairs of config key-value to update.

    :return: None
    """
    item_config = load_item_config(item_type, package_path)
    for key, value in kwargs.items():
        setattr(item_config, key, value)

    config_filepath = os.path.join(package_path,
                                   item_config.default_configuration_filename)
    loader = ConfigLoaders.from_package_type(item_type)
    with open(config_filepath, "w") as f:
        loader.dump(item_config, f)
Example #17
0
def test_agent_configuration_dump_multipage():
    """Test agent configuration dump with component configuration."""
    loader = ConfigLoaders.from_package_type(PackageType.AGENT)
    agent_config = loader.load(
        Path(CUR_PATH, "data", "aea-config.example_multipage.yaml").open())

    # test main agent configuration loaded correctly
    assert agent_config.agent_name == "myagent"
    assert agent_config.author == "fetchai"

    # test component configurations loaded correctly
    assert len(agent_config.component_configurations) == 1
    fp = io.StringIO()
    loader.dump(agent_config, fp)
    fp.seek(0)
    agent_config = yaml_load_all(fp)
    assert agent_config[0]["agent_name"] == "myagent"
    assert agent_config[1]["public_id"] == "dummy_author/dummy:0.1.0"
    assert agent_config[1]["type"] == "skill"
Example #18
0
def test_agent_configuration_dump_multipage_fails_bad_component_configuration(
):
    """Test agent configuration dump with INCORRECT component configuration."""
    loader = ConfigLoaders.from_package_type(PackageType.AGENT)
    agent_config = loader.load(
        Path(CUR_PATH, "data", "aea-config.example_multipage.yaml").open())

    # test main agent configuration loaded correctly
    assert agent_config.agent_name == "myagent"
    assert agent_config.author == "fetchai"

    # test component configurations loaded correctly
    assert len(agent_config.component_configurations) == 1
    list(agent_config.component_configurations.values()
         )[0]["BAD FIELD"] = "not in specs!"
    fp = io.StringIO()
    with pytest.raises(
            ValueError,
            match="Configuration of component .* is not valid..*'BAD FIELD'"):
        loader.dump(agent_config, fp)
Example #19
0
def _update_non_vendor_package(
    package_path: Path,
    replacements: Dict[ComponentType, Dict[Tuple[str, str], PublicId]],
) -> None:
    """Update a single non-vendor package."""
    """
    A path to a non-vendor package in an AEA project is of the form:

        .../aea-project-path/package-type/package-name/

    so we need to get the second-to-last part of the path to infer the type.
    """
    type_plural = package_path.parts[-2]
    loader = ConfigLoaders.from_package_type(PackageType(type_plural[:-1]))
    path_to_config = (
        package_path / loader.configuration_class.default_configuration_filename
    )
    with path_to_config.open() as file_in:
        component_config = loader.load(file_in)
    update_dependencies(component_config, replacements)
    with path_to_config.open(mode="w") as file_out:
        loader.dump(component_config, file_out)
Example #20
0
def test_agent_configuration_loading_multipage_positive_case(component_type):
    """Test agent configuration loading, multi-page case, positive case."""
    public_id = PublicId("dummy_author", "dummy", "0.1.0")
    file = Path(CUR_PATH, "data", "aea-config.example.yaml").open()
    json_data = yaml.safe_load(file)
    json_data[component_type.to_plural()].append(str(public_id))
    modified_file = io.StringIO()
    yaml.safe_dump(json_data, modified_file)
    modified_file.flush()
    modified_file.write("---\n")
    modified_file.write(f"public_id: {public_id}\n")
    modified_file.write(f"type: {component_type.value}\n")
    modified_file.seek(0)
    expected_component_id = ComponentId(
        component_type, PublicId("dummy_author", "dummy", "0.1.0"))

    loader = ConfigLoaders.from_package_type(PackageType.AGENT)
    agent_config = loader.load(modified_file)
    assert isinstance(agent_config.component_configurations, dict)
    assert len(agent_config.component_configurations)
    assert set(agent_config.component_configurations.keys()) == {
        expected_component_id
    }
Example #21
0
    def test_update_method_raises_error_if_skill_component_not_allowed(self):
        """Test that we raise error if the custom configuration contain unexpected skill components."""
        skill_config_path = Path(
            ROOT_DIR,
            "packages",
            "fetchai",
            "skills",
            "error",
            DEFAULT_SKILL_CONFIG_FILE,
        )
        loader = ConfigLoaders.from_package_type(PackageType.SKILL)
        skill_config = loader.load(skill_config_path.open())
        new_configurations = {
            "behaviours": {
                "new_behaviour": {
                    "args": {}
                }
            },
            "handlers": {
                "new_handler": {
                    "args": {}
                }
            },
            "models": {
                "new_model": {
                    "args": {}
                }
            },
        }

        with pytest.raises(
                ValueError,
                match=
                "The custom configuration for skill fetchai/error:0.9.0 includes new behaviours: {'new_behaviour'}. This is not allowed.",
        ):
            skill_config.update(new_configurations)
Example #22
0
def test_agent_configuration_loading_multipage_when_empty_file():
    """Test agent configuration loading, multi-page case, in case of empty file."""
    with pytest.raises(ValueError,
                       match="Agent configuration file was empty."):
        loader = ConfigLoaders.from_package_type(PackageType.AGENT)
        loader.load(io.StringIO())
Example #23
0
def test_check_public_id_consistency_positive():
    """Test ComponentId.check_public_id_consistency works."""
    skill_config_path = Path(DUMMY_SKILL_PATH)
    loader = ConfigLoaders.from_package_type(PackageType.SKILL)
    skill_config = loader.load(skill_config_path.open())
    skill_config.check_public_id_consistency(Path(skill_config_path).parent)