diff --git a/.github/workflows/publish-mango.yml b/.github/workflows/publish-mango.yml index fbd35b9..f242161 100644 --- a/.github/workflows/publish-mango.yml +++ b/.github/workflows/publish-mango.yml @@ -37,4 +37,4 @@ jobs: - name: Publish package uses: pypa/gh-action-pypi-publish@v1 with: - password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/test-mango.yml b/.github/workflows/test-mango.yml index 69dcef9..f69f8b6 100644 --- a/.github/workflows/test-mango.yml +++ b/.github/workflows/test-mango.yml @@ -5,7 +5,7 @@ on: [push] permissions: contents: read -jobs: +jobs: build: runs-on: ubuntu-latest strategy: @@ -36,4 +36,4 @@ jobs: run: | source venv/bin/activate coverage run -m pytest - coverage report \ No newline at end of file + coverage report diff --git a/.gitignore b/.gitignore index e2df163..ef9d2ee 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,4 @@ docs/build/ docs/source/_build # Git -*.orig \ No newline at end of file +*.orig diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9e289f9..7f0ae3b 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -17,4 +17,4 @@ formats: python: version: "3.7" install: - - requirements: docs/requirements.txt \ No newline at end of file + - requirements: docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt index dc09473..fb5c28f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,4 +6,4 @@ imagesize==0.7.1 Jinja2==2.9 MarkupSafe==0.23 Sphinx==4.0.2 -sphinx-rtd-theme==0.5.2 \ No newline at end of file +sphinx-rtd-theme==0.5.2 diff --git a/docs/source/ACL messages.rst b/docs/source/ACL messages.rst index 8b13789..e69de29 100644 --- a/docs/source/ACL messages.rst +++ b/docs/source/ACL messages.rst @@ -1 +0,0 @@ - diff --git a/docs/source/agents-container.rst b/docs/source/agents-container.rst index 449ff58..c920be6 100644 --- a/docs/source/agents-container.rst +++ b/docs/source/agents-container.rst @@ -60,15 +60,15 @@ provided when :py:meth:`__init__()` of an agent is called. Custom agents that inherit from the ``Agent`` class have to call ``super().__init__(container, suggested_aid: str = None)__`` on initialization. This will register the agent at the provided container instance and will assign a unique agent id -(``self.aid``) to the agent. However, it is possible to suggest an aid by setting the variable ``suggested_aid`` to your aid wish. -The aid is granted if there is no other agent with this id, and if the aid doesn't interfere with the default aid pattern, otherwise +(``self.aid``) to the agent. However, it is possible to suggest an aid by setting the variable ``suggested_aid`` to your aid wish. +The aid is granted if there is no other agent with this id, and if the aid doesn't interfere with the default aid pattern, otherwise the generated aid will be used. To check if the aid is available beforehand, you can use ``container.is_aid_available``. It will also create the task to check for incoming messages. *************** agent process *************** -To improve multicore utilization, mango provides a way to distribute agents to processes. For this, it is necessary to create and +To improve multicore utilization, mango provides a way to distribute agents to processes. For this, it is necessary to create and register the agent in a slightly different way. .. code-block:: python3 @@ -82,7 +82,7 @@ The process_handle is awaitable and will finish exactly when the process is full Note that after the creation, the agent lives in a mirror container in another process. Therefore, it is not possible to interact with the agent directly from the main process. If you want to interact with the agent after the creation, it is possible to -dispatch a task in the agent process using `dispatch_to_agent_process`. +dispatch a task in the agent process using `dispatch_to_agent_process`. .. code-block:: python3 main_container.dispatch_to_agent_process( diff --git a/docs/source/codecs.rst b/docs/source/codecs.rst index 98c8b07..b145071 100644 --- a/docs/source/codecs.rst +++ b/docs/source/codecs.rst @@ -5,12 +5,12 @@ Codecs Most of the codec related code is taken and adapted from aiomas: https://gitlab.com/sscherfke/aiomas/ -Codecs enable the container to encode and decode known data types to send them as messages. +Codecs enable the container to encode and decode known data types to send them as messages. Mango already contains two codecs: A json serializer that can (recursively) handle any json serializable object and a protobuf codec -that will wrap an object into a generic protobuf message. Other codecs can be implemented by inheriting -from the ``Codec`` base class and implementing its ``encode`` and ``decode`` methods. -Codecs will only handle types explicitely known to them. -New known types can be added to a codec with the ``add_serializer`` method. +that will wrap an object into a generic protobuf message. Other codecs can be implemented by inheriting +from the ``Codec`` base class and implementing its ``encode`` and ``decode`` methods. +Codecs will only handle types explicitely known to them. +New known types can be added to a codec with the ``add_serializer`` method. This method expects a type together with a serialization method and a deserialization method that translate the object into a format the codec can handle (for example a json-serializable string for the json codec). @@ -137,7 +137,7 @@ All that is left to do now is to pass our codec to the container. This is done d **@json_serializable decorator** In the above example we explicitely defined methods to (de)serialize our class. For simple classes, especially data classes, -we can achieve the same result (for json codecs) via the ``@json_serializable`` decorator. This creates the ``__asdict__``, +we can achieve the same result (for json codecs) via the ``@json_serializable`` decorator. This creates the ``__asdict__``, ``__fromdict__`` and ``__serializer__`` functions in the class: .. code-block:: python3 @@ -178,10 +178,10 @@ provides the `codecs.FastJson` codec. This codec usese `msgspec` and does not pr proto codec and ACLMessage ########################## -Serialization methods for the proto codec are expected to encode the object into a protobuf message object with the ``SerializeToString`` +Serialization methods for the proto codec are expected to encode the object into a protobuf message object with the ``SerializeToString`` method. -The codec then wraps the message into a generic message wrapper, containing the serialized -protobuf message object and a type id. +The codec then wraps the message into a generic message wrapper, containing the serialized +protobuf message object and a type id. This is necessary because in general the original type of a protobuf message can not be infered from its serialized form. @@ -280,6 +280,3 @@ process by making the proto type known to the codec using the ``register_proto_t b'\x08\x01\x12\x0c\x12\nsome_bytes' content: "some_bytes" - - - \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 2e62031..bf92433 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,21 +12,20 @@ # import os import sys -import sphinx_rtd_theme # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../mango')) +sys.path.insert(0, os.path.abspath("../../mango")) # -- Project information ----------------------------------------------------- -project = 'mango' -copyright = '2023, mango team' -author = 'mango team' +project = "mango" +copyright = "2023, mango team" +author = "mango team" # The full version, including alpha/beta/rc tags -release = '0.1' +release = "0.1" # -- General configuration --------------------------------------------------- @@ -35,17 +34,17 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.graphviz', - 'sphinx.ext.imgmath', - 'sphinx_rtd_theme', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.graphviz", + "sphinx.ext.imgmath", + "sphinx_rtd_theme", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -58,9 +57,9 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] diff --git a/docs/source/customizing-container.rst b/docs/source/customizing-container.rst index 1b0e3ec..515dad4 100644 --- a/docs/source/customizing-container.rst +++ b/docs/source/customizing-container.rst @@ -6,4 +6,4 @@ A mango container can be customized regarding its way it connects to other conta *************** connection type -*************** \ No newline at end of file +*************** diff --git a/docs/source/development.rst b/docs/source/development.rst index 0516890..7570328 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -2,12 +2,12 @@ Development Guidelines ====================== -As we mainly work in a critical domain, we set great value on code quality not only to ensure correctness, but also to improve readability and maintainability. To reach this goal we have to set some standards regarding the development process and the test quality. +As we mainly work in a critical domain, we set great value on code quality not only to ensure correctness, but also to improve readability and maintainability. To reach this goal we have to set some standards regarding the development process and the test quality. Quickstart ########## -In mango it is not possible to directly push on the branches *development* or *master*. Both branches are protected and changes can only be merged using a github pull-request. So when you work on a feature, the typical process would be to create a feature-branch. When you are finished you just have to create a merge-request, pass the CI/CD pipeline, make a maintainer review the changes, and you are ready to merge! +In mango it is not possible to directly push on the branches *development* or *master*. Both branches are protected and changes can only be merged using a github pull-request. So when you work on a feature, the typical process would be to create a feature-branch. When you are finished you just have to create a merge-request, pass the CI/CD pipeline, make a maintainer review the changes, and you are ready to merge! CI/CD ##### @@ -32,7 +32,7 @@ A unit test is a test for the smallest possible testable part of the code. This Integration Tests ***************** -An integration test is everything what aims to test more than one unit. +An integration test is everything what aims to test more than one unit. Coverage ***************** @@ -42,12 +42,12 @@ We aim to reach a code coverage of > 90%. Currently, we measure the **statement* Reviews ***************** -Tests are great but do not lead to better readability and maintainability. One part that will is the review process. In mango we came to the understanding that we want to review **every** change, which should be merged into the development branch. There are no exceptions to this. The idea is not only to check the code for errors, bad smells and security flaws, its part of generating a common understanding of good coding. +Tests are great but do not lead to better readability and maintainability. One part that will is the review process. In mango we came to the understanding that we want to review **every** change, which should be merged into the development branch. There are no exceptions to this. The idea is not only to check the code for errors, bad smells and security flaws, its part of generating a common understanding of good coding. Linting ***************** -Another approach to improve the code quality is static code analysis, or better known as linting. Linting is an easy to set up possibility to make sure that a certain code standard is fulfilled. There are many useful rules, which can be checked automatically, so we have another line of defense and spare some time when reviewing. +Another approach to improve the code quality is static code analysis, or better known as linting. Linting is an easy to set up possibility to make sure that a certain code standard is fulfilled. There are many useful rules, which can be checked automatically, so we have another line of defense and spare some time when reviewing. Formatting ***************** @@ -57,4 +57,4 @@ The project is formatted using black + isort with default settings. ```bash isort mango examples tests black mango examples tests -``` \ No newline at end of file +``` diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 6baedb1..442b358 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -48,5 +48,3 @@ __ https://mosquitto.org/ The protobuf codec is an optional feature that you need to explicity install if you need it. **TODO: make protobuf optional** - - diff --git a/docs/source/migration.rst b/docs/source/migration.rst index 4e2fc6f..7d8eaf5 100644 --- a/docs/source/migration.rst +++ b/docs/source/migration.rst @@ -14,4 +14,4 @@ mango 0.4.0 to 1.0.0 * The parameters create_acl and acl_metadata from 'send_message' has been removed: use send_acl_message instead * The parameter mqtt_kwargs from 'send_message' has been removed: use kwargs instead * The DateTimeScheduledTask has been removed: use TimestampScheduledTask instead -* The context and scheduler of an agent are no longer public: use the convenience methods for sending/scheduling or _context, _scheduler from within the agent \ No newline at end of file +* The context and scheduler of an agent are no longer public: use the convenience methods for sending/scheduling or _context, _scheduler from within the agent diff --git a/docs/source/scheduling.rst b/docs/source/scheduling.rst index ff955d6..bacde2e 100644 --- a/docs/source/scheduling.rst +++ b/docs/source/scheduling.rst @@ -61,7 +61,7 @@ When using the scheduling another feature becomes available: suspendable tasks. Dispatch Tasks to other Process ******************************* -As asyncio does not provide real parallelism to utilize multiple cores and agents may have tasks, which need a lot computational power, the need to dispatch certain tasks to other processes appear. Handling inter process communication manually is quite exhausting and having multiple process pools across different roles or agents leads to inefficient resource allocations. As a result mango offers a way to dispatch tasks, based on coroutine-functions, to other processes, managed by the framework. +As asyncio does not provide real parallelism to utilize multiple cores and agents may have tasks, which need a lot computational power, the need to dispatch certain tasks to other processes appear. Handling inter process communication manually is quite exhausting and having multiple process pools across different roles or agents leads to inefficient resource allocations. As a result mango offers a way to dispatch tasks, based on coroutine-functions, to other processes, managed by the framework. Analogues to the normal API there are two different ways, first you create a ScheduledProcessTask and call ``schedule_process_task``, second you invoke the convnience methods with "process" in the name. These methods exists on any Agent, the RoleContext and the Scheduler. In mango the following process tasks are available: @@ -190,4 +190,3 @@ If you comment in the ExternalClock and change your main() as follows, the progr clock.set_time(clock.time + 5) await receiver.wait_for_reply await c.shutdown() - diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index e714292..36e1efc 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -8,10 +8,10 @@ Introduction This tutorial gives an overview of the basic functions of mango agents and containers. It consists of four parts building a scenario of two PV plants, operated by their respective agents being directed by a remote -controller. +controller. -Each part comes with a standalone executable file. Subsequent parts either extend the functionality or simplify -some concept in the previous part. +Each part comes with a standalone executable file. Subsequent parts either extend the functionality or simplify +some concept in the previous part. As a whole, this tutorial covers: - container and agent creation @@ -92,10 +92,10 @@ Now we can create our agents. Agents always live inside a container and this con pv_agent_0 = PVAgent(pv_container) pv_agent_1 = PVAgent(pv_container) -For now, our agents are purely passive entities. To make them do something, we need to send them a message. Messages are +For now, our agents are purely passive entities. To make them do something, we need to send them a message. Messages are passed by the container via the ``send_message`` function always at least expects some content and a target address. To send a message directly to an agent, we also need to provide its agent id which is set by the container when the agent -is created. +is created. .. code-block:: python @@ -120,7 +120,7 @@ This concludes the first part of our tutorial. If you run this code, you should | Hello I am a PV agent! My id is agent0. | Hello I am a PV agent! My id is agent1. | Received message with content: Hello, this is a simple message. and meta {'network_protocol': 'tcp', 'priority': 0}. - + .. raw:: html @@ -170,7 +170,7 @@ The replies to feed_in requests and later the acknowledgements that a new maximu We use the ``performative`` field of the ACL message to do this. We set the ``performative`` field to ``inform`` for feed_in replies and to ``accept_proposal`` for feed_in change acknowledgements. All messages between containers are expected to be ACL Messages (or implement the split_content_and_meta function). Since we do not want to create -the full ACL object ourselves every time, within this example we always use the convenience method +the full ACL object ourselves every time, within this example we always use the convenience method ``container.send_acl_message``, which also supports setting the acl metadata. .. code-block:: python @@ -270,7 +270,7 @@ perform its active actions. We do this by implementing a ``run`` function with t - send a feed_in request to each known pv agent - wait for all pv agents to answer - find the minimum reported feed_in -- send a maximum feed_in setpoint of this minimum to each pv agent +- send a maximum feed_in setpoint of this minimum to each pv agent - again, wait for all pv agents to reply - terminate @@ -406,7 +406,7 @@ This example covers: step by step We want to use the types of custom message objects as the new mechanism for message typing. We define these -as simple data classes. For simple classes like this, we can use the ``json_serializable`` decorator to +as simple data classes. For simple classes like this, we can use the ``json_serializable`` decorator to provide us with the serialization functionality. .. code-block:: python @@ -454,7 +454,7 @@ Next, we need to create a codec, make our message objects known to it, and pass ) Any time the content of a message matches one of these types now the corresponding serialize and deserialize -functions are called. Of course, you can also create your own serialization and deserialization functions with +functions are called. Of course, you can also create your own serialization and deserialization functions with more sophisticated behaviours and pass them to the codec. For more details refer to the ``codecs`` section of the documentation. @@ -507,7 +507,7 @@ you should receive the same output as in part 2: Corresponding file: `v4_scheduling_and_roles.py` In example 3, you restructured your code to use codecs for easier handling of typed message objects. -Now it is time to expand the functionality of our controller. In addition to setting the maximum feed_in +Now it is time to expand the functionality of our controller. In addition to setting the maximum feed_in of the pv agents, the controller should now also periodically check if the pv agents are still reachable. To achieve this, the controller should send a regular "ping" message to each pv agent that is in turn answered @@ -527,7 +527,7 @@ Thus, things like message handlers that require container knowledge are introduc This example covers: - role API basics - - scheduling and periodic tasks + - scheduling and periodic tasks .. raw:: html diff --git a/examples/distributed_clock/README.md b/examples/distributed_clock/README.md index 0ec81d9..e2509c9 100644 --- a/examples/distributed_clock/README.md +++ b/examples/distributed_clock/README.md @@ -13,4 +13,4 @@ If the `clock_agent`s routine takes longer, the manager will wait for it to fini Caution: it is needed, that all agents are connected before starting the manager -This example is tested with MQTT as well as by using TCP connection \ No newline at end of file +This example is tested with MQTT as well as by using TCP connection diff --git a/examples/distributed_clock/clock_agent.py b/examples/distributed_clock/clock_agent.py index f1c4328..f893b12 100644 --- a/examples/distributed_clock/clock_agent.py +++ b/examples/distributed_clock/clock_agent.py @@ -3,7 +3,6 @@ from typing import TypedDict import numpy as np - from mango import Role, RoleAgent, create_container from mango.messages.message import Performatives from mango.util.clock import ExternalClock diff --git a/examples/distributed_clock/clock_manager.py b/examples/distributed_clock/clock_manager.py index 2ca9175..12923fc 100644 --- a/examples/distributed_clock/clock_manager.py +++ b/examples/distributed_clock/clock_manager.py @@ -132,7 +132,7 @@ async def main(start): if isinstance(clock, ExternalClock): for i in tqdm(range(30)): next_event = await clock_agent.distribute_time() - logger.info(f"current step: {clock.time}") + logger.info("current step: %s", clock.time) await tasks_complete_or_sleeping(c) # comment next line to see that the first message is not received in correct timings # also comment sleep(0) in termination_detection to see other timing problems diff --git a/examples/distributed_clock/mqtt.conf b/examples/distributed_clock/mqtt.conf index 7d7884a..bf704e1 100644 --- a/examples/distributed_clock/mqtt.conf +++ b/examples/distributed_clock/mqtt.conf @@ -3,4 +3,4 @@ listener 1883 allow_anonymous true # https://blog.jaimyn.dev/mqtt-use-acls-multiple-user-accounts/ -max_keepalive 3600 \ No newline at end of file +max_keepalive 3600 diff --git a/examples/rrule_event.py b/examples/rrule_event.py index 6c4a6fc..0372274 100644 --- a/examples/rrule_event.py +++ b/examples/rrule_event.py @@ -2,7 +2,6 @@ from datetime import datetime from dateutil import rrule - from mango import Agent, create_container from mango.util.clock import ExternalClock diff --git a/examples/simple_agent.py b/examples/simple_agent.py index 65ab232..733c655 100644 --- a/examples/simple_agent.py +++ b/examples/simple_agent.py @@ -51,7 +51,7 @@ def handle_message(self, content, meta): :param content: the content of the mssage :param meta: meta information """ - logger.info(f"Received message: {content} with meta {meta}") + logger.info("Received message: %s with meta %s", content, meta) # so far we only expect and react to greetings t = asyncio.create_task(self.react_to_greeting(content, meta)) @@ -108,7 +108,7 @@ async def react_to_greeting(self, msg_in, meta_in): sub_msg.text = message_out_content message.content_class = type(sub_msg).__name__ message.content = sub_msg.SerializeToString() - logger.debug(f"Going to send {message}") + logger.debug("Going to send %s", message) await self.send_message(message, sender_addr) # shutdown if no more open conversations diff --git a/examples/tcp_container_example.py b/examples/tcp_container_example.py index a39004f..3022697 100644 --- a/examples/tcp_container_example.py +++ b/examples/tcp_container_example.py @@ -1,6 +1,5 @@ import asyncio -import mango.messages.other_proto_msgs_pb2 as other_proto_msg from examples.simple_agent import SimpleAgent from mango import create_container diff --git a/examples/tutorial/v2_inter_container_messaging_and_basic_functionality.py b/examples/tutorial/v2_inter_container_messaging_and_basic_functionality.py index fbd0fad..906e016 100644 --- a/examples/tutorial/v2_inter_container_messaging_and_basic_functionality.py +++ b/examples/tutorial/v2_inter_container_messaging_and_basic_functionality.py @@ -4,7 +4,7 @@ from mango.messages.message import Performatives """ -In the previous example, you have learned how to create mango agents and containers and +In the previous example, you have learned how to create mango agents and containers and how to send basic messages between them. In this example, you expand upon this. We introduce a controller agent that asks the current feed_in of our PV agents and subsequently limits the output of both to their minimum. diff --git a/examples/tutorial/v3_codecs_and_typing.py b/examples/tutorial/v3_codecs_and_typing.py index 0794caf..1eedb04 100644 --- a/examples/tutorial/v3_codecs_and_typing.py +++ b/examples/tutorial/v3_codecs_and_typing.py @@ -1,12 +1,12 @@ import asyncio from dataclasses import dataclass -import mango.messages.codecs as codecs from mango import Agent, create_container +from mango.messages import codecs """ In example 2 we created some basic agent functionality and established inter-container communication. -To distinguish message types we used a corresponding field in our content dictionary. This approach is +To distinguish message types we used a corresponding field in our content dictionary. This approach is tedious and prone to error. A better way is to use dedicated message objects and using their types to distinguish messages. Arbitrary objects can be encoded for messaging between agents by mangos codecs. diff --git a/examples/tutorial/v4_scheduling_and_roles.py b/examples/tutorial/v4_scheduling_and_roles.py index bb838e0..f9f6585 100644 --- a/examples/tutorial/v4_scheduling_and_roles.py +++ b/examples/tutorial/v4_scheduling_and_roles.py @@ -1,14 +1,13 @@ import asyncio from dataclasses import dataclass -import mango.messages.codecs as codecs - # note that our imports changed because we now use the specialized RoleAgent superclass from mango import Role, RoleAgent, create_container +from mango.messages import codecs """ In example 3, you restructured your code to use codecs for easier handling of typed message objects. -Now we want to expand the functionality of our controller. In addition to setting the maximum feed_in +Now we want to expand the functionality of our controller. In addition to setting the maximum feed_in of the pv agents, the controller should now also periodically check if the pv agents are still reachable. To achieve this, the controller should seend a regular "ping" message to each pv agent that is in turn answered @@ -19,7 +18,7 @@ __init__ - where you do the initial object setup setup - which is called when the role is assigned to an agent -This distinction is relevant because only within `setup` the +This distinction is relevant because only within `setup` the RoleContext (i.e. access to the parent agent and container) exist. Thus, things like message handlers that require container knowledge are introduced there. diff --git a/mango/agent/core.py b/mango/agent/core.py index cfbdad8..205033f 100644 --- a/mango/agent/core.py +++ b/mango/agent/core.py @@ -8,7 +8,6 @@ import asyncio import logging from abc import ABC -from datetime import datetime from typing import Any, Dict, Optional, Tuple, Union from ..container.core import Container @@ -429,7 +428,9 @@ def raise_exceptions(self, fut: asyncio.Future): """ if fut.exception() is not None: logger.error( - f"Agent {self.aid}: Caught the following exception in _check_inbox: {fut.exception()}" + "Agent %s: Caught the following exception in _check_inbox: ", + self.aid, + fut.exception(), ) raise fut.exception() diff --git a/mango/agent/role.py b/mango/agent/role.py index fc9d941..e8871db 100644 --- a/mango/agent/role.py +++ b/mango/agent/role.py @@ -31,9 +31,10 @@ for initialization and scheduling of tasks * :func:`Role.on_stop` is called when the container the agent lives in, is shut down """ + import asyncio from abc import ABC -from typing import Any, Dict, List, Optional, Tuple, Union, Callable +from typing import Any, Callable, Dict, List, Optional, Tuple, Union from mango.agent.core import Agent, AgentContext, AgentDelegates from mango.util.scheduling import Scheduler @@ -246,7 +247,7 @@ def _notify_send_message_subs(self, content, receiver_addr, receiver_id, **kwarg content=content, receiver_addr=receiver_addr, receiver_id=receiver_id, - **kwargs + **kwargs, ) async def send_message( @@ -255,14 +256,14 @@ async def send_message( receiver_addr: Union[str, Tuple[str, int]], *, receiver_id: Optional[str] = None, - **kwargs + **kwargs, ): self._notify_send_message_subs(content, receiver_addr, receiver_id, **kwargs) return await self._agent_context.send_message( content=content, receiver_addr=receiver_addr, receiver_id=receiver_id, - **kwargs + **kwargs, ) async def send_acl_message( @@ -272,7 +273,7 @@ async def send_acl_message( *, receiver_id: Optional[str] = None, acl_metadata: Optional[Dict[str, Any]] = None, - **kwargs + **kwargs, ): self._notify_send_message_subs(content, receiver_addr, receiver_id, **kwargs) return await self._agent_context.send_acl_message( @@ -280,7 +281,7 @@ async def send_acl_message( receiver_addr=receiver_addr, receiver_id=receiver_id, acl_metadata=acl_metadata, - **kwargs + **kwargs, ) def subscribe_message(self, role, method, message_condition, priority=0): @@ -310,7 +311,7 @@ def emit_event(self, event: Any, event_source: Any = None): method(event, event_source) def subscribe_event(self, role: Role, event_type: type, method: Callable): - if not event_type in self._role_event_type_to_handler: + if event_type not in self._role_event_type_to_handler: self._role_event_type_to_handler[event_type] = [] self._role_event_type_to_handler[event_type] += [(role, method)] @@ -406,13 +407,13 @@ async def send_message( receiver_addr: Union[str, Tuple[str, int]], *, receiver_id: Optional[str] = None, - **kwargs + **kwargs, ): return await self._role_handler.send_message( content=content, receiver_addr=receiver_addr, receiver_id=receiver_id, - **kwargs + **kwargs, ) async def send_acl_message( @@ -422,14 +423,14 @@ async def send_acl_message( *, receiver_id: Optional[str] = None, acl_metadata: Optional[Dict[str, Any]] = None, - **kwargs + **kwargs, ): return await self._role_handler.send_acl_message( content=content, receiver_addr=receiver_addr, receiver_id=receiver_id, acl_metadata=acl_metadata, - **kwargs + **kwargs, ) def emit_event(self, event: Any, event_source: Any = None): diff --git a/mango/container/core.py b/mango/container/core.py index a1aa6f1..8064e50 100644 --- a/mango/container/core.py +++ b/mango/container/core.py @@ -1,19 +1,18 @@ import asyncio import copy import logging -import warnings import os -from dataclasses import dataclass -from multiprocessing import Process, Event +import warnings from abc import ABC, abstractmethod -from typing import Any, Dict, Optional, Tuple, Union, List +from dataclasses import dataclass +from multiprocessing import Event, Process +from typing import Any, Dict, List, Optional, Tuple, Union + +import dill # noqa F401 # do not remove! Necessary for the auto loaded pickle reg extensions from ..messages.codecs import ACLMessage, Codec from ..util.clock import Clock -from ..util.multiprocessing import aioduplex, AioDuplex, PipeToWriteQueue - - -import dill # do not remove! Necessary for the auto loaded pickle reg extensions +from ..util.multiprocessing import AioDuplex, PipeToWriteQueue, aioduplex logger = logging.getLogger(__name__) @@ -306,7 +305,8 @@ async def _move_incoming_messages_to_inbox( receiver = self._container._agents.get(meta["receiver_id"], None) if receiver is None: logger.error( - f"A message was routed to the wrong process, as the {meta} doesn't contain a known receiver-id" + "A message was routed to the wrong process, as the %s doesn't contain a known receiver-id", + meta, ) target_inbox = receiver.inbox target_inbox.put_nowait((priority, message, meta)) @@ -601,7 +601,7 @@ def _reserve_aid(self, suggested_aid=None): "The suggested aid could not be reserved, either it is not available or it is not allowed (pattern agentX);%s", suggested_aid, ) - + aid = f"{AGENT_PATTERN_NAME_PRE}{self._aid_counter}" self._aid_counter += 1 return aid diff --git a/mango/container/external_coupling.py b/mango/container/external_coupling.py index 5f51011..570e9f3 100644 --- a/mango/container/external_coupling.py +++ b/mango/container/external_coupling.py @@ -2,7 +2,7 @@ import logging import time from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import List, Optional, Tuple, Union from mango.container.core import Container, ContainerMirrorData diff --git a/mango/container/factory.py b/mango/container/factory.py index 2669fc4..824b7a4 100644 --- a/mango/container/factory.py +++ b/mango/container/factory.py @@ -9,6 +9,7 @@ from mango.container.mqtt import MQTTContainer from mango.container.tcp import TCPContainer from mango.messages.codecs import JSON + from ..messages.codecs import Codec from ..util.clock import AsyncioClock, Clock, ExternalClock @@ -116,7 +117,9 @@ async def create( ) # create paho.Client object for mqtt communication - mqtt_messenger: paho.Client = paho.Client(paho.CallbackAPIVersion.VERSION2, client_id=client_id, **init_kwargs) + mqtt_messenger: paho.Client = paho.Client( + paho.CallbackAPIVersion.VERSION2, client_id=client_id, **init_kwargs + ) # set TLS options if provided # expected as a dict: @@ -138,7 +141,7 @@ def on_con(client, userdata, flags, reason_code, properties): # check broker_addr input and connect if isinstance(broker_addr, tuple): if not 0 < len(broker_addr) < 4: - raise ValueError(f"Invalid broker address argument count") + raise ValueError("Invalid broker address argument count") if len(broker_addr) > 0 and not isinstance(broker_addr[0], str): raise ValueError("Invalid broker address - host must be str") if len(broker_addr) > 1 and not isinstance(broker_addr[1], int): @@ -185,7 +188,7 @@ def on_con(client, userdata, flags, reason_code, properties): if addr is not None: # connection has been set up, subscribe to inbox topic now logger.info( - f"[{client_id}]: Going to subscribe to {addr} " f"as inbox topic.." + "[%s]: Going to subscribe to %s as inbox topic..", client_id, addr ) # create Future that is triggered on successful subscription diff --git a/mango/container/mqtt.py b/mango/container/mqtt.py index 358af09..0d08097 100644 --- a/mango/container/mqtt.py +++ b/mango/container/mqtt.py @@ -164,9 +164,7 @@ async def _handle_message(self, *, priority: int, content, meta: Dict[str, Any]) :param meta: Dict with additional information (e.g. topic) """ topic = meta["topic"] - logger.debug( - f"Received message with content and meta;{str(content)};{str(meta)}" - ) + logger.debug("Received message with content and meta;%s;%s", content, meta) if topic == self.inbox_topic: # General inbox topic, so no receiver is specified by the topic # try to find the receiver from meta @@ -185,7 +183,7 @@ async def _handle_message(self, *, priority: int, content, meta: Dict[str, Any]) receivers.update(rec) if not receivers: logger.warning( - f"Received a message at a topic which no agent subscribed;{topic}" + "Received a message at a topic which no agent subscribed;%s", topic ) else: for receiver_id in receivers: diff --git a/mango/messages/acl_message.proto b/mango/messages/acl_message.proto index ba5aa40..a068da9 100644 --- a/mango/messages/acl_message.proto +++ b/mango/messages/acl_message.proto @@ -39,4 +39,4 @@ message ACLMessage { string reply_with = 13; string reply_by = 14; string in_reply_to = 15; -} \ No newline at end of file +} diff --git a/mango/messages/acl_message_pb2.py b/mango/messages/acl_message_pb2.py index 8bbfc7f..135562b 100644 --- a/mango/messages/acl_message_pb2.py +++ b/mango/messages/acl_message_pb2.py @@ -1,38 +1,36 @@ -# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: mango/messages/acl_message.proto # Protobuf Python Version: 5.27.2 """Generated protocol buffer code.""" + from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder + _runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 5, - 27, - 2, - '', - 'mango/messages/acl_message.proto' + _runtime_version.Domain.PUBLIC, 5, 27, 2, "", "mango/messages/acl_message.proto" ) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n mango/messages/acl_message.proto\"\xa9\x05\n\nACLMessage\x12\x11\n\tsender_id\x18\x01 \x01(\t\x12\x13\n\x0bsender_addr\x18\x02 \x01(\t\x12\x13\n\x0breceiver_id\x18\x03 \x01(\t\x12\x15\n\rreceiver_addr\x18\x04 \x01(\t\x12\x17\n\x0f\x63onversation_id\x18\x05 \x01(\t\x12.\n\x0cperformative\x18\x06 \x01(\x0e\x32\x18.ACLMessage.Performative\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x08 \x01(\x05\x12\x10\n\x08protocol\x18\t \x01(\t\x12\x10\n\x08language\x18\n \x01(\t\x12\x10\n\x08\x65ncoding\x18\x0b \x01(\t\x12\x10\n\x08ontology\x18\x0c \x01(\t\x12\x12\n\nreply_with\x18\r \x01(\t\x12\x10\n\x08reply_by\x18\x0e \x01(\t\x12\x13\n\x0bin_reply_to\x18\x0f \x01(\t\"\xd3\x02\n\x0cPerformative\x12\x13\n\x0f\x61\x63\x63\x65pt_proposal\x10\x00\x12\t\n\x05\x61gree\x10\x01\x12\n\n\x06\x63\x61ncel\x10\x02\x12\x15\n\x11\x63\x61ll_for_proposal\x10\x03\x12\x0b\n\x07\x63onfirm\x10\x04\x12\x0e\n\ndisconfirm\x10\x05\x12\x0b\n\x07\x66\x61ilure\x10\x06\x12\n\n\x06inform\x10\x07\x12\x12\n\x0enot_understood\x10\x08\x12\x0b\n\x07propose\x10\t\x12\x0c\n\x08query_if\x10\n\x12\r\n\tquery_ref\x10\x0b\x12\n\n\x06refuse\x10\x0c\x12\x13\n\x0freject_proposal\x10\r\x12\x0b\n\x07request\x10\x0e\x12\x10\n\x0crequest_when\x10\x0f\x12\x14\n\x10request_whenever\x10\x10\x12\r\n\tsubscribe\x10\x11\x12\r\n\tinform_if\x10\x12\x12\t\n\x05proxy\x10\x13\x12\r\n\tpropagate\x10\x14\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n mango/messages/acl_message.proto"\xa9\x05\n\nACLMessage\x12\x11\n\tsender_id\x18\x01 \x01(\t\x12\x13\n\x0bsender_addr\x18\x02 \x01(\t\x12\x13\n\x0breceiver_id\x18\x03 \x01(\t\x12\x15\n\rreceiver_addr\x18\x04 \x01(\t\x12\x17\n\x0f\x63onversation_id\x18\x05 \x01(\t\x12.\n\x0cperformative\x18\x06 \x01(\x0e\x32\x18.ACLMessage.Performative\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x08 \x01(\x05\x12\x10\n\x08protocol\x18\t \x01(\t\x12\x10\n\x08language\x18\n \x01(\t\x12\x10\n\x08\x65ncoding\x18\x0b \x01(\t\x12\x10\n\x08ontology\x18\x0c \x01(\t\x12\x12\n\nreply_with\x18\r \x01(\t\x12\x10\n\x08reply_by\x18\x0e \x01(\t\x12\x13\n\x0bin_reply_to\x18\x0f \x01(\t"\xd3\x02\n\x0cPerformative\x12\x13\n\x0f\x61\x63\x63\x65pt_proposal\x10\x00\x12\t\n\x05\x61gree\x10\x01\x12\n\n\x06\x63\x61ncel\x10\x02\x12\x15\n\x11\x63\x61ll_for_proposal\x10\x03\x12\x0b\n\x07\x63onfirm\x10\x04\x12\x0e\n\ndisconfirm\x10\x05\x12\x0b\n\x07\x66\x61ilure\x10\x06\x12\n\n\x06inform\x10\x07\x12\x12\n\x0enot_understood\x10\x08\x12\x0b\n\x07propose\x10\t\x12\x0c\n\x08query_if\x10\n\x12\r\n\tquery_ref\x10\x0b\x12\n\n\x06refuse\x10\x0c\x12\x13\n\x0freject_proposal\x10\r\x12\x0b\n\x07request\x10\x0e\x12\x10\n\x0crequest_when\x10\x0f\x12\x14\n\x10request_whenever\x10\x10\x12\r\n\tsubscribe\x10\x11\x12\r\n\tinform_if\x10\x12\x12\t\n\x05proxy\x10\x13\x12\r\n\tpropagate\x10\x14\x62\x06proto3' +) _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mango.messages.acl_message_pb2', _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "mango.messages.acl_message_pb2", _globals +) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_ACLMESSAGE']._serialized_start=37 - _globals['_ACLMESSAGE']._serialized_end=718 - _globals['_ACLMESSAGE_PERFORMATIVE']._serialized_start=379 - _globals['_ACLMESSAGE_PERFORMATIVE']._serialized_end=718 + DESCRIPTOR._loaded_options = None + _globals["_ACLMESSAGE"]._serialized_start = 37 + _globals["_ACLMESSAGE"]._serialized_end = 718 + _globals["_ACLMESSAGE_PERFORMATIVE"]._serialized_start = 379 + _globals["_ACLMESSAGE_PERFORMATIVE"]._serialized_end = 718 # @@protoc_insertion_point(module_scope) diff --git a/mango/messages/codecs.py b/mango/messages/codecs.py index fc71e95..66376cb 100644 --- a/mango/messages/codecs.py +++ b/mango/messages/codecs.py @@ -13,6 +13,7 @@ import inspect import json + import msgspec from mango.messages.message import ACLMessage, Performatives, enum_serializer @@ -54,7 +55,7 @@ def __fromdict__(cls, attrs): return cls(**attrs) def __repr__(self): - args = ("{}={!r}".format(a, getattr(self, a)) for a in attrs) + args = (f"{a}={getattr(self, a)!r}" for a in attrs) return "{}({})".format(self.__class__.__name__, ", ".join(args)) @classmethod @@ -123,9 +124,7 @@ def add_serializer(self, otype, serialize, deserialize): an instance of the original object. """ if otype in self._serializers: - raise ValueError( - 'There is already a serializer for type "{}"'.format(otype) - ) + raise ValueError(f'There is already a serializer for type "{otype}"') typeid = len(self._serializers) self._serializers[otype] = (typeid, serialize) self._deserializers[typeid] = deserialize @@ -141,14 +140,14 @@ def serialize_obj(self, obj): typeid, serialize = self._serializers[otype] except KeyError: raise SerializationError( - 'No serializer found for type "{}"'.format(orig_type) + f'No serializer found for type "{orig_type}"' ) from None try: return {"__type__": (typeid, serialize(obj))} except Exception as e: raise SerializationError( - 'Could not serialize object "{!r}": {}'.format(obj, e) + f'Could not serialize object "{obj!r}": {e}' ) from e def deserialize_obj(self, obj_repr): diff --git a/mango/messages/message.py b/mango/messages/message.py index 0dd2208..228891e 100644 --- a/mango/messages/message.py +++ b/mango/messages/message.py @@ -1,11 +1,12 @@ """This module implements the ACLMessage class. - The class is used to implement messages that are based on the FIPA ACL - standard. - http://www.fipa.org/specs/fipa00061/SC00061G.html#_Toc26669715 +The class is used to implement messages that are based on the FIPA ACL +standard. +http://www.fipa.org/specs/fipa00061/SC00061G.html#_Toc26669715 - It also includes the enum classes for the message Performative and Type +It also includes the enum classes for the message Performative and Type """ + import pickle from enum import Enum from typing import Any, Dict diff --git a/mango/messages/other_proto_msgs.proto b/mango/messages/other_proto_msgs.proto index 27c7ae6..4112b59 100644 --- a/mango/messages/other_proto_msgs.proto +++ b/mango/messages/other_proto_msgs.proto @@ -3,4 +3,4 @@ syntax = "proto3"; message GenericMsg { int32 type_id = 1; bytes content = 2; -} \ No newline at end of file +} diff --git a/mango/messages/other_proto_msgs_pb2.py b/mango/messages/other_proto_msgs_pb2.py index 5254d09..3551cf3 100644 --- a/mango/messages/other_proto_msgs_pb2.py +++ b/mango/messages/other_proto_msgs_pb2.py @@ -1,36 +1,39 @@ -# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: mango/messages/other_proto_msgs.proto # Protobuf Python Version: 5.27.2 """Generated protocol buffer code.""" + from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder + _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, 5, 27, 2, - '', - 'mango/messages/other_proto_msgs.proto' + "", + "mango/messages/other_proto_msgs.proto", ) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%mango/messages/other_proto_msgs.proto\".\n\nGenericMsg\x12\x0f\n\x07type_id\x18\x01 \x01(\x05\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n%mango/messages/other_proto_msgs.proto".\n\nGenericMsg\x12\x0f\n\x07type_id\x18\x01 \x01(\x05\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\x62\x06proto3' +) _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mango.messages.other_proto_msgs_pb2', _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "mango.messages.other_proto_msgs_pb2", _globals +) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_GENERICMSG']._serialized_start=41 - _globals['_GENERICMSG']._serialized_end=87 + DESCRIPTOR._loaded_options = None + _globals["_GENERICMSG"]._serialized_start = 41 + _globals["_GENERICMSG"]._serialized_end = 87 # @@protoc_insertion_point(module_scope) diff --git a/mango/modules/base_module.py b/mango/modules/base_module.py index 203bdc9..7cb1ed2 100644 --- a/mango/modules/base_module.py +++ b/mango/modules/base_module.py @@ -1,5 +1,5 @@ """This module contains the base class for basic modules that can be used - inside agents to encapsulate complex functionality """ +inside agents to encapsulate complex functionality""" import traceback diff --git a/mango/modules/mqtt_module.py b/mango/modules/mqtt_module.py index f005f23..a42cc02 100644 --- a/mango/modules/mqtt_module.py +++ b/mango/modules/mqtt_module.py @@ -1,7 +1,7 @@ """ TODO """ -import logging + from functools import partial import paho.mqtt.client as paho diff --git a/mango/modules/rabbit_module.py b/mango/modules/rabbit_module.py index 9b7ad9a..cbef1cd 100644 --- a/mango/modules/rabbit_module.py +++ b/mango/modules/rabbit_module.py @@ -80,7 +80,6 @@ def run_mq(self): # run loop while self.thread_active: if self.thread_running: - # set up saved callback for cb in self.known_registers: queues = [] diff --git a/mango/modules/zero_module.py b/mango/modules/zero_module.py index b3e850c..2aba48c 100644 --- a/mango/modules/zero_module.py +++ b/mango/modules/zero_module.py @@ -72,7 +72,7 @@ def run_mq(self): try: [topic, msg] = self.sub_socket.recv_multipart(flags=zmq.NOBLOCK) got_message = True - except: + except Exception: # didnt get a message got_message = False diff --git a/mango/util/distributed_clock.py b/mango/util/distributed_clock.py index 1c0893e..8e66b8e 100644 --- a/mango/util/distributed_clock.py +++ b/mango/util/distributed_clock.py @@ -2,7 +2,7 @@ import logging from mango import Agent -from mango.container.mqtt import MQTTContainer + from .termination_detection import tasks_complete_or_sleeping logger = logging.getLogger(__name__) diff --git a/mango/util/multiprocessing.py b/mango/util/multiprocessing.py index 5222320..5d0be16 100644 --- a/mango/util/multiprocessing.py +++ b/mango/util/multiprocessing.py @@ -1,7 +1,7 @@ """ Utility classes for handling multiprocessing in mango, especially focusing on IPC in an asyncio context. -The package contains two different variants of async pipes for IPC: duplex, and non-duplex pipes. For creating +The package contains two different variants of async pipes for IPC: duplex, and non-duplex pipes. For creating these pipes, use aiopipe() or aioduplex(). The idea of the code is based on the pypi package 'aiopipe'. These pipes provide async compatible APIs, here a general example: @@ -17,25 +17,19 @@ Further there are internal connection objects, which can be used if a synchronous access outside of the asyncio loop is necessary: 'main.write_connection, main.read_connection'. Note, that you can't use 'write_connection' if the pipe has been opened with 'open()', as this will lock the write access to the pipe. -For that case you could use 'open_readonly()', +For that case you could use 'open_readonly()', """ -import os + import asyncio import io +import os import struct -from typing import Tuple, Any, ContextManager, AsyncContextManager -from contextlib import contextmanager, asynccontextmanager - -import dill, multiprocessing - -""" -dill.Pickler.dumps, dill.Pickler.loads = dill.dumps, dill.loads -multiprocessing.reduction.ForkingPickler = dill.Pickler -multiprocessing.reduction.dump = dill.dump -""" - -from multiprocessing.reduction import ForkingPickler +from contextlib import asynccontextmanager, contextmanager from multiprocessing.connection import Connection +from multiprocessing.reduction import ForkingPickler +from typing import Any, AsyncContextManager, ContextManager, Tuple + +import dill def aiopipe() -> Tuple["AioPipeReader", "AioPipeWriter"]: diff --git a/mango/util/scheduling.py b/mango/util/scheduling.py index 6d5c3da..14d16b7 100644 --- a/mango/util/scheduling.py +++ b/mango/util/scheduling.py @@ -6,15 +6,15 @@ import concurrent.futures import datetime from abc import abstractmethod -from multiprocessing import Manager, Event -from typing import Any, List, Tuple +from asyncio import Future from dataclasses import dataclass +from multiprocessing import Manager from multiprocessing.synchronize import Event as MultiprocessingEvent +from typing import Any, List, Tuple from dateutil.rrule import rrule from mango.util.clock import AsyncioClock, Clock, ExternalClock -from asyncio import Future @dataclass @@ -120,7 +120,7 @@ def coro(self): def _close_coro(coro): try: coro.close() - except: + except Exception: pass @@ -168,7 +168,6 @@ def on_stop(self, fut: asyncio.Future = None): def close(self): """Perform closing actions""" - pass class TimestampScheduledTask(ScheduledTask): diff --git a/mango/util/termination_detection.py b/mango/util/termination_detection.py index 96b41b8..a4f6512 100644 --- a/mango/util/termination_detection.py +++ b/mango/util/termination_detection.py @@ -52,4 +52,4 @@ async def tasks_complete_or_sleeping(container: Container, except_sources=["no_w await agent.inbox.join() task_list.extend(agent._scheduler._scheduled_tasks) task_list.extend(agent._scheduler._scheduled_process_tasks) - task_list = list(filter(lambda x: x[3] not in except_sources, task_list)) \ No newline at end of file + task_list = list(filter(lambda x: x[3] not in except_sources, task_list)) diff --git a/noxfile.py b/noxfile.py index a3902ce..ed1fcdf 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,10 +1,11 @@ -import nox import os +import nox + + @nox.session(python=["3.8"]) def flake8(session): - session.install("flake8", "flake8-bugbear", "flake8-import-order", - "flake8-bandit") + session.install("flake8", "flake8-bugbear", "flake8-import-order", "flake8-bandit") session.run("flake8", "mango") @@ -41,4 +42,4 @@ def integration_tests(session): session.install("-e", ".") session.install("pytest", "pytest-cov") os.chdir("tests/integration_tests") - session.run("pytest", "--cov", ".") \ No newline at end of file + session.run("pytest", "--cov", ".") diff --git a/readme.md b/readme.md index ad0fb65..378db92 100644 --- a/readme.md +++ b/readme.md @@ -3,13 +3,13 @@ [PyPi](https://pypi.org/project/mango-agents/) | [Read the Docs](https://mango-agents.readthedocs.io) | [Github](https://github.com/OFFIS-DAI/mango) | [mail](mailto:mango@offis.de) -**Note:** _This project is still in an early development stage. +**Note:** _This project is still in an early development stage. We appreciate constructive feedback and suggestions for improvement._ mango (**m**odul**a**r pytho**n** a**g**ent framew**o**rk) is a python library for multi-agent systems (MAS). It is written on top of asyncio and is released under the MIT license. -mango allows the user to create simple agents with little effort and in the same time offers options +mango allows the user to create simple agents with little effort and in the same time offers options to structure agents with complex behaviour. The main features of mango are: @@ -75,7 +75,7 @@ The container is responsible for message exchange between agents. ``` This is how a container is created. Since the method `create_container()` is a -[coroutine](https://docs.python.org/3.9/library/asyncio-task.html) we need to await its result. +[coroutine](https://docs.python.org/3.9/library/asyncio-task.html) we need to await its result. #### Running your first agent within a container @@ -181,7 +181,7 @@ def test_second_example(): asyncio.run(run_container_and_two_agents( first_addr=('localhost', 5555), second_addr=('localhost', 5556)) ) - + ``` You should now see the following output: @@ -198,8 +198,7 @@ You have now successfully created two agents and connected them. ## License -Distributed under the MIT license. +Distributed under the MIT license. [comment]: <> (##TODO Release History * 0.0.1 First TCPContainer with json) [comment]: <> (* 0.0.2 * Added MQTTContainer and protobuf support ) - diff --git a/requirements.txt b/requirements.txt index 957c4a8..99ad005 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,4 +25,4 @@ python-dateutil==2.9.0 dill==0.3.6 msgspec==0.18.6 apipkg>=3.0.2 -six>=1.16.0 \ No newline at end of file +six>=1.16.0 diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..e3f8b59 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,25 @@ +target-version = "py38" + +src = ["crawler"] + +[lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E", "F", "G", "I", "UP", "AIR", "PIE", "PLR1714", "PLW2901", "TRY201"] +ignore = ["E501"] + +[lint.per-file-ignores] +"__init__.py" = [ + "I001", # allow unsorted imports in __init__.py + "F401", # allow unused imports in __init__.py +] +"examples/*" = [ + "ARG", # allow unused arguments + "F841", # allow unused local variables +] +"tests/*" = [ + "ARG", # allow unused arguments for pytest fixtures + "E741", # allow reused variables + "F841", # allow unused local variables +] diff --git a/setup.py b/setup.py index 12c99e8..e3f0059 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,13 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # from https://github.com/navdeep-G/setup.py # Note: To use the 'upload' functionality of this file, you must: # $ pipenv install twine --dev -import io import os import sys from shutil import rmtree -from setuptools import find_packages, setup, Command +from setuptools import Command, find_packages, setup # Package meta-data. NAME = "mango-agents" @@ -44,7 +42,7 @@ # Import the README and use it as the long-description. # Note: this will only work if 'README.md' is present in your MANIFEST.in file! try: - with io.open(os.path.join(here, "readme.md"), encoding="utf-8") as f: + with open(os.path.join(here, "readme.md"), encoding="utf-8") as f: long_description = "\n" + f.read() except FileNotFoundError: long_description = DESCRIPTION @@ -68,7 +66,7 @@ class UploadCommand(Command): @staticmethod def status(s): """Prints things in bold.""" - print("\033[1m{0}\033[0m".format(s)) + print(f"\033[1m{s}\033[0m") def initialize_options(self): pass @@ -84,13 +82,13 @@ def run(self): pass self.status("Building Source and Wheel (universal) distribution…") - os.system("{0} setup.py sdist bdist_wheel --universal".format(sys.executable)) + os.system(f"{sys.executable} setup.py sdist bdist_wheel --universal") self.status("Uploading the package to PyPI via Twine…") os.system("twine upload dist/*") self.status("Pushing git tags…") - os.system("git tag v{0}".format(about["__version__"])) + os.system("git tag v{}".format(about["__version__"])) os.system("git push --tags") sys.exit() diff --git a/tests/integration_tests/test_distributed_clock.py b/tests/integration_tests/test_distributed_clock.py index 081a687..f7878a7 100644 --- a/tests/integration_tests/test_distributed_clock.py +++ b/tests/integration_tests/test_distributed_clock.py @@ -1,8 +1,7 @@ import asyncio import pytest - -from mango import Agent, create_container +from mango import create_container from mango.messages.codecs import JSON from mango.util.clock import ExternalClock from mango.util.distributed_clock import DistributedClockAgent, DistributedClockManager diff --git a/tests/integration_tests/test_message_roundtrip.py b/tests/integration_tests/test_message_roundtrip.py index 91ff504..7d39a90 100644 --- a/tests/integration_tests/test_message_roundtrip.py +++ b/tests/integration_tests/test_message_roundtrip.py @@ -1,13 +1,12 @@ import asyncio -import pytest -from ..unit_tests.messages.msg_pb2 import MyMsg - import mango.container.factory as container_factory +import pytest from mango.agent.core import Agent -from mango.container.core import Container from mango.messages.codecs import JSON, PROTOBUF, FastJSON +from ..unit_tests.messages.msg_pb2 import MyMsg + M1 = "Hello" M2 = "Hello2" M3 = "Goodbye" @@ -127,7 +126,6 @@ async def start(self): ) # shut down - pass # ReplierAgent: @@ -166,7 +164,6 @@ async def start(self): await self.got_second # shut down - pass @pytest.mark.asyncio diff --git a/tests/integration_tests/test_message_roundtrip_mp.py b/tests/integration_tests/test_message_roundtrip_mp.py index 800fba8..3ca17fe 100644 --- a/tests/integration_tests/test_message_roundtrip_mp.py +++ b/tests/integration_tests/test_message_roundtrip_mp.py @@ -1,8 +1,7 @@ import asyncio -import pytest - import mango.container.factory as container_factory +import pytest from mango.agent.core import Agent diff --git a/tests/integration_tests/test_single_container_termination.py b/tests/integration_tests/test_single_container_termination.py index 09bd39f..8547df6 100644 --- a/tests/integration_tests/test_single_container_termination.py +++ b/tests/integration_tests/test_single_container_termination.py @@ -1,11 +1,8 @@ import asyncio import pytest - from mango import Agent, create_container -from mango.messages.codecs import JSON from mango.util.clock import ExternalClock -from mango.util.distributed_clock import DistributedClockAgent, DistributedClockManager from mango.util.termination_detection import tasks_complete_or_sleeping from multiprocessing import Process diff --git a/tests/unit_tests/clock/test_external_clock.py b/tests/unit_tests/clock/test_external_clock.py index 495c272..388e298 100644 --- a/tests/unit_tests/clock/test_external_clock.py +++ b/tests/unit_tests/clock/test_external_clock.py @@ -2,7 +2,6 @@ import time import pytest - from mango.util.clock import AsyncioClock, ExternalClock from mango.util.scheduling import Scheduler @@ -86,8 +85,10 @@ async def test_schedule_timestamp_task(): for simulation_time, real_time in results_dict_external.items(): if int(simulation_time) < simulation_time: - simulation_time = int(simulation_time) + 1 - assert round(simulation_time / 10, 1) == round(real_time, 1) + sim_time = int(simulation_time) + 1 + else: + sim_time = simulation_time + assert round(sim_time / 10, 1) == round(real_time, 1) for simulation_time, real_time in results_dict_asyncio.items(): assert round(simulation_time, 1) == round(real_time, 1) diff --git a/tests/unit_tests/container/test_mp.py b/tests/unit_tests/container/test_mp.py index 35d6a9e..7cd3235 100644 --- a/tests/unit_tests/container/test_mp.py +++ b/tests/unit_tests/container/test_mp.py @@ -1,6 +1,7 @@ import asyncio + import pytest -from mango import create_container, Agent +from mango import Agent, create_container class MyAgent(Agent): @@ -106,18 +107,18 @@ async def test_agent_processes_ping_pong_p_to_p(): c = await create_container(addr=addr, copy_internal_messages=False) await c.as_agent_process( agent_creator=lambda container: P2PTestAgent( - container, aid_main_agent, suggested_aid=f"process_agent1" + container, aid_main_agent, suggested_aid="process_agent1" ) ) main_agent = P2PMainAgent(c, suggested_aid=aid_main_agent) # WHEN def agent_init(c): - agent = MyAgent(c, suggested_aid=f"process_agent2") + agent = MyAgent(c, suggested_aid="process_agent2") agent.schedule_instant_acl_message( "Message To Process Agent", receiver_addr=addr, - receiver_id=f"process_agent1", + receiver_id="process_agent1", acl_metadata={"sender_id": agent.aid}, ) return agent @@ -131,6 +132,7 @@ def agent_init(c): await c.shutdown() + @pytest.mark.asyncio async def test_async_agent_processes_ping_pong_p_to_p(): # GIVEN @@ -140,9 +142,7 @@ async def test_async_agent_processes_ping_pong_p_to_p(): main_agent = P2PMainAgent(c, suggested_aid=aid_main_agent) async def agent_creator(container): - p2pta = P2PTestAgent( - container, aid_main_agent, suggested_aid=f"process_agent1" - ) + p2pta = P2PTestAgent(container, aid_main_agent, suggested_aid="process_agent1") await p2pta.send_message( content="pong", receiver_addr=addr, @@ -150,21 +150,18 @@ async def agent_creator(container): acl_metadata={ "sender_addr": p2pta.addr, "sender_id": p2pta.aid, - } + }, ) - await c.as_agent_process( - agent_creator=agent_creator - ) - + await c.as_agent_process(agent_creator=agent_creator) # WHEN def agent_init(c): - agent = MyAgent(c, suggested_aid=f"process_agent2") + agent = MyAgent(c, suggested_aid="process_agent2") agent.schedule_instant_acl_message( "Message To Process Agent", receiver_addr=addr, - receiver_id=f"process_agent1", + receiver_id="process_agent1", acl_metadata={"sender_id": agent.aid}, ) return agent diff --git a/tests/unit_tests/container/test_tcp.py b/tests/unit_tests/container/test_tcp.py index 13f2f81..133e037 100644 --- a/tests/unit_tests/container/test_tcp.py +++ b/tests/unit_tests/container/test_tcp.py @@ -1,14 +1,17 @@ -import pytest import asyncio -from mango.container.tcp import TCPConnectionPool -from mango.container.protocol import ContainerProtocol + +import pytest from mango import create_container +from mango.container.protocol import ContainerProtocol +from mango.container.tcp import TCPConnectionPool + @pytest.mark.asyncio async def test_connection_open_close(): c = await create_container(addr=("127.0.0.2", 5555), copy_internal_messages=False) await c.shutdown() + @pytest.mark.asyncio async def test_connection_pool_obtain_release(): c = await create_container(addr=("127.0.0.2", 5555), copy_internal_messages=False) diff --git a/tests/unit_tests/core/test_agent.py b/tests/unit_tests/core/test_agent.py index 605c008..85c408e 100644 --- a/tests/unit_tests/core/test_agent.py +++ b/tests/unit_tests/core/test_agent.py @@ -2,13 +2,11 @@ from typing import Any, Dict import pytest - from mango import create_container from mango.agent.core import Agent class MyAgent(Agent): - test_counter: int = 0 def handle_message(self, content, meta: Dict[str, Any]): diff --git a/tests/unit_tests/core/test_container.py b/tests/unit_tests/core/test_container.py index c36c2b8..cb4fef9 100644 --- a/tests/unit_tests/core/test_container.py +++ b/tests/unit_tests/core/test_container.py @@ -1,6 +1,5 @@ -import pytest - import mango.container.factory as container_factory +import pytest from mango.agent.core import Agent diff --git a/tests/unit_tests/core/test_external_scheduling_container.py b/tests/unit_tests/core/test_external_scheduling_container.py index 86ec941..befe587 100644 --- a/tests/unit_tests/core/test_external_scheduling_container.py +++ b/tests/unit_tests/core/test_external_scheduling_container.py @@ -1,12 +1,11 @@ import asyncio from typing import Any, Dict -import pytest - import mango.container.factory as container_factory +import pytest from mango.agent.core import Agent -from mango.container.factory import EXTERNAL_CONNECTION from mango.container.external_coupling import ExternalAgentMessage +from mango.container.factory import EXTERNAL_CONNECTION from mango.messages.message import ACLMessage from mango.util.clock import ExternalClock @@ -30,7 +29,9 @@ async def test_send_msg(): content="test", receiver_addr="eid321", receiver_id="Agent0" ) assert len(external_scheduling_container.message_buffer) == 1 - external_agent_msg: ExternalAgentMessage = external_scheduling_container.message_buffer[0] + external_agent_msg: ExternalAgentMessage = ( + external_scheduling_container.message_buffer[0] + ) assert external_agent_msg.receiver == "eid321" decoded_msg = external_scheduling_container.codec.decode(external_agent_msg.message) assert decoded_msg.content == "test" @@ -47,7 +48,9 @@ async def test_step(): await external_scheduling_container.send_acl_message( content="test", receiver_addr="eid321", receiver_id="Agent0" ) - step_output = await external_scheduling_container.step(simulation_time=12, incoming_messages=[]) + step_output = await external_scheduling_container.step( + simulation_time=12, incoming_messages=[] + ) assert external_scheduling_container.message_buffer == [] assert external_scheduling_container.clock.time == 12 assert 0 < step_output.duration < 0.01 @@ -135,7 +138,9 @@ async def test_step_with_cond_task(): # advance time without anything happening print("starting step") return_values = await asyncio.wait_for( - external_scheduling_container.step(simulation_time=current_time, incoming_messages=[]), + external_scheduling_container.step( + simulation_time=current_time, incoming_messages=[] + ), timeout=1, ) @@ -147,7 +152,9 @@ async def test_step_with_cond_task(): # create and send message in next step message = external_scheduling_container._create_acl( - content="", receiver_addr=external_scheduling_container.addr, receiver_id=agent_1.aid + content="", + receiver_addr=external_scheduling_container.addr, + receiver_id=agent_1.aid, ) encoded_msg = external_scheduling_container.codec.encode(message) print("created message") @@ -206,7 +213,9 @@ async def test_send_internal_messages(): ) agent_1 = SelfSendAgent(container=external_scheduling_container, final_number=3) message = external_scheduling_container._create_acl( - content="", receiver_addr=external_scheduling_container.addr, receiver_id=agent_1.aid + content="", + receiver_addr=external_scheduling_container.addr, + receiver_id=agent_1.aid, ) encoded_msg = external_scheduling_container.codec.encode(message) return_values = await external_scheduling_container.step( @@ -242,14 +251,19 @@ async def test_step_with_replying_agent(): < external_scheduling_container.clock.time + 0.1 ) assert ( - container_output.messages[2].time > external_scheduling_container.clock.time + 0.1 + container_output.messages[2].time + > external_scheduling_container.clock.time + 0.1 ) # since we had a sleep of 0.1 seconds - assert container_output.next_activity == external_scheduling_container.clock.time + 10 + assert ( + container_output.next_activity == external_scheduling_container.clock.time + 10 + ) container_output = await external_scheduling_container.step( simulation_time=20, incoming_messages=[] ) assert len(container_output.messages) == 1 - assert container_output.next_activity == external_scheduling_container.clock.time + 10 + assert ( + container_output.next_activity == external_scheduling_container.clock.time + 10 + ) await reply_agent.stop_tasks() await external_scheduling_container.shutdown() diff --git a/tests/unit_tests/messages/msg.proto b/tests/unit_tests/messages/msg.proto index b877de9..cbe0490 100644 --- a/tests/unit_tests/messages/msg.proto +++ b/tests/unit_tests/messages/msg.proto @@ -2,4 +2,4 @@ syntax = "proto3"; message MyMsg { bytes content = 1; -} \ No newline at end of file +} diff --git a/tests/unit_tests/messages/msg_pb2.py b/tests/unit_tests/messages/msg_pb2.py index 78cac3b..40418a0 100644 --- a/tests/unit_tests/messages/msg_pb2.py +++ b/tests/unit_tests/messages/msg_pb2.py @@ -1,36 +1,34 @@ -# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: tests/unit_tests/messages/msg.proto # Protobuf Python Version: 5.27.2 """Generated protocol buffer code.""" + from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder + _runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 5, - 27, - 2, - '', - 'tests/unit_tests/messages/msg.proto' + _runtime_version.Domain.PUBLIC, 5, 27, 2, "", "tests/unit_tests/messages/msg.proto" ) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#tests/unit_tests/messages/msg.proto\"\x18\n\x05MyMsg\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\x0c\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n#tests/unit_tests/messages/msg.proto"\x18\n\x05MyMsg\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\x0c\x62\x06proto3' +) _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tests.unit_tests.messages.msg_pb2', _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "tests.unit_tests.messages.msg_pb2", _globals +) if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_MYMSG']._serialized_start=39 - _globals['_MYMSG']._serialized_end=63 + DESCRIPTOR._loaded_options = None + _globals["_MYMSG"]._serialized_start = 39 + _globals["_MYMSG"]._serialized_end = 63 # @@protoc_insertion_point(module_scope) diff --git a/tests/unit_tests/messages/test_codecs.py b/tests/unit_tests/messages/test_codecs.py index dd796ad..2225671 100644 --- a/tests/unit_tests/messages/test_codecs.py +++ b/tests/unit_tests/messages/test_codecs.py @@ -2,8 +2,6 @@ from dataclasses import dataclass import pytest -from .msg_pb2 import MyMsg - from mango.messages.codecs import ( JSON, PROTOBUF, @@ -13,6 +11,8 @@ ) from mango.messages.message import ACLMessage, Performatives +from .msg_pb2 import MyMsg + testcodecs = [JSON, PROTOBUF] diff --git a/tests/unit_tests/role/role_test.py b/tests/unit_tests/role/role_test.py index 12a859c..2a9a0f8 100644 --- a/tests/unit_tests/role/role_test.py +++ b/tests/unit_tests/role/role_test.py @@ -1,7 +1,8 @@ -from mango.agent.role import Role, RoleHandler, DataContainer, RoleContext -from mango.util.scheduling import Scheduler from dataclasses import dataclass +from mango.agent.role import DataContainer, Role, RoleContext, RoleHandler +from mango.util.scheduling import Scheduler + class RoleModel: def __init__(self): @@ -100,7 +101,7 @@ def test_data_container(): # WHEN THEN assert "abc" in data_container assert "cba" in data_container - assert not "bca" in data_container + assert "bca" not in data_container @dataclass @@ -145,15 +146,14 @@ def test_emit_event(): assert ex_role2.event == event assert ex_role2.source == ex_role -def test_data_container(): + +def test_data_container_get(): # GIVEN data_container = DataContainer() data_container["abc"] = "123" - data_container.update({ - "cba": 123 - }) + data_container.update({"cba": 123}) # WHEN THEN assert data_container.cba == 123 assert data_container.get("abc") == "123" - assert data_container.get("bca") is None \ No newline at end of file + assert data_container.get("bca") is None diff --git a/tests/unit_tests/role_agent_test.py b/tests/unit_tests/role_agent_test.py index 46f4824..2778ab7 100644 --- a/tests/unit_tests/role_agent_test.py +++ b/tests/unit_tests/role_agent_test.py @@ -3,9 +3,8 @@ from abc import abstractmethod from typing import Any, Dict -import pytest - import mango.container.factory as container_factory +import pytest from mango.agent.role import Role, RoleAgent, RoleContext from mango.util.scheduling import TimestampScheduledTask @@ -166,7 +165,7 @@ async def test_send_ping_pong(num_agents, num_containers): if a._check_inbox_task.exception() is not None: raise a._check_inbox_task.exception() else: - assert False, f"check_inbox terminated unexpectedly." + assert False, "check_inbox terminated unexpectedly." for a in agents: await a.tasks_complete() @@ -209,7 +208,7 @@ async def test_send_ping_pong_deactivated_pong(num_agents, num_containers): if a._check_inbox_task.exception() is not None: raise a._check_inbox_task.exception() else: - assert False, f"check_inbox terminated unexpectedly." + assert False, "check_inbox terminated unexpectedly." for a in agents: await a.tasks_complete() diff --git a/tests/unit_tests/test_agents.py b/tests/unit_tests/test_agents.py index 2b81da4..595e8d0 100644 --- a/tests/unit_tests/test_agents.py +++ b/tests/unit_tests/test_agents.py @@ -1,9 +1,8 @@ import asyncio from typing import Any, Dict -import pytest - import mango.container.factory as container_factory +import pytest from mango.agent.core import Agent @@ -128,7 +127,7 @@ async def test_send_ping_pong(num_agents, num_containers): if a._check_inbox_task.exception() is not None: raise a._check_inbox_task.exception() else: - assert False, f"check_inbox terminated unexpectedly." + assert False, "check_inbox terminated unexpectedly." for a in agents: # await a.wait_for_sending_messages() await a.wait_for_pong_replies() diff --git a/tests/unit_tests/util/scheduling_test.py b/tests/unit_tests/util/scheduling_test.py index d66ff7e..b7497c6 100644 --- a/tests/unit_tests/util/scheduling_test.py +++ b/tests/unit_tests/util/scheduling_test.py @@ -4,12 +4,10 @@ import pytest from dateutil import rrule - from mango.util.clock import ExternalClock from mango.util.scheduling import ( ConditionalProcessTask, InstantScheduledProcessTask, - InstantScheduledTask, PeriodicScheduledTask, RecurrentScheduledTask, Scheduler, @@ -29,13 +27,9 @@ async def test_recurrent(): async def increase_counter(): l.append(1) - recurrency = rrule.rrule( - rrule.DAILY, interval=1, dtstart=start, until=end - ) + recurrency = rrule.rrule(rrule.DAILY, interval=1, dtstart=start, until=end) # WHEN - scheduler.schedule_task( - RecurrentScheduledTask(increase_counter, recurrency, clock) - ) + scheduler.schedule_task(RecurrentScheduledTask(increase_counter, recurrency, clock)) clock.set_time(start.timestamp()) await asyncio.sleep(0) # THEN @@ -141,7 +135,7 @@ async def increase_counter(): # WHEN t = scheduler.schedule_task(PeriodicScheduledTask(increase_counter, 0.2)) - + with pytest.raises(asyncio.exceptions.TimeoutError): await asyncio.wait_for(t, timeout=0.3)