Skip to content

Commit

Permalink
Merge pull request #1833 from fetchai/develop
Browse files Browse the repository at this point in the history
Release v0.6.3
  • Loading branch information
DavidMinarsch authored Oct 16, 2020
2 parents fadf792 + 7c609d4 commit 63e46ce
Show file tree
Hide file tree
Showing 492 changed files with 14,499 additions and 3,834 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ jobs:
pip install tox
sudo apt-get install -y protobuf-compiler
- name: Run all tests
run: tox -e py3.7-cov -- --ignore=tests/test_docs --ignore=tests/test_examples --ignore=tests/test_packages/test_contracts --ignore=tests/test_packages/test_skills -m 'not unstable'
run: tox -e py3.7-cov -- --ignore=tests/test_docs --ignore=tests/test_examples --ignore=tests/test_packages/test_contracts --ignore=tests/test_packages/test_skills_integration -m 'not unstable'
continue-on-error: true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
Expand Down
20 changes: 20 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Release History

## 0.6.3 (2020-10-16)

- Adds skill testing tools and documentation
- Adds human readable log output regarding configuration for p2p_libp2p connection
- Adds support to install PyPI dependencies from AEABuilder and MultiAgentManager
- Adds CLI upgrade command to upgrade entire agent project and components
- Extends CLI remove command to include option to remove dependencies
- Extends SOEF chain identifier support
- Adds CLI transfer command to transfer wealth
- Adds integration tests for skills generic buyer and seller using skill testing tool
- Adds validation of component configurations when setting componenet configuration overrides
- Multiple refactoring of internal configuration and helper objects and methods
- Fix a bug on CLI push local with latest rather than version specifier
- Adds readmes in all agent projects
- Adds agent name in logger paths of runnable objects
- Fixes tac skills to work with and without erc1155 contract
- Adds additional validations on message flow
- Multiple docs updates based on user feedback
- Multiple additional tests and test stability fixes

## 0.6.2 (2020-10-01)

- Adds MultiAgentManager to manage multiple agent projects programmatically
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ common_checks: security misc_checks lint static docs

.PHONY: test
test:
pytest -rfE --doctest-modules aea packages/fetchai/protocols packages/fetchai/connections tests/ --cov-report=html --cov-report=xml --cov-report=term-missing --cov-report=term --cov=aea --cov=packages/fetchai/protocols --cov=packages/fetchai/connections --cov-config=.coveragerc
pytest -rfE --doctest-modules aea packages/fetchai/protocols packages/fetchai/connections packages/fetchai/skills/generic_buyer tests/ --cov-report=html --cov-report=xml --cov-report=term-missing --cov-report=term --cov=aea --cov=packages/fetchai/protocols --cov=packages/fetchai/connections --cov=packages/fetchai/skills/generic_buyer --cov-config=.coveragerc
find . -name ".coverage*" -not -name ".coveragerc" -exec rm -fr "{}" \;

.PHONY: test-sub
Expand Down
2 changes: 1 addition & 1 deletion aea/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
__title__ = "aea"
__description__ = "Autonomous Economic Agent framework"
__url__ = "https://github.com/fetchai/agents-aea.git"
__version__ = "0.6.2"
__version__ = "0.6.3"
__author__ = "Fetch.AI Limited"
__license__ = "Apache-2.0"
__copyright__ = "2019 Fetch.AI Limited"
20 changes: 10 additions & 10 deletions aea/aea.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

"""This module contains the implementation of an autonomous economic agent (AEA)."""
import datetime
import logging
from asyncio import AbstractEventLoop
from logging import Logger
from multiprocessing.pool import AsyncResult
from typing import (
Any,
Expand Down Expand Up @@ -48,7 +48,7 @@
)
from aea.exceptions import AEAException
from aea.helpers.exception_policy import ExceptionPolicyEnum
from aea.helpers.logging import AgentLoggerAdapter, WithLogger
from aea.helpers.logging import AgentLoggerAdapter, get_logger
from aea.identity.base import Identity
from aea.mail.base import Envelope
from aea.protocols.base import Message
Expand All @@ -60,7 +60,7 @@
from aea.skills.error.handlers import ErrorHandler


class AEA(Agent, WithLogger):
class AEA(Agent):
"""This class implements an autonomous economic agent."""

RUN_LOOPS: Dict[str, Type[BaseAgentLoop]] = {
Expand Down Expand Up @@ -99,7 +99,7 @@ def __init__(
:param resources: the resources (protocols and skills) of the agent.
:param loop: the event loop to run the connections.
:param period: period to call agent's act
:param exeution_timeout: amount of time to limit single act/handle to execute.
:param execution_timeout: amount of time to limit single act/handle to execute.
:param max_reactions: the processing rate of envelopes per tick (i.e. single loop).
:param decision_maker_handler_class: the class implementing the decision maker handler to be used.
:param skill_exception_policy: the skill exception policy enum
Expand All @@ -117,20 +117,20 @@ def __init__(
self._skills_exception_policy = skill_exception_policy
self._connection_exception_policy = connection_exception_policy

aea_logger = AgentLoggerAdapter(
logger=get_logger(__name__, identity.name), agent_name=identity.name,
)

super().__init__(
identity=identity,
connections=[],
loop=loop,
period=period,
loop_mode=loop_mode,
runtime_mode=runtime_mode,
logger=cast(Logger, aea_logger),
)

aea_logger = AgentLoggerAdapter(
logger=logging.getLogger(__name__), agent_name=identity.name
)
WithLogger.__init__(self, logger=cast(logging.Logger, aea_logger))

self.max_reactions = max_reactions
decision_maker_handler = decision_maker_handler_class(
identity=identity, wallet=wallet
Expand Down Expand Up @@ -388,7 +388,7 @@ def teardown(self) -> None:
:return: None
"""
self.logger.debug("[{}]: Calling teardown method...".format(self.name))
self.logger.debug("Calling teardown method...")
self.resources.teardown()

def get_task_result(self, task_id: int) -> AsyncResult:
Expand Down
96 changes: 41 additions & 55 deletions aea/aea_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,18 @@
#
# ------------------------------------------------------------------------------


"""This module contains utilities for building an AEA."""

import itertools
import logging
import logging.config
import os
import pprint
from collections import defaultdict, deque
from collections import defaultdict
from copy import copy, deepcopy
from pathlib import Path
from typing import (
Any,
Collection,
Deque,
Dict,
List,
Optional,
Set,
Tuple,
Type,
Union,
cast,
)
from typing import Any, Collection, Dict, List, Optional, Set, Tuple, Type, Union, cast

import jsonschema
from packaging.specifiers import SpecifierSet
Expand Down Expand Up @@ -77,16 +66,18 @@
DecisionMakerHandler as DefaultDecisionMakerHandler,
)
from aea.exceptions import AEAException
from aea.helpers.base import load_module
from aea.helpers.base import find_topological_order, load_env_file, load_module
from aea.helpers.exception_policy import ExceptionPolicyEnum
from aea.helpers.logging import AgentLoggerAdapter
from aea.helpers.install_dependency import install_dependency
from aea.helpers.logging import AgentLoggerAdapter, WithLogger, get_logger
from aea.identity.base import Identity
from aea.registries.resources import Resources


PathLike = Union[os.PathLike, Path, str]

logger = logging.getLogger(__name__)
_default_logger = logging.getLogger(__name__)
DEFAULT_ENV_DOTFILE = ".env"


class _DependenciesManager:
Expand Down Expand Up @@ -231,8 +222,13 @@ def pypi_dependencies(self) -> Dependencies:
)
return all_pypi_dependencies

def install_dependencies(self) -> None:
"""Install extra dependencies for components."""
for name, d in self.pypi_dependencies.items():
install_dependency(name, d)


class AEABuilder:
class AEABuilder(WithLogger): # pylint: disable=too-many-public-methods
"""
This class helps to build an AEA.
Expand Down Expand Up @@ -302,6 +298,7 @@ def __init__(self, with_default_packages: bool = True):
:param with_default_packages: add the default packages.
"""
WithLogger.__init__(self, logger=_default_logger)
self._with_default_packages = with_default_packages
self._reset(is_full_reset=True)

Expand Down Expand Up @@ -422,7 +419,7 @@ def set_decision_maker_handler(
_class = getattr(module, class_name)
self._decision_maker_handler_class = _class
except Exception as e: # pragma: nocover
logger.error(
self.logger.error(
"Could not locate decision maker handler for dotted path '{}', class name '{}' and file path '{}'. Error message: {}".format(
dotted_path, class_name, file_path, e
)
Expand Down Expand Up @@ -852,6 +849,10 @@ def _process_connection_ids(

return sorted_selected_connections_ids

def install_pypi_dependencies(self) -> None:
"""Install components extra dependecies."""
self._package_dependency_manager.install_dependencies()

def build(self, connection_ids: Optional[Collection[PublicId]] = None,) -> AEA:
"""
Build the AEA.
Expand All @@ -868,11 +869,11 @@ def build(self, connection_ids: Optional[Collection[PublicId]] = None,) -> AEA:
:raises ValueError: if we cannot
"""
self._check_we_can_build()
resources = Resources()
wallet = Wallet(
copy(self.private_key_paths), copy(self.connection_private_key_paths)
)
identity = self._build_identity_from_wallet(wallet)
resources = Resources(identity.name)
self._load_and_add_components(ComponentType.PROTOCOL, resources, identity.name)
self._load_and_add_components(ComponentType.CONTRACT, resources, identity.name)
self._load_and_add_components(
Expand Down Expand Up @@ -1082,10 +1083,10 @@ def _check_pypi_dependencies(self, configuration: ComponentConfiguration):
all_pypi_dependencies, configuration.pypi_dependencies
)
for pkg_name, dep_info in all_pypi_dependencies.items():
set_specifier = SpecifierSet(dep_info.get("version", ""))
set_specifier = SpecifierSet(dep_info.version)
if not is_satisfiable(set_specifier):
raise AEAException(
f"Conflict on package {pkg_name}: specifier set '{dep_info['version']}' not satisfiable."
f"Conflict on package {pkg_name}: specifier set '{dep_info.version}' not satisfiable."
)

@staticmethod
Expand Down Expand Up @@ -1266,12 +1267,10 @@ def _find_import_order(
- detect if there are cycles
- import skills from the leaves of the dependency graph, by finding a topological ordering.
"""
# the adjacency list for the dependency graph
depends_on: Dict[ComponentId, Set[ComponentId]] = defaultdict(set)
# the adjacency list for the inverse dependency graph
supports: Dict[ComponentId, Set[ComponentId]] = defaultdict(set)
# nodes with no incoming edges
roots = copy(skill_ids)
dependency_to_supported_dependencies: Dict[
ComponentId, Set[ComponentId]
] = defaultdict(set)
for skill_id in skill_ids:
component_path = self._find_component_directory_from_component_id(
aea_project_path, skill_id
Expand All @@ -1283,31 +1282,16 @@ def _find_import_order(
),
)

if len(configuration.skills) != 0:
roots.remove(skill_id)

depends_on[skill_id].update(
[
ComponentId(ComponentType.SKILL, skill)
for skill in configuration.skills
]
)
if skill_id not in dependency_to_supported_dependencies:
dependency_to_supported_dependencies[skill_id] = set()
for dependency in configuration.skills:
supports[ComponentId(ComponentType.SKILL, dependency)].add(skill_id)

# find topological order (Kahn's algorithm)
queue: Deque[ComponentId] = deque()
order = []
queue.extend(roots)
while len(queue) > 0:
current = queue.pop()
order.append(current)
for node in supports[current]: # pragma: nocover
depends_on[node].discard(current)
if len(depends_on[node]) == 0:
queue.append(node)

if any(len(edges) > 0 for edges in depends_on.values()):
dependency_to_supported_dependencies[
ComponentId(ComponentType.SKILL, dependency)
].add(skill_id)

try:
order = find_topological_order(dependency_to_supported_dependencies)
except ValueError:
raise AEAException("Cannot load skills, there is a cyclic dependency.")

return order
Expand Down Expand Up @@ -1337,6 +1321,8 @@ def from_aea_project(
)
builder = AEABuilder(with_default_packages=False)

load_env_file(str(aea_project_path / DEFAULT_ENV_DOTFILE))

# load agent configuration file
configuration_file = cls.get_configuration_file_path(aea_project_path)
agent_configuration = cls.loader.load(configuration_file.open())
Expand Down Expand Up @@ -1401,14 +1387,14 @@ def _load_and_add_components(
component = self._component_instances[component_type][configuration]
if configuration.component_type != ComponentType.SKILL:
component.logger = cast(
logging.Logger, make_logger(configuration, agent_name)
logging.Logger, make_component_logger(configuration, agent_name)
)
else:
new_configuration = self._overwrite_custom_configuration(configuration)
if new_configuration.is_abstract_component:
load_aea_package(configuration)
continue
_logger = make_logger(new_configuration, agent_name)
_logger = make_component_logger(new_configuration, agent_name)
component = load_component_from_config(
new_configuration, logger=_logger, **kwargs
)
Expand Down Expand Up @@ -1442,7 +1428,7 @@ def _overwrite_custom_configuration(self, configuration: ComponentConfiguration)
return new_configuration


def make_logger(
def make_component_logger(
configuration: ComponentConfiguration, agent_name: str,
) -> Optional[logging.Logger]:
"""
Expand All @@ -1456,5 +1442,5 @@ def make_logger(
# skip because skill object already have their own logger from the skill context.
return None
logger_name = f"aea.packages.{configuration.author}.{configuration.component_type.to_plural()}.{configuration.name}"
_logger = AgentLoggerAdapter(logging.getLogger(logger_name), agent_name)
_logger = AgentLoggerAdapter(get_logger(logger_name, agent_name), agent_name)
return cast(logging.Logger, _logger)
Loading

0 comments on commit 63e46ce

Please sign in to comment.