From a94625e79c0865cd9bbae4e0c3411e6ac0b4b4f2 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Sun, 28 Jan 2024 23:10:46 +0200 Subject: [PATCH 01/16] Moved REST Proxy implementation in a separate repository --- commlib/rest_proxy.py | 119 ------------------------------ examples/rest_proxy/client.py | 51 ------------- examples/rest_proxy/rest_proxy.py | 25 ------- 3 files changed, 195 deletions(-) delete mode 100644 commlib/rest_proxy.py delete mode 100755 examples/rest_proxy/client.py delete mode 100755 examples/rest_proxy/rest_proxy.py diff --git a/commlib/rest_proxy.py b/commlib/rest_proxy.py deleted file mode 100644 index 7133956..0000000 --- a/commlib/rest_proxy.py +++ /dev/null @@ -1,119 +0,0 @@ -import json -import time -from typing import Any, Dict, Optional, Union - -import requests - -from commlib.msg import RPCMessage -from commlib import Node - - -""" -{ - BASE_URL: 'https://example.org:9080', - VERB: 'GET', - PARAMS: { - QUERY: [{name: '', val: ''}]. - PATH: [{name: '', val: ''}], - BODY: [{name: '', val: ''}] - } - HEADERS: {} -} - -""" - - -class RESTProxyMessage(RPCMessage): - class Request(RPCMessage.Request): - host: Optional[str] = "localhost" - port: Optional[int] = 8080 - ssl: Optional[bool] = False - base_url: str = "" - path: str = "/" - verb: str = "GET" - query_params: Dict = {} - path_params: Dict = {} - body_params: Dict = {} - headers: Dict = {} - - class Response(RPCMessage.Response): - data: Union[str, Dict, int] - headers: Dict[str, Any] - status_code: int = 200 - - -class RestProxyServer: - def __init__(self, service_map): - self._service_map = service_map - - -class RESTProxy(Node): - """RESTProxy. - - REST Proxy implementation class. Call REST Web services via commlib - supported protocols (AMQP, MQTT, REDIS). - """ - - def __init__( - self, broker_uri: str, broker_params: Any, debug: bool = False, *args, **kwargs - ): - """__init__. - - Args: - broker_uri (str): broker_uri - broker_type (TransportType): broker_type - broker_params (Any): broker_params - debug (bool): debug - """ - self._debug = debug - - super().__init__( - node_name="util.rest_proxy", - connection_params=broker_params, - *args, - **kwargs, - ) - self._broker_uri = broker_uri - self._svc = self.create_rpc( - rpc_name=broker_uri, msg_type=RESTProxyMessage, on_request=self._on_request - ) - self.logger().info(f"Initiated REST Proxy @ {broker_uri}") - - def _on_request(self, msg: RESTProxyMessage.Request): - schema = "https" if msg.ssl else "http" - port = msg.port - if port is None: - port = 443 if msg.ssl else 80 - url = f"{schema}://{msg.host}:{port}{msg.base_url}{msg.path}" - self.logger().info(f"Request for: {url}") - # -------- > Perform HTTP Request from input message - if msg.verb == "GET": - resp = requests.get(url, params=msg.query_params, headers=msg.headers) - elif msg.verb == "PUT": - resp = requests.put( - url, params=msg.query_params, data=msg.body_params, headers=msg.headers - ) - elif msg.verb == "POST": - resp = requests.post( - url, params=msg.query_params, data=msg.body_params, headers=msg.headers - ) - else: - raise ValueError(f"HTTP Verb [{msg.verb}] is not valid!") - # <--------------------------------------------------- - headers = dict(**resp.headers) - data = resp.text - if headers.get("Content-Type") == "application/json": - data = json.loads(data) - return RESTProxyMessage.Response( - data=data, headers=headers, status_code=resp.status_code - ) - - def run(self): - """run.""" - self._svc.run() - - def run_forever(self): - """run_forever.""" - self._svc.run() - while True: - time.sleep(0.01) diff --git a/examples/rest_proxy/client.py b/examples/rest_proxy/client.py deleted file mode 100755 index 936e2e2..0000000 --- a/examples/rest_proxy/client.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python - -import sys -import time - -from commlib.node import Node -from commlib.rest_proxy import RESTProxyMessage - - -if __name__ == "__main__": - if len(sys.argv) < 2: - broker = "redis" - else: - broker = str(sys.argv[1]) - if broker == "redis": - from commlib.transports.redis import ConnectionParameters - elif broker == "amqp": - from commlib.transports.amqp import ConnectionParameters - elif broker == "mqtt": - from commlib.transports.mqtt import ConnectionParameters - else: - print("Not a valid broker-type was given!") - sys.exit(1) - conn_params = ConnectionParameters() - - node = Node( - node_name="rest_proxy_client", - connection_params=conn_params, - # heartbeat_uri='nodes.add_two_ints.heartbeat', - debug=True, - ) - - rpc = node.create_rpc_client(msg_type=RESTProxyMessage, rpc_name="proxy.rest") - node.run() - - msg = RESTProxyMessage.Request( - host="httpbin.org", - port=443, - ssl=True, - base_url="", - path="/put", - verb="PUT", - query_params={"a": 1, "b": 2}, - body_params={"c": 3, "d": 4}, - ) - - while True: - # returns AddTwoIntMessage.Response instance - resp = rpc.call(msg) - print(resp) - time.sleep(10) diff --git a/examples/rest_proxy/rest_proxy.py b/examples/rest_proxy/rest_proxy.py deleted file mode 100755 index 91dc973..0000000 --- a/examples/rest_proxy/rest_proxy.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -import sys - -from commlib.rest_proxy import RESTProxy - -if __name__ == "__main__": - if len(sys.argv) < 2: - broker = "redis" - else: - broker = str(sys.argv[1]) - if broker == "redis": - from commlib.transports.redis import ConnectionParameters - elif broker == "amqp": - from commlib.transports.amqp import ConnectionParameters - elif broker == "mqtt": - from commlib.transports.mqtt import ConnectionParameters - else: - print("Not a valid broker-type was given!") - sys.exit(1) - conn_params = ConnectionParameters() - - proxy = RESTProxy("proxy.rest", conn_params, debug=True) - - proxy.run_forever() From 7947c8671d4dbbe38dfba54ef9caa3cbf614bf9c Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Fri, 9 Feb 2024 20:15:34 +0200 Subject: [PATCH 02/16] Code improvements --- commlib/bridges.py | 18 --------------- commlib/connection.py | 2 +- commlib/node.py | 3 +-- commlib/transports/amqp.py | 2 +- commlib/transports/kafka.py | 35 ++++++++++++------------------ commlib/transports/mqtt.py | 6 ++--- commlib/transports/redis.py | 6 ++--- examples/bridges/bridge_example.py | 6 ++--- 8 files changed, 26 insertions(+), 52 deletions(-) diff --git a/commlib/bridges.py b/commlib/bridges.py index fb6f82d..20bcbdf 100644 --- a/commlib/bridges.py +++ b/commlib/bridges.py @@ -305,40 +305,22 @@ def __init__( bB_type_str = str(type(self._to_broker_params)).split("'")[1] if "redis" in bA_type_str and "amqp" in bB_type_str: self._btype = TopicBridgeType.REDIS_TO_AMQP - from_transport = TransportType.REDIS - to_transport = TransportType.AMQP elif "amqp" in bA_type_str and "redis" in bB_type_str: self._btype = TopicBridgeType.AMQP_TO_REDIS - from_transport = TransportType.AMQP - to_transport = TransportType.REDIS elif "amqp" in bA_type_str and "amqp" in bB_type_str: self._btype = TopicBridgeType.AMQP_TO_AMQP - from_transport = TransportType.AMQP - to_transport = TransportType.AMQP elif "redis" in bA_type_str and "redis" in bB_type_str: self._btype = TopicBridgeType.REDIS_TO_REDIS - from_transport = TransportType.REDIS - to_transport = TransportType.REDIS elif "mqtt" in bA_type_str and "redis" in bB_type_str: self._btype = TopicBridgeType.MQTT_TO_REDIS - from_transport = TransportType.MQTT - to_transport = TransportType.REDIS elif "mqtt" in bA_type_str and "amqp" in bB_type_str: self._btype = TopicBridgeType.MQTT_TO_AMQP - from_transport = TransportType.MQTT - to_transport = TransportType.AMQP elif "mqtt" in bA_type_str and "mqtt" in bB_type_str: self._btype = TopicBridgeType.MQTT_TO_MQTT - from_transport = TransportType.MQTT - to_transport = TransportType.MQTT elif "redis" in bA_type_str and "mqtt" in bB_type_str: self._btype = TopicBridgeType.REDIS_TO_MQTT - from_transport = TransportType.REDIS - to_transport = TransportType.MQTT elif "amqp" in bA_type_str and "mqtt" in bB_type_str: self._btype = TopicBridgeType.AMQP_TO_MQTT - from_transport = TransportType.AMQP - to_transport = TransportType.MQTT self._sub = endpoint_factory(EndpointType.PSubscriber, self._from_transport)( topic=self._from_uri, msg_type=self._msg_type, diff --git a/commlib/connection.py b/commlib/connection.py index 184a1a4..ca86ce3 100644 --- a/commlib/connection.py +++ b/commlib/connection.py @@ -16,4 +16,4 @@ class BaseConnectionParameters(BaseModel): ssl: bool = False ssl_insecure: bool = False reconnect: bool = False - reconnect_wait: int = 5 \ No newline at end of file + reconnect_wait: int = 5 diff --git a/commlib/node.py b/commlib/node.py index 50c01fc..cb78208 100644 --- a/commlib/node.py +++ b/commlib/node.py @@ -1,6 +1,5 @@ import logging import threading -from threading import Event import time from enum import IntEnum from typing import Any, List, Optional @@ -342,7 +341,7 @@ def run_forever(self, sleep_rate: float = 0.01) -> None: self.run() try: while self.state != NodeState.EXITED: - time.sleep(sleep_rate) + time.sleep(sleep_rate) except: pass self.stop() diff --git a/commlib/transports/amqp.py b/commlib/transports/amqp.py index deac08f..f7da9fc 100644 --- a/commlib/transports/amqp.py +++ b/commlib/transports/amqp.py @@ -492,7 +492,7 @@ def _rpc_exists(self): return self._transport.queue_exists(self._rpc_name) def _on_request_handle(self, ch, method, properties, body): - task = self._executor.submit( + self._executor.submit( self._on_request_callback, ch, method, properties, body ) # TODO handle tasks diff --git a/commlib/transports/kafka.py b/commlib/transports/kafka.py index bc28371..cee38a8 100644 --- a/commlib/transports/kafka.py +++ b/commlib/transports/kafka.py @@ -321,14 +321,10 @@ def _on_message(self, msg: Any): class RPCService(BaseRPCService): - """RPCService. - MQTT RPC Service class. - """ - def __init__(self, *args, **kwargs): raise NotImplementedError("RPCService for Kafka transport not supported") super(RPCService, self).__init__(*args, **kwargs) - self._transport = MQTTTransport( + self._transport = KafkaTransport( conn_params=self._conn_params, serializer=self._serializer, compression=self._compression, @@ -338,10 +334,10 @@ def _send_response(self, data: Dict[str, Any], reply_to: str): self._comm_obj.header.timestamp = gen_timestamp() # pylint: disable=E0237 self._comm_obj.data = data _resp = self._comm_obj.dict() - self._transport.publish(reply_to, _resp, qos=MQTTQoS.L1) + self._transport.publish(reply_to, _resp) def _on_request_handle(self, client: Any, userdata: Any, msg: Dict[str, Any]): - task = self._executor.submit(self._on_request_internal, client, userdata, msg) + self._executor.submit(self._on_request_internal, client, userdata, msg) def _on_request_internal(self, client: Any, userdata: Any, msg: Dict[str, Any]): try: @@ -378,9 +374,7 @@ def _unpack_comm_msg(self, msg: Any) -> Tuple[CommRPCMessage, str]: def run_forever(self): """run_forever.""" - self._transport.subscribe( - self._rpc_name, self._on_request_handle, qos=MQTTQoS.L1 - ) + self._transport.subscribe(self._rpc_name, self._on_request_handle) self._transport.start() while True: if self._t_stop_event is not None: @@ -400,7 +394,7 @@ def __init__(self, *args, **kwargs): kwargs: See BaseRPCServer """ super(RPCServer, self).__init__(*args, **kwargs) - self._transport = MQTTTransport( + self._transport = KafkaTransport( conn_params=self._conn_params, serializer=self._serializer, compression=self._compression, @@ -420,10 +414,10 @@ def _send_response(self, data: Dict[str, Any], reply_to: str): self._comm_obj.header.timestamp = gen_timestamp() # pylint: disable=E0237 self._comm_obj.data = data _resp = self._comm_obj.dict() - self._transport.publish(reply_to, _resp, qos=MQTTQoS.L1) + self._transport.publish(reply_to, _resp) def _on_request_handle(self, client: Any, userdata: Any, msg: Dict[str, Any]): - task = self._executor.submit(self._on_request_internal, client, userdata, msg) + self._executor.submit(self._on_request_internal, client, userdata, msg) def _on_request_internal(self, client: Any, userdata: Any, msg: Dict[str, Any]): """_on_request_internal. @@ -493,7 +487,7 @@ def _register_endpoint( else: full_uri = f"{self._base_uri}.{uri}" self.log.info(f"Registering endpoint <{full_uri}>") - self._transport.subscribe(full_uri, self._on_request_handle, qos=MQTTQoS.L1) + self._transport.subscribe(full_uri, self._on_request_handle) def run_forever(self): """run_forever.""" @@ -509,7 +503,6 @@ def run_forever(self): class RPCClient(BaseRPCClient): """RPCClient. - MQTT RPC Client """ def __init__(self, *args, **kwargs): @@ -522,7 +515,7 @@ def __init__(self, *args, **kwargs): self._response = None super(RPCClient, self).__init__(*args, **kwargs) - self._transport = MQTTTransport( + self._transport = KafkaTransport( conn_params=self._conn_params, serializer=self._serializer, compression=self._compression, @@ -575,7 +568,9 @@ def _wait_for_response(self, timeout: float = 10.0): while self._response is None: elapsed_t = time.time() - start_t if elapsed_t >= timeout: - raise RPCClientTimeoutError(f"Response timeout after {timeout} seconds") + raise RPCClientTimeoutError( + f"Response timeout after {timeout} seconds" + ) time.sleep(0.001) return self._response @@ -599,10 +594,10 @@ def call(self, msg: RPCMessage.Request, timeout: float = 30) -> RPCMessage.Respo _reply_to = _msg["header"]["reply_to"] self._transport.subscribe( - _reply_to, callback=self._on_response_wrapper, qos=MQTTQoS.L1 + _reply_to, callback=self._on_response_wrapper ) start_t = time.time() - self._transport.publish(self._rpc_name, _msg, qos=MQTTQoS.L1) + self._transport.publish(self._rpc_name, _msg) _resp = self._wait_for_response(timeout=timeout) elapsed_t = time.time() - start_t self._delay = elapsed_t @@ -615,7 +610,6 @@ def call(self, msg: RPCMessage.Request, timeout: float = 30) -> RPCMessage.Respo class ActionService(BaseActionService): """ActionService. - MQTT Action Server """ def __init__(self, *args, **kwargs): @@ -664,7 +658,6 @@ def __init__(self, *args, **kwargs): class ActionClient(BaseActionClient): """ActionClient. - MQTT Action Client """ def __init__(self, *args, **kwargs): diff --git a/commlib/transports/mqtt.py b/commlib/transports/mqtt.py index c644b78..b586275 100644 --- a/commlib/transports/mqtt.py +++ b/commlib/transports/mqtt.py @@ -276,7 +276,7 @@ def publish( pl = self._serializer.serialize(payload) if self._compression != CompressionType.NO_COMPRESSION: pl = inflate_str(pl) - ph = self._client.publish( + self._client.publish( topic, pl, qos=qos, retain=retain, properties=self._mqtt_properties ) @@ -513,7 +513,7 @@ def _send_response(self, data: Dict[str, Any], reply_to: str): self._transport.publish(reply_to, _resp, qos=MQTTQoS.L1) def _on_request_handle(self, client: Any, userdata: Any, msg: Dict[str, Any]): - task = self._executor.submit(self._on_request_internal, client, userdata, msg) + self._executor.submit(self._on_request_internal, client, userdata, msg) def _on_request_internal(self, client: Any, userdata: Any, msg: Dict[str, Any]): try: @@ -595,7 +595,7 @@ def _send_response(self, data: Dict[str, Any], reply_to: str): self._transport.publish(reply_to, _resp, qos=MQTTQoS.L1) def _on_request_handle(self, client: Any, userdata: Any, msg: Dict[str, Any]): - task = self._executor.submit(self._on_request_internal, client, userdata, msg) + self._executor.submit(self._on_request_internal, client, userdata, msg) def _on_request_internal(self, client: Any, userdata: Any, msg: Dict[str, Any]): """_on_request_internal. diff --git a/commlib/transports/redis.py b/commlib/transports/redis.py index b3eb586..1827d52 100644 --- a/commlib/transports/redis.py +++ b/commlib/transports/redis.py @@ -29,7 +29,7 @@ from commlib.transports.base_transport import BaseTransport from commlib.utils import gen_timestamp -from rich import print, console, pretty +from rich import console, pretty pretty.install() console = console.Console() @@ -162,7 +162,7 @@ def wait_for_msg(self, queue_name: str, timeout=10): msgq, payload = self._redis.blpop(queue_name, timeout=timeout) if self._compression != CompressionType.NO_COMPRESSION: payload = deflate(payload) - except Exception as exc: + except Exception: self.log.error(f"Timeout after {timeout} seconds waiting for message") msgq = "" payload = None @@ -195,7 +195,7 @@ def _send_response(self, data: Dict[str, Any], reply_to: str): self._transport.push_msg_to_queue(reply_to, _resp) def _on_request_handle(self, data: Dict[str, Any], header: Dict[str, Any]): - task = self._executor.submit(self._on_request_internal, data, header) + self._executor.submit(self._on_request_internal, data, header) def _on_request_internal(self, data: Dict[str, Any], header: Dict[str, Any]): if "reply_to" not in header: diff --git a/examples/bridges/bridge_example.py b/examples/bridges/bridge_example.py index 8d9ddaa..b70c7f6 100755 --- a/examples/bridges/bridge_example.py +++ b/examples/bridges/bridge_example.py @@ -4,9 +4,9 @@ import commlib.transports.mqtt as mcomm import commlib.transports.redis as rcomm -from commlib.bridges import RPCBridge, RPCBridgeType, TopicBridge, TopicBridgeType -from rich import print, console, pretty -from commlib.msg import PubSubMessage, RPCMessage +from commlib.bridges import RPCBridge, TopicBridge +from rich import print, pretty +from commlib.msg import RPCMessage pretty.install() From 0489e176a5fd6a992532a0f5a3fafa5bbf07fb33 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Tue, 13 Feb 2024 21:53:16 +0200 Subject: [PATCH 03/16] Lock paho-mqtt < 2.0.0 as it breaks compatibility --- poetry.lock | 14 +++++++------- pyproject.toml | 10 +++++----- requirements-dev.txt | 3 +-- requirements.txt | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5addda4..cf7603c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -41,13 +41,13 @@ files = [ [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -465,13 +465,13 @@ twisted = ["twisted"] [[package]] name = "pip" -version = "23.3.2" +version = "24.0" description = "The PyPA recommended tool for installing Python packages." optional = false python-versions = ">=3.7" files = [ - {file = "pip-23.3.2-py3-none-any.whl", hash = "sha256:5052d7889c1f9d05224cd41741acb7c5d6fa735ab34e339624a614eaaa7e7d76"}, - {file = "pip-23.3.2.tar.gz", hash = "sha256:7fd9972f96db22c8077a1ee2691b172c8089b17a5652a44494a9ecb0d78f9149"}, + {file = "pip-24.0-py3-none-any.whl", hash = "sha256:ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc"}, + {file = "pip-24.0.tar.gz", hash = "sha256:ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2"}, ] [[package]] @@ -843,4 +843,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "1c42f557757de9a51a896a3629e3c6570570b65612dbf46debe9de08aca9240e" +content-hash = "9e096e7f5cbf77d2ac85b278572a5de8d65ef1c0ea4927fd0ea46a2a1bc03c2f" diff --git a/pyproject.toml b/pyproject.toml index 0b5ce77..17ddcbc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,13 +16,13 @@ packages = [{include = "commlib"}] [tool.poetry.dependencies] python = "^3.7" wheel = ">=0.38.0" -pydantic = ">=2.0.0" -ujson = ">=5.7.0" +pydantic = "^2.0.0" +ujson = "^5.7.0" rich = "^13.7.0" redis = {extras = ["hiredis"], version = "^5.0.1"} -paho-mqtt = ">=1.6.1" -pika = ">=1.3.1" -requests = ">=2.1.0" +paho-mqtt = "^1.6.1" +pika = "^1.3.1" +requests = "^2.1.0" [tool.poetry.group.dev.dependencies] bump2version = "^0.5.11" diff --git a/requirements-dev.txt b/requirements-dev.txt index 2bd5a21..beecaf6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,4 @@ pip>=21.1 bump2version>=0.5.11 -wheel>=0.38.4 flake8>=3.7.8 -coverage>=7.0.1 +coverage>=7.0.1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1e75fcd..ed995e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,6 @@ pydantic>=2.0.0 ujson>=5.7.0 rich redis[hiredis] -paho-mqtt>=1.6.1 +paho-mqtt>=1.6.1,<2.0.0 pika>=1.3.1 requests>=2.1.0 From 9fc4037c267f959c646a9293bc89271cf7b54327 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Tue, 13 Feb 2024 22:07:54 +0200 Subject: [PATCH 04/16] Updates README.md --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b69fc32..87e17d3 100644 --- a/README.md +++ b/README.md @@ -844,19 +844,16 @@ Run tests by executing `tox` command under this repo directory: make tests ``` -**TODO - Currently working on!!** +# Development -# Docs +Starting from 2024 (>0.11.3) only PRs from the `devel` branch are merged into the `master` branch. -Make docs by running: +Other branches are merged into `devel` first. -```bash -make docs -``` - -**TODO - Currently working on!!** - -# Contributions +Types of branches: -- [klpanagi](https://github.com/klpanagi) +- Bug fix: `fix/` +- New feature: `feat/` +- Documentation: `doc/` +New versions will be released from the `master` branch after a PR request from `devel`. \ No newline at end of file From 7f808801633f55c22f796307eaaa84cd108d31c5 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Tue, 13 Feb 2024 22:12:44 +0200 Subject: [PATCH 05/16] remove mypy dependency from pytest --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b0e96f1..c1937a3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,4 +52,4 @@ exclude = docs ignore_missing_imports=true [tool:pytest] -addopts = --mypy +# addopts = --mypy From 93b634772bee189c56161419f6ccf0af9fa6ca2c Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Tue, 13 Feb 2024 22:21:12 +0200 Subject: [PATCH 06/16] Replace pydantic dict() to model_dump() in tests --- tests/test_msgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msgs.py b/tests/test_msgs.py index c30ad25..ded0597 100644 --- a/tests/test_msgs.py +++ b/tests/test_msgs.py @@ -41,7 +41,7 @@ class TestPubSubMessage(PubSubMessage): _msg = TestPubSubMessage() _msg.b = TestObject(c=2, d=3) - self.assertEqual(_msg.dict(), _d) + self.assertEqual(_msg.model_dump(), _d) def test_nested_message_from_dict(self): _d = {"a": 1, "b": {"c": 2, "d": 3}} From c3ce45d440eb61f55c9850c6311b39b431457ccc Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Tue, 13 Feb 2024 23:21:58 +0200 Subject: [PATCH 07/16] Adds linting action with pylint --- .github/workflows/pylint.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/pylint.yml diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 0000000..383e65c --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,23 @@ +name: Pylint + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Analysing the code with pylint + run: | + pylint $(git ls-files '*.py') From 978810bd4e3cbe7e723828085b56dd4deea1ed95 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 17:15:13 +0200 Subject: [PATCH 08/16] Pylint setup for CI --- .pylintrc | 636 +++++++++++++++++++++++++++++++++++++++++++ Makefile | 17 +- requirements-dev.txt | 5 +- setup.cfg | 5 +- 4 files changed, 657 insertions(+), 6 deletions(-) diff --git a/.pylintrc b/.pylintrc index e69de29..51c7cfa 100644 --- a/.pylintrc +++ b/.pylintrc @@ -0,0 +1,636 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=7 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.11 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException,builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + use-implicit-booleaness-not-comparison-to-string, + use-implicit-booleaness-not-comparison-to-zero, + missing-function-docstring, + missing-class-docstring + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are: text, parseable, colorized, +# json2 (improved json format), json (old json format) and msvs (visual +# studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/Makefile b/Makefile index ea1d6be..fccf987 100644 --- a/Makefile +++ b/Makefile @@ -63,11 +63,13 @@ lint: ## check style with flake8 test: ## run tests quickly with the default Python coverage run -m unittest discover -cov: test ## check code coverage quickly with the default Python +cov: + test ## check code coverage quickly with the default Python coverage report -m coverage xml -cov_html: test +cov_html: + test html diff: ## Calculate diff @@ -85,10 +87,17 @@ docs: ## generate Sphinx HTML documentation, including API docs servedocs: docs ## compile the docs watching for changes watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . -release: dist ## package and upload a release +_release: + dist ## package and upload a release twine upload dist/* -dist: clean ## builds source and wheel package +release: + poetry build + poetry update + poetry publish + +dist: + clean ## builds source and wheel package python setup.py sdist python setup.py bdist_wheel ls -l dist diff --git a/requirements-dev.txt b/requirements-dev.txt index beecaf6..147b7fc 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,7 @@ pip>=21.1 bump2version>=0.5.11 flake8>=3.7.8 -coverage>=7.0.1 \ No newline at end of file +coverage>=7.0.1 +pytest==8.0.0 +pylint==3.0.3 +pytest-pylint==0.21.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index c1937a3..88a69e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,4 +52,7 @@ exclude = docs ignore_missing_imports=true [tool:pytest] -# addopts = --mypy +# addopts = --pylint + +[pylint] +## defined in .pylintrc From 91e4d6463e0acaa1155c2fe3a40de51eb2b4f4a2 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 20:26:20 +0200 Subject: [PATCH 09/16] Refactor Coverage action --- .github/workflows/{main.yml => coverage.yml} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename .github/workflows/{main.yml => coverage.yml} (94%) diff --git a/.github/workflows/main.yml b/.github/workflows/coverage.yml similarity index 94% rename from .github/workflows/main.yml rename to .github/workflows/coverage.yml index 50f705f..eba27e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/coverage.yml @@ -1,11 +1,13 @@ # This workflow will install Python dependencies, run tests and lint with a single version of Python # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python -name: Python application +name: Coverage on: pull_request: branches: [ "master" ] + push: + branches: [ "devel" ] permissions: contents: read From 288fa8c8834cc5b80f2cd1c3820d8343a67c0a10 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 20:29:18 +0200 Subject: [PATCH 10/16] Fixes bug in Makefile --- Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index fccf987..4491ef5 100644 --- a/Makefile +++ b/Makefile @@ -63,13 +63,11 @@ lint: ## check style with flake8 test: ## run tests quickly with the default Python coverage run -m unittest discover -cov: - test ## check code coverage quickly with the default Python +cov: test ## check code coverage quickly with the default Python coverage report -m coverage xml -cov_html: - test +cov_html: test html diff: ## Calculate diff From 3901a2297be58cdd5369483640350174c2fd42aa Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 20:38:42 +0200 Subject: [PATCH 11/16] Adds Pytest action --- .github/pytest.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/pytest.yml diff --git a/.github/pytest.yml b/.github/pytest.yml new file mode 100644 index 0000000..74eb8d4 --- /dev/null +++ b/.github/pytest.yml @@ -0,0 +1,30 @@ +name: Pytest + +on: + push: + branches: [ "devel", "master" ] + pull_request: + branches: [ "devel", "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install . + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi + - name: Test with pytest + run: | + pytest From 55b5b8bc314cb93410bcb7ee780e36096beb6895 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 20:41:43 +0200 Subject: [PATCH 12/16] Move action yml in proper directory --- .github/{ => workflows}/pytest.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/pytest.yml (100%) diff --git a/.github/pytest.yml b/.github/workflows/pytest.yml similarity index 100% rename from .github/pytest.yml rename to .github/workflows/pytest.yml From 72ae746ed64947bacd9aadd85a68714239cd8e9a Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 21:02:07 +0200 Subject: [PATCH 13/16] Relax paho-mqtt dependency --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ed995e0..1186cc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,6 @@ pydantic>=2.0.0 ujson>=5.7.0 rich redis[hiredis] -paho-mqtt>=1.6.1,<2.0.0 +paho-mqtt<2.0.0 pika>=1.3.1 requests>=2.1.0 From 55acabc266f300b03548f356e1280ac99423b87d Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 15 Feb 2024 21:02:38 +0200 Subject: [PATCH 14/16] Adds conda environment.yml --- environment.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 environment.yml diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..22125b1 --- /dev/null +++ b/environment.yml @@ -0,0 +1,7 @@ +name: commlib-py +dependencies: + - python>=3.7 + - anaconda + - pip + - pip: + - -r requirements.txt From 66bdc4a2dabb9d5d99566a36c3a687097d967456 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 29 Feb 2024 00:32:20 +0200 Subject: [PATCH 15/16] Adds release action with poetry --- .github/workflows/release.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..533a46b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Release +on: + release: + types: + - created + +jobs: + publish: + strategy: + fail-fast: false + matrix: + python-version: [3.9] + poetry-version: [1.1.2] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Run image + uses: abatilo/actions-poetry@v2.0.0 + with: + poetry-version: ${{ matrix.poetry-version }} + - name: Publish + env: + PYPI_TOKEN: ${{ secrets.PYPI_COMMLIB_TOKEN }} + run: | + poetry config pypi-token.pypi $PYPI_TOKEN + poetry publish --build From 0d681a6ac05a7469bd0493cba288fb15d6cb7438 Mon Sep 17 00:00:00 2001 From: Konstantinos Panayiotou Date: Thu, 29 Feb 2024 00:34:00 +0200 Subject: [PATCH 16/16] Updates version --- commlib/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commlib/__init__.py b/commlib/__init__.py index 44a3ebd..551bb0e 100644 --- a/commlib/__init__.py +++ b/commlib/__init__.py @@ -2,4 +2,4 @@ __author__ = """Konstantinos Panayiotou""" __email__ = "klpanagi@gmail.com" -__version__ = "0.11.3" +__version__ = "0.11.4" diff --git a/pyproject.toml b/pyproject.toml index 17ddcbc..4a88544 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ optional-dependencies = {dev = { file = ["requirements-dev.txt"] }} [tool.poetry] name = "commlib-py" -version = "0.11.3" +version = "0.11.4" description = "Internal DSL for communication and messaging in CyberPhysical Systems" authors = ["Konstantinos Panayiotou "] readme = "README.md"