예제 #1
0
    def _load_state(self, local: bool) -> None:
        """
        Load saved state from file.

        :param local: bool is local project and agents re-creation.

        :return: None
        :raises: ValueError if failed to load state.
        """
        if not os.path.exists(self._save_path):
            return

        save_json = {}
        with open(self._save_path) as f:
            save_json = json.load(f)

        if not save_json:
            return  # pragma: nocover

        try:
            for public_id in save_json["projects"]:
                self.add_project(
                    PublicId.from_str(public_id), local=local, restore=True
                )

            for agent_settings in save_json["agents"]:
                self.add_agent_with_config(
                    public_id=PublicId.from_str(agent_settings["public_id"]),
                    agent_name=agent_settings["agent_name"],
                    config=agent_settings["config"],
                )
        except ValueError as e:  # pragma: nocover
            raise ValueError(f"Failed to load state. {e}")
예제 #2
0
    def add_project(
        self, public_id: PublicId, local: bool = True, restore: bool = False
    ) -> "MultiAgentManager":
        """
        Fetch agent project and all dependencies to working_dir.

        :param public_id: the public if of the agent project.
        :param local: whether or not to fetch from local registry.
        :param restore: bool flag for restoring already fetched agent.
        """
        if public_id.to_any() in self._versionless_projects_set:
            raise ValueError(
                f"The project ({public_id.author}/{public_id.name}) was already added!"
            )

        self._versionless_projects_set.add(public_id.to_any())

        project = Project.load(
            self.working_dir,
            public_id,
            local,
            registry_path=self.registry_path,
            is_restore=restore,
        )

        if not restore:
            project.install_pypi_dependencies()
            project.build()

        self._projects[public_id] = project
        return self
예제 #3
0
def test_local_registry_update():
    """Test local-registry-sync cli command."""
    PACKAGES = [
        PackageId(PackageType.CONNECTION, PublicId("fetchai", "local", "0.11.0")),
        PackageId(PackageType.AGENT, PublicId("fetchai", "my_first_aea", "0.10.0")),
    ]
    with TemporaryDirectory() as tmp_dir:
        for package_id in PACKAGES:
            package_dir = os.path.join(
                tmp_dir,
                package_id.public_id.author,
                str(package_id.package_type.to_plural()),
                package_id.public_id.name,
            )
            os.makedirs(package_dir)
            fetch_package(
                str(package_id.package_type),
                public_id=package_id.public_id,
                cwd=tmp_dir,
                dest=package_dir,
            )

        assert set(PACKAGES) == set([i[0] for i in enlist_packages(tmp_dir)])

        runner = CliRunner()
        with cd(tmp_dir):
            # check intention to upgrade
            with patch(
                "aea.cli.local_registry_sync.replace_package"
            ) as replace_package_mock:
                result = runner.invoke(
                    cli, ["-s", "local-registry-sync"], catch_exceptions=False
                )
                assert result.exit_code == 0, result.stdout
            assert replace_package_mock.call_count == len(PACKAGES)

            # do actual upgrade
            result = runner.invoke(
                cli, ["-s", "local-registry-sync"], catch_exceptions=False
            )
            assert result.exit_code == 0, result.stdout

            # check next update will do nothing
            with patch(
                "aea.cli.local_registry_sync.replace_package"
            ) as replace_package_mock:
                result = runner.invoke(
                    cli, ["-s", "local-registry-sync"], catch_exceptions=False
                )
                assert result.exit_code == 0, result.stdout
            assert replace_package_mock.call_count == 0

        def sort_(packages):
            return sorted(packages, key=lambda x: str(x))

        new_packages = [i[0] for i in enlist_packages(tmp_dir)]

        for new_package, old_package in zip(sort_(new_packages), sort_(PACKAGES)):
            assert new_package.public_id > old_package.public_id
예제 #4
0
def test_find_component_directory_from_component_id():
    """Test find_component_directory_from_component_id."""
    with pytest.raises(ValueError, match=r"Package .* not found."):
        find_component_directory_from_component_id(
            Path("."),
            ComponentId(
                component_type=CONNECTION, public_id=PublicId("test", "test", "1.0.1")
            ),
        )
예제 #5
0
    def _load_state(self, local: bool, remote: bool) -> None:
        """
        Load saved state from file.

        Fetch agent project and all dependencies to working_dir.

        If local = False and remote = False, then the packages
        are fetched in mixed mode (i.e. first try from local
        registry, and then from remote registry in case of failure).

        :param local: whether or not to fetch from local registry.
        :param remote: whether or not to fetch from remote registry.

        :return: None
        :raises: ValueError if failed to load state.
        """
        if not os.path.exists(self._save_path):
            return

        save_json = {}
        with open_file(self._save_path) as f:
            save_json = json.load(f)

        if not save_json:
            return  # pragma: nocover

        try:
            for public_id in save_json["projects"]:
                self.add_project(
                    PublicId.from_str(public_id),
                    local=local,
                    remote=remote,
                    restore=True,
                )

            for agent_settings in save_json["agents"]:
                self.add_agent_with_config(
                    public_id=PublicId.from_str(agent_settings["public_id"]),
                    agent_name=agent_settings["agent_name"],
                    config=agent_settings["config"],
                )
        except ValueError as e:  # pragma: nocover
            raise ValueError(f"Failed to load state. {e}")
예제 #6
0
def get_package_latest_public_id(package_id: PackageId) -> PublicId:
    """
    Get package latest package id from the remote repo.

    :param package_id: id of the package to check

    :return: package id of the latest package in remote repo
    """
    package_meta = get_package_meta(str(package_id.package_type),
                                    package_id.public_id.to_latest())
    return PublicId.from_str(package_meta["public_id"])
예제 #7
0
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)
예제 #8
0
    def remove_project(
        self, public_id: PublicId, keep_files: bool = False
    ) -> "MultiAgentManager":
        """Remove agent project."""
        if public_id not in self._projects:
            raise ValueError(f"Project {public_id} is not present!")

        if self._projects[public_id].agents:
            raise ValueError(
                f"Can not remove projects with aliases exists: {self._projects[public_id].agents}"
            )

        project = self._projects.pop(public_id)
        self._versionless_projects_set.remove(public_id.to_any())
        if not keep_files:
            project.remove()

        return self
예제 #9
0
class SampleConnection(BaseSyncConnection):
    """Sample connection for testing."""

    MAX_WORKER_THREADS = 3

    connection_id = PublicId("test", "test", "0.1.0")
    PAUSE = 0.5

    def __init__(self, *args, **kwargs):
        """Init connection."""
        super().__init__(*args, **kwargs)
        self.main_called = False
        self.send_counter = 0
        self.on_connect_called = False
        self.on_disconnect_called = False

    def main(self):
        """Run main."""
        self.main_called = True
        envelope = Mock()
        envelope.message = "main"
        self.put_envelope(envelope)

    def on_send(self, envelope: Envelope) -> None:
        """Run on send."""
        time.sleep(self.PAUSE)
        resp_envelope = Mock()
        resp_envelope.message = f"resp for {str(envelope.message)}"
        self.put_envelope(resp_envelope)
        self.send_counter += 1

    def on_connect(self):
        """Run on connect."""
        self.on_connect_called = True

    def on_disconnect(self):
        """Run on disconnect."""
        self.on_disconnect_called = True
예제 #10
0
    def split_component_id_and_config(
            component_index: int,
            component_configuration_json: Dict) -> ComponentId:
        """
        Split component id and configuration.

        :param component_index: the position of the component configuration in the agent config file..
        :param component_configuration_json: the JSON object to process.
        :return: the component id and the configuration object.
        :raises ValueError: if the component id cannot be extracted.
        """
        # author, name, version, type are mandatory fields
        missing_fields = {"public_id", "type"
                          }.difference(component_configuration_json.keys())
        if len(missing_fields) > 0:
            raise ValueError(
                f"There are missing fields in component id {component_index + 1}: {missing_fields}."
            )
        public_id_str = component_configuration_json.pop("public_id")
        component_type = ComponentType(
            component_configuration_json.pop("type"))
        component_public_id = PublicId.from_str(public_id_str)
        component_id = ComponentId(component_type, component_public_id)
        return component_id
예제 #11
0
def handle_dotted_path(
    value: str,
    author: str,
    aea_project_path: Union[str, Path] = ".",
) -> Tuple[List[str], Path, ConfigLoader, Optional[ComponentId]]:
    """Separate the path between path to resource and json path to attribute.

    Allowed values:
        '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|contracts|connections|skills].package_name.attribute_name

    We also return the component id to retrieve the configuration of a specific
    component. Notice that at this point we don't know the version,
    so we put 'latest' as version, but later we will ignore it because
    we will filter with only the component prefix (i.e. the triple type, author and name).

    :param value: dotted path.
    :param author: the author string.
    :param aea_project_path: project path

    :return: Tuple[list of settings dict keys, filepath, config loader, component id].
    """
    parts = value.split(".")
    aea_project_path = Path(aea_project_path)

    root = parts[0]
    if root not in ALLOWED_PATH_ROOTS:
        raise AEAException(
            "The root of the dotted path must be one of: {}".format(
                ALLOWED_PATH_ROOTS))

    if (len(parts) < 2 or parts[0] == AGENT and len(parts) < 2
            or parts[0] == VENDOR and len(parts) < 5
            or parts[0] != AGENT and len(parts) < 3):
        raise AEAException(
            "The path is too short. Please specify a path up to an attribute name."
        )

    # if the root is 'agent', stop.
    if root == AGENT:
        resource_type_plural = AGENTS
        path_to_resource_configuration = Path(DEFAULT_AEA_CONFIG_FILE)
        json_path = parts[1:]
        component_id = None
    elif root == VENDOR:
        # parse json path
        resource_author = parts[1]
        resource_type_plural = parts[2]
        resource_name = parts[3]

        # extract component id
        resource_type_singular = resource_type_plural[:-1]
        try:
            component_type = ComponentType(resource_type_singular)
        except ValueError as e:
            raise AEAException(
                f"'{resource_type_plural}' is not a valid component type. Please use one of {ComponentType.plurals()}."
            ) from e
        component_id = ComponentId(component_type,
                                   PublicId(resource_author, resource_name))

        # find path to the resource directory
        path_to_resource_directory = (aea_project_path / VENDOR /
                                      resource_author / resource_type_plural /
                                      resource_name)
        path_to_resource_configuration = (
            path_to_resource_directory /
            RESOURCE_TYPE_TO_CONFIG_FILE[resource_type_plural])
        json_path = parts[4:]
        if not path_to_resource_directory.exists():
            raise AEAException(  # pragma: nocover
                "Resource vendor/{}/{}/{} does not exist.".format(
                    resource_author, resource_type_plural, resource_name))
    else:
        # navigate the resources of the agent to reach the target configuration file.
        resource_type_plural = root
        resource_name = parts[1]

        # extract component id
        resource_type_singular = resource_type_plural[:-1]
        component_type = ComponentType(resource_type_singular)
        resource_author = author
        component_id = ComponentId(component_type,
                                   PublicId(resource_author, resource_name))

        # find path to the resource directory
        path_to_resource_directory = Path(
            ".") / resource_type_plural / resource_name
        path_to_resource_configuration = (
            path_to_resource_directory /
            RESOURCE_TYPE_TO_CONFIG_FILE[resource_type_plural])
        json_path = parts[2:]
        if not path_to_resource_directory.exists():
            raise AEAException("Resource {}/{} does not exist.".format(
                resource_type_plural, resource_name))

    config_loader = ConfigLoader.from_configuration_type(
        resource_type_plural[:-1])
    return json_path, path_to_resource_configuration, config_loader, component_id