amqtt
Advanced tools
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||
| <!-- Creator: CorelDRAW --> | ||
| <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="195.122mm" height="44.2024mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" | ||
| viewBox="0 0 17484.57 3960.9" | ||
| xmlns:xlink="http://www.w3.org/1999/xlink" | ||
| xmlns:xodm="http://www.corel.com/coreldraw/odm/2003"> | ||
| <defs> | ||
| <style type="text/css"> | ||
| <![CDATA[ | ||
| .fil1 {fill:#D11149} | ||
| .fil0 {fill:#D11149;fill-rule:nonzero} | ||
| ]]> | ||
| </style> | ||
| </defs> | ||
| <g id="Layer_x0020_1"> | ||
| <metadata id="CorelCorpID_0Corel-Layer"/> | ||
| <path class="fil0" d="M15857.77 3758.63l0 -3088.45c0,-69.46 -56.65,-126.11 -126.11,-126.11l-857.7 0c-69.46,0 -126.11,-56.65 -126.11,-126.11l0 -215.69c0,-69.46 56.64,-126.11 126.11,-126.11l2484.5 0c69.46,0 126.11,56.65 126.11,126.11l0 215.69c0,69.46 -56.65,126.11 -126.11,126.11l-857.7 0c-69.46,0 -126.11,56.65 -126.11,126.11l0 3088.45c0,69.46 -56.64,126.11 -126.11,126.11l-264.67 0c-69.46,0 -126.11,-56.65 -126.11,-126.11z"/> | ||
| <path class="fil0" d="M12957.82 3758.63l0 -3088.45c0,-69.46 -56.64,-126.11 -126.11,-126.11l-857.7 0c-69.46,0 -126.11,-56.65 -126.11,-126.11l0 -215.69c0,-69.46 56.65,-126.11 126.11,-126.11l2484.5 0c69.46,0 126.11,56.65 126.11,126.11l0 215.69c0,69.46 -56.65,126.11 -126.11,126.11l-857.7 0c-69.46,0 -126.11,56.65 -126.11,126.11l0 3088.45c0,69.46 -56.65,126.11 -126.11,126.11l-264.66 0c-69.46,0 -126.11,-56.65 -126.11,-126.11z"/> | ||
| <path class="fil0" d="M9802.16 3960.9c-362.72,0 -685.53,-81.61 -968.46,-243.93 -282.92,-162.31 -505.09,-391.73 -666.5,-688.25 -161.41,-296.52 -242.12,-645.65 -242.12,-1048.26 0,-402.62 80.71,-752.65 242.12,-1049.16 161.41,-296.53 383.58,-525.95 666.5,-688.27 282.93,-162.31 605.74,-243.02 968.46,-243.02 362.73,0 685.55,80.71 968.47,243.02 282.92,162.33 505.09,391.74 666.5,688.27 161.4,296.51 242.11,646.54 242.11,1049.16 0,402.62 -80.71,751.74 -242.11,1048.26 -161.41,296.52 -383.58,525.95 -666.5,688.25 -282.92,162.33 -605.74,243.93 -968.47,243.93zm0 -500.55c292,0 535.93,-66.19 731.79,-197.68 195.87,-131.48 343.67,-309.22 442.51,-533.19 98.85,-223.98 147.81,-473.36 147.81,-749.03 0,-275.66 -48.96,-525.94 -147.81,-749.92 -98.84,-223.98 -246.65,-401.71 -442.51,-533.2 -195.87,-131.49 -439.8,-196.78 -731.79,-196.78 -291.99,0 -535.91,65.29 -731.79,196.78 -195.87,131.48 -342.77,309.21 -441.62,533.2 -98.84,223.98 -148.71,474.26 -148.71,749.92 0,275.66 49.88,525.04 148.71,749.03 98.85,223.97 245.75,401.7 441.62,533.19 195.87,131.49 439.8,197.68 731.79,197.68z"/> | ||
| <path class="fil0" d="M10195.2 2695.71l117.11 -119.29c48.66,-49.56 128.77,-50.3 178.33,-1.64l1017.01 998.52c49.57,48.67 50.29,128.77 1.63,178.34l-117.11 119.27c-48.65,49.56 -128.77,50.3 -178.32,1.64l-1017.02 -998.5c-49.57,-48.67 -50.3,-128.77 -1.63,-178.34z"/> | ||
| <path class="fil0" d="M3597.08 3741.84l477.57 -3556.34c8.46,-62.99 61.44,-109.33 124.99,-109.33l254.52 0c47.9,0 88.97,24.72 111.41,67.03l853.55 1609.69c22.48,42.39 63.7,67.13 111.69,67.02 48,-0.11 89.12,-25.02 111.41,-67.53l843.74 -1608.66c22.34,-42.6 63.58,-67.54 111.68,-67.54l259.42 0c63.6,0 116.63,46.43 125,109.5l472.49 3556.34c4.96,37.32 -5.33,71.38 -30.15,99.7 -24.8,28.33 -57.21,43.02 -94.86,43.02l-288.7 0c-63.99,0 -117.25,-47.04 -125.15,-110.55l-372.56 -2996.15 136.03 21.77 -1031.91 1970.44c-22.24,42.48 -63.27,67.41 -111.22,67.61 -47.95,0.19 -89.17,-24.43 -111.74,-66.73l-1052.04 -1971.32 136.02 -21.77 -372.55 2996.15c-7.89,63.51 -61.16,110.55 -125.15,110.55l-288.5 0c-37.69,0 -70.12,-14.73 -94.92,-43.08 -24.8,-28.36 -35.08,-62.46 -30.06,-99.81z"/> | ||
| <g id="_105553308281216"> | ||
| <path class="fil0" d="M1957.06 947c352.33,76.52 651.85,286.65 843.59,591.4 191.78,304.81 251.46,665.75 168.03,1016.44 -10.41,43.56 -1.77,87.36 20.16,122.22 21.94,34.86 56.06,60.32 98.27,70.42 85.73,20.44 172.16,-32.79 192.59,-118.52 103.37,-434.94 29.47,-882.64 -208.46,-1260.81 -237.88,-378.08 -609.7,-638.82 -1046.38,-733.6 -86.12,-18.68 -171.43,36.15 -190.2,122.32 -18.68,86.12 36.15,171.43 122.32,190.19l0.07 -0.07z"/> | ||
| <path class="fil0" d="M1765.12 2135.2c159.79,137.95 -47.11,377.6 -206.9,239.65 -159.79,-137.95 47.11,-377.6 206.9,-239.65z"/> | ||
| <path class="fil0" d="M324.48 2355.38c-29.95,-359.29 83.33,-707.2 318.62,-979.73 235.34,-272.59 563.02,-435.29 922.76,-458.15 44.71,-2.79 84.05,-23.88 110.97,-55.06 26.91,-31.17 41.27,-71.26 38.58,-114.57 -5.55,-87.96 -81.74,-155.02 -169.69,-149.48 -446.15,28.44 -852.62,230.13 -1144.6,568.32 -291.91,338.12 -432.4,769.97 -395.24,1215.27 7.34,87.82 84.73,153.35 172.63,146.07 87.82,-7.34 153.35,-84.73 146.07,-172.63l-0.09 -0.04z"/> | ||
| <path class="fil0" d="M2199.55 3483.38c-329.07,147.33 -694.85,155.77 -1029.96,24.1 -335.18,-131.69 -597.28,-386.93 -738.08,-718.77 -17.45,-41.25 -50.39,-71.39 -88.72,-86.45 -38.33,-15.06 -80.91,-15.32 -120.89,1.57 -81.14,34.39 -119.17,128.51 -84.78,209.65 174.7,411.51 499.7,728.16 915.55,891.55 415.76,163.35 869.76,152.77 1277.57,-29.87 80.42,-36.03 116.6,-130.77 80.6,-211.29 -36.03,-80.42 -130.77,-116.59 -211.29,-80.6l-0.01 0.11z"/> | ||
| <path class="fil0" d="M2891 1719.29c146.75,329.33 154.53,695.12 22.27,1030 -132.29,334.95 -387.99,596.59 -720.07,736.8 -41.28,17.38 -71.48,50.26 -86.61,88.57 -15.13,38.3 -15.47,80.88 1.35,120.89 34.25,81.2 128.29,119.39 209.5,85.15 411.82,-173.97 729.04,-498.41 893.17,-913.97 164.09,-415.47 154.32,-869.49 -27.59,-1277.62 -35.89,-80.49 -130.56,-116.83 -211.14,-80.98 -80.49,35.89 -116.83,130.56 -80.98,211.14l0.11 0.01z"/> | ||
| <path class="fil0" d="M1750.82 1872.33c25.7,-29.77 40.54,-68.92 38.62,-111.19 -3.77,-88.23 -78.19,-156.6 -166.57,-153.09 -175.23,7.18 -341.42,87.66 -455.97,220.35 -114.55,132.68 -169.66,308.52 -151.46,483.24 9.14,87.82 87.99,151.52 175.76,142.45 87.82,-9.13 151.52,-87.99 142.45,-175.75 -9.23,-88.17 17.36,-173.79 75.27,-240.86 57.9,-67.07 138.5,-106.06 227.29,-109.6 45.89,-1.77 86.41,-22.88 114.47,-55.38l0.15 -0.17z"/> | ||
| <path class="fil0" d="M1714.29 1378.19c26.15,-30.29 41.11,-70.61 38.58,-113.69 -5.14,-88.12 -80.75,-155.58 -168.87,-150.43 -308.84,17.67 -590.11,156.49 -791.96,390.29 -201.85,233.81 -298.15,532.33 -270.57,840.43 7.76,87.94 85.52,152.87 173.45,145.12 87.93,-7.77 152.87,-85.53 145.11,-173.46 -19.88,-221.14 49.25,-435.33 194.02,-603.03 144.84,-167.76 346.8,-267.13 568.27,-279.92 44.99,-2.54 84.63,-23.64 111.8,-55.12l0.17 -0.2z"/> | ||
| </g> | ||
| <path class="fil1" d="M3181.77 632.27l-3.25 0c-83.23,0 -151.32,68.1 -151.32,151.32l0 2966c0,83.22 68.09,151.31 151.32,151.31l3.25 0c83.23,0 151.32,-68.09 151.32,-151.31l0 -2966c0,-83.22 -68.09,-151.32 -151.32,-151.32z"/> | ||
| </g> | ||
| </svg> |
+1
-0
@@ -6,2 +6,3 @@ #------- Package & Cache Files ------- | ||
| .vite | ||
| *.pem | ||
@@ -8,0 +9,0 @@ #------- Environment Files ------- |
| """INIT.""" | ||
| __version__ = "0.11.0" | ||
| __version__ = "0.11.1" |
+20
-13
@@ -459,2 +459,4 @@ import asyncio | ||
| connection_timeout = self.config.get("connection_timeout", None) | ||
| if secure: | ||
@@ -467,6 +469,8 @@ sc = ssl.create_default_context( | ||
| ) | ||
| if "certfile" in self.config and "keyfile" in self.config: | ||
| sc.load_cert_chain(self.config["certfile"], self.config["keyfile"]) | ||
| if "certfile" in self.config: | ||
| sc.load_verify_locations(cafile=self.config["certfile"]) | ||
| if "check_hostname" in self.config and isinstance(self.config["check_hostname"], bool): | ||
| sc.check_hostname = self.config["check_hostname"] | ||
| sc.verify_mode = ssl.CERT_REQUIRED | ||
| kwargs["ssl"] = sc | ||
@@ -481,7 +485,8 @@ | ||
| if scheme in ("mqtt", "mqtts"): | ||
| conn_reader, conn_writer = await asyncio.open_connection( | ||
| self.session.remote_address, | ||
| self.session.remote_port, | ||
| **kwargs, | ||
| ) | ||
| conn_reader, conn_writer = await asyncio.wait_for( | ||
| asyncio.open_connection( | ||
| self.session.remote_address, | ||
| self.session.remote_port, | ||
| **kwargs, | ||
| ), timeout=connection_timeout) | ||
@@ -491,8 +496,10 @@ reader = StreamReaderAdapter(conn_reader) | ||
| elif scheme in ("ws", "wss") and self.session.broker_uri: | ||
| websocket: ClientConnection = await websockets.connect( | ||
| self.session.broker_uri, | ||
| subprotocols=[websockets.Subprotocol("mqtt")], | ||
| additional_headers=self.additional_headers, | ||
| **kwargs, | ||
| ) | ||
| websocket: ClientConnection = await asyncio.wait_for( | ||
| websockets.connect( | ||
| self.session.broker_uri, | ||
| subprotocols=[websockets.Subprotocol("mqtt")], | ||
| additional_headers=self.additional_headers, | ||
| **kwargs, | ||
| ), timeout=connection_timeout) | ||
| reader = WebSocketsReader(websocket) | ||
@@ -499,0 +506,0 @@ writer = WebSocketsWriter(websocket) |
| import asyncio | ||
| from decimal import ROUND_HALF_UP, Decimal | ||
| from struct import pack, unpack | ||
@@ -142,1 +143,8 @@ | ||
| return str(value).encode("utf-8") | ||
| def float_to_bytes_str(value: float, places:int=3) -> bytes: | ||
| """Convert an float value to a bytes array containing the numeric character.""" | ||
| quant = Decimal(f"0.{''.join(['0' for i in range(places-1)])}1") | ||
| rounded = Decimal(value).quantize(quant, rounding=ROUND_HALF_UP) | ||
| return str(rounded).encode("utf-8") |
@@ -156,3 +156,3 @@ import asyncio | ||
| self.logger.debug("Writer close was cancelled.") | ||
| except TimeoutError: | ||
| except asyncio.TimeoutError: | ||
| self.logger.debug("Writer close operation timed out.", exc_info=True) | ||
@@ -325,3 +325,3 @@ except OSError: | ||
| app_message.puback_packet = await asyncio.wait_for(waiter, timeout=5) | ||
| except TimeoutError: | ||
| except asyncio.TimeoutError: | ||
| msg = f"Timeout waiting for PUBACK for packet ID {app_message.packet_id}" | ||
@@ -533,3 +533,3 @@ self.logger.warning(msg) | ||
| break | ||
| except TimeoutError: | ||
| except asyncio.TimeoutError: | ||
| self.logger.debug(f"{self.session.client_id} Input stream read timeout") | ||
@@ -536,0 +536,0 @@ self.handle_read_timeout() |
| from pathlib import Path | ||
| from typing import Any | ||
@@ -7,4 +6,3 @@ from passlib.apps import custom_app_context as pwd_context | ||
| from amqtt.broker import BrokerContext | ||
| from amqtt.plugins.base import BasePlugin | ||
| from amqtt.plugins.manager import BaseContext | ||
| from amqtt.plugins.base import BaseAuthPlugin | ||
| from amqtt.session import Session | ||
@@ -15,30 +13,2 @@ | ||
| class BaseAuthPlugin(BasePlugin[BaseContext]): | ||
| """Base class for authentication plugins.""" | ||
| def __init__(self, context: BaseContext) -> None: | ||
| super().__init__(context) | ||
| self.auth_config: dict[str, Any] | None = self._get_config_section("auth") | ||
| if not self.auth_config: | ||
| self.context.logger.warning("'auth' section not found in context configuration") | ||
| async def authenticate(self, *, session: Session) -> bool | None: | ||
| """Logic for session authentication. | ||
| Args: | ||
| session: amqtt.session.Session | ||
| Returns: | ||
| - `True` if user is authentication succeed, `False` if user authentication fails | ||
| - `None` if authentication can't be achieved (then plugin result is then ignored) | ||
| """ | ||
| if not self.auth_config: | ||
| # auth config section not found | ||
| self.context.logger.warning("'auth' section not found in context configuration") | ||
| return False | ||
| return True | ||
| class AnonymousAuthPlugin(BaseAuthPlugin): | ||
@@ -45,0 +15,0 @@ """Authentication plugin allowing anonymous access.""" |
| from typing import Any, Generic, TypeVar | ||
| from amqtt.broker import Action | ||
| from amqtt.plugins.manager import BaseContext | ||
| from amqtt.session import Session | ||
@@ -27,1 +29,60 @@ C = TypeVar("C", bound=BaseContext) | ||
| """Override if plugin needs to clean up resources upon shutdown.""" | ||
| class BaseTopicPlugin(BasePlugin[BaseContext]): | ||
| """Base class for topic plugins.""" | ||
| def __init__(self, context: BaseContext) -> None: | ||
| super().__init__(context) | ||
| self.topic_config: dict[str, Any] | None = self._get_config_section("topic-check") | ||
| if self.topic_config is None: | ||
| self.context.logger.warning("'topic-check' section not found in context configuration") | ||
| async def topic_filtering( | ||
| self, *, session: Session | None = None, topic: str | None = None, action: Action | None = None | ||
| ) -> bool: | ||
| """Logic for filtering out topics. | ||
| Args: | ||
| session: amqtt.session.Session | ||
| topic: str | ||
| action: amqtt.broker.Action | ||
| Returns: | ||
| bool: `True` if topic is allowed, `False` otherwise | ||
| """ | ||
| if not self.topic_config: | ||
| # auth config section not found | ||
| self.context.logger.warning("'topic-check' section not found in context configuration") | ||
| return False | ||
| return True | ||
| class BaseAuthPlugin(BasePlugin[BaseContext]): | ||
| """Base class for authentication plugins.""" | ||
| def __init__(self, context: BaseContext) -> None: | ||
| super().__init__(context) | ||
| self.auth_config: dict[str, Any] | None = self._get_config_section("auth") | ||
| if not self.auth_config: | ||
| self.context.logger.warning("'auth' section not found in context configuration") | ||
| async def authenticate(self, *, session: Session) -> bool | None: | ||
| """Logic for session authentication. | ||
| Args: | ||
| session: amqtt.session.Session | ||
| Returns: | ||
| - `True` if user is authentication succeed, `False` if user authentication fails | ||
| - `None` if authentication can't be achieved (then plugin result is then ignored) | ||
| """ | ||
| if not self.auth_config: | ||
| # auth config section not found | ||
| self.context.logger.warning("'auth' section not found in context configuration") | ||
| return False | ||
| return True |
@@ -21,6 +21,5 @@ __all__ = ["BaseContext", "PluginManager", "get_plugin_manager"] | ||
| from amqtt.broker import Action | ||
| from amqtt.plugins.authentication import BaseAuthPlugin | ||
| from amqtt.plugins.base import BasePlugin | ||
| from amqtt.plugins.topic_checking import BaseTopicPlugin | ||
| from amqtt.plugins.base import BaseAuthPlugin, BasePlugin, BaseTopicPlugin | ||
| class Plugin(NamedTuple): | ||
@@ -91,4 +90,4 @@ name: str | ||
| auth_filter_list = self.app_context.config["auth"].get("plugins", []) | ||
| if self.app_context.config and "topic" in self.app_context.config: | ||
| topic_filter_list = self.app_context.config["topic"].get("plugins", []) | ||
| if self.app_context.config and "topic-check" in self.app_context.config: | ||
| topic_filter_list = self.app_context.config["topic-check"].get("plugins", []) | ||
@@ -95,0 +94,0 @@ ep: EntryPoints | list[EntryPoint] = [] |
| import asyncio | ||
| from collections import deque # pylint: disable=C0412 | ||
| from typing import SupportsIndex, SupportsInt, TypeAlias # pylint: disable=C0412 | ||
| from typing import Any, SupportsIndex, SupportsInt, TypeAlias # pylint: disable=C0412 | ||
| import psutil | ||
| from amqtt.plugins.base import BasePlugin | ||
@@ -29,3 +31,3 @@ from amqtt.session import Session | ||
| from amqtt.broker import BrokerContext | ||
| from amqtt.codecs_amqtt import int_to_bytes_str | ||
| from amqtt.codecs_amqtt import float_to_bytes_str, int_to_bytes_str | ||
| from amqtt.mqtt.packet import PUBLISH, MQTTFixedHeader, MQTTPacket, MQTTPayload, MQTTVariableHeader | ||
@@ -44,2 +46,5 @@ | ||
| STAT_CLIENTS_DISCONNECTED = "clients_disconnected" | ||
| MEMORY_USAGE_MAXIMUM = "memory_maximum" | ||
| CPU_USAGE_MAXIMUM = "cpu_usage_maximum" | ||
| CPU_USAGE_LAST = "cpu_usage_last" | ||
@@ -50,2 +55,16 @@ | ||
| def val_to_bytes_str(value: Any) -> bytes: | ||
| """Convert an int, float or string to byte string.""" | ||
| match value: | ||
| case int(): | ||
| return int_to_bytes_str(value) | ||
| case float(): | ||
| return float_to_bytes_str(value) | ||
| case str(): | ||
| return value.encode("utf-8") | ||
| case _: | ||
| msg = f"Unsupported type {type(value)}" | ||
| raise NotImplementedError(msg) | ||
| class BrokerSysPlugin(BasePlugin[BrokerContext]): | ||
@@ -57,2 +76,3 @@ def __init__(self, context: BrokerContext) -> None: | ||
| self._sys_handle: asyncio.Handle | None = None | ||
| self._current_process = psutil.Process() | ||
@@ -71,2 +91,4 @@ def _clear_stats(self) -> None: | ||
| STAT_PUBLISH_SENT, | ||
| MEMORY_USAGE_MAXIMUM, | ||
| CPU_USAGE_MAXIMUM | ||
| ): | ||
@@ -135,3 +157,11 @@ self._stats[stat] = 0 | ||
| subscriptions_count = sum(len(sub) for sub in self.context.subscriptions.values()) | ||
| self._stats[STAT_CLIENTS_MAXIMUM] = client_connected | ||
| cpu_usage = self._current_process.cpu_percent(interval=0) | ||
| self._stats[CPU_USAGE_MAXIMUM] = max(self._stats[CPU_USAGE_MAXIMUM], cpu_usage) | ||
| mem_info_usage = self._current_process.memory_full_info() | ||
| mem_size = mem_info_usage.rss / (1024 ** 2) | ||
| self._stats[MEMORY_USAGE_MAXIMUM] = max(self._stats[MEMORY_USAGE_MAXIMUM], mem_size) | ||
| # Broadcast updates | ||
@@ -159,5 +189,9 @@ tasks: deque[asyncio.Task[None]] = deque() | ||
| "messages/subscriptions/count": subscriptions_count, | ||
| "heap/size": mem_size, | ||
| "heap/maximum": self._stats[MEMORY_USAGE_MAXIMUM], | ||
| "cpu/percent": cpu_usage, | ||
| "cpu/maximum": self._stats[CPU_USAGE_MAXIMUM], | ||
| } | ||
| for stat_name, stat_value in stats.items(): | ||
| data: bytes = int_to_bytes_str(stat_value) if isinstance(stat_value, int) else stat_value.encode("utf-8") | ||
| data: bytes = val_to_bytes_str(stat_value) | ||
| tasks.append(self.schedule_broadcast_sys_topic(stat_name, data)) | ||
@@ -164,0 +198,0 @@ |
| from typing import Any | ||
| from amqtt.broker import Action | ||
| from amqtt.plugins.base import BasePlugin | ||
| from amqtt.plugins.base import BaseTopicPlugin | ||
| from amqtt.plugins.manager import BaseContext | ||
@@ -9,33 +9,2 @@ from amqtt.session import Session | ||
| class BaseTopicPlugin(BasePlugin[BaseContext]): | ||
| """Base class for topic plugins.""" | ||
| def __init__(self, context: BaseContext) -> None: | ||
| super().__init__(context) | ||
| self.topic_config: dict[str, Any] | None = self._get_config_section("topic-check") | ||
| if self.topic_config is None: | ||
| self.context.logger.warning("'topic-check' section not found in context configuration") | ||
| async def topic_filtering( | ||
| self, *, session: Session | None = None, topic: str | None = None, action: Action | None = None | ||
| ) -> bool: | ||
| """Logic for filtering out topics. | ||
| Args: | ||
| session: amqtt.session.Session | ||
| topic: str | ||
| action: amqtt.broker.Action | ||
| Returns: | ||
| bool: `True` if topic is allowed, `False` otherwise | ||
| """ | ||
| if not self.topic_config: | ||
| # auth config section not found | ||
| self.context.logger.warning("'topic-check' section not found in context configuration") | ||
| return False | ||
| return True | ||
| class TopicTabooPlugin(BaseTopicPlugin): | ||
@@ -42,0 +11,0 @@ def __init__(self, context: BaseContext) -> None: |
+11
-9
| Metadata-Version: 2.4 | ||
| Name: amqtt | ||
| Version: 0.11.0 | ||
| Version: 0.11.1 | ||
| Summary: Python's asyncio-native MQTT broker and client. | ||
@@ -21,2 +21,3 @@ Author: aMQTT Contributors | ||
| Requires-Dist: passlib==1.7.4 | ||
| Requires-Dist: psutil>=7.0.0 | ||
| Requires-Dist: pyyaml==6.0.2 | ||
@@ -33,4 +34,4 @@ Requires-Dist: transitions==0.9.2 | ||
| [](https://github.com/Yakifo/amqtt/actions/workflows/codeql-analysis.yml) | ||
| [](https://amqtt.readthedocs.io/en/latest/) | ||
| [](https://discord.gg/S3sP6dDaF3) | ||
| [](https://amqtt.readthedocs.io/) | ||
| [](https://discord.gg/S3sP6dDaF3) | ||
|  | ||
@@ -40,3 +41,3 @@  | ||
|  | ||
|  | ||
@@ -84,11 +85,12 @@ `aMQTT` is an open source [MQTT](http://www.mqtt.org) broker and client[^1], natively implemented with Python's [asyncio](https://docs.python.org/3/library/asyncio.html). | ||
| | Version | hbmqtt compatibility | Supported Python Versions | PyPi Release | | ||
| | ------- | -------------------- | ------------------------- | ------------ | | ||
| | 0.10.x | yes [^2] | 3.7 - 3.9 | 0.10.1 | | ||
| | 0.11.x | no [^3] | 3.10 - 3.13 | 0.11.0 | | ||
| | Version | hbmqtt compatibility | Supported Python Versions | | ||
| | ------- | -------------------- | ------------------------- | | ||
| | 0.10.x | yes [^2] | 3.7 - 3.9 | | ||
| | 0.11.x | no [^3] | 3.10 - 3.13 | | ||
| For a full feature roadmap, see ... | ||
| [^1]: Forked from [HBMQTT](https://github.com/beerfactory/hbmqtt) after it was deprecated by the original author. | ||
| [^2]: drop-in replacement | ||
| [^3]: module renamed and small API differences |
+13
-2
@@ -23,3 +23,3 @@ [build-system] | ||
| version = "0.11.0" | ||
| version = "0.11.1" | ||
| requires-python = ">=3.10.0" | ||
@@ -37,2 +37,3 @@ readme = "README.md" | ||
| "typer==0.15.4", | ||
| "psutil>=7.0.0", | ||
| ] | ||
@@ -91,4 +92,14 @@ | ||
| [tool.hatch.build] | ||
| exclude = [ | ||
| ".venv*", | ||
| "tests/", | ||
| "**/tests/", | ||
| "**/*.pyc", | ||
| "**/__pycache__/", | ||
| "**/README.md", | ||
| ] | ||
| [tool.hatch.build.targets.sdist] | ||
| include = ["/amqtt", "README.md"] | ||
| include = ["/amqtt", "README.md", "docs/assets/amqtt.svg"] | ||
@@ -95,0 +106,0 @@ [tool.hatch.version] |
+9
-8
| [](https://amqtt.readthedocs.io/en/latest/) | ||
| [](https://github.com/Yakifo/amqtt/actions/workflows/ci.yml) | ||
| [](https://github.com/Yakifo/amqtt/actions/workflows/codeql-analysis.yml) | ||
| [](https://amqtt.readthedocs.io/en/latest/) | ||
| [](https://discord.gg/S3sP6dDaF3) | ||
| [](https://amqtt.readthedocs.io/) | ||
| [](https://discord.gg/S3sP6dDaF3) | ||
|  | ||
@@ -10,3 +10,3 @@  | ||
|  | ||
|  | ||
@@ -54,11 +54,12 @@ `aMQTT` is an open source [MQTT](http://www.mqtt.org) broker and client[^1], natively implemented with Python's [asyncio](https://docs.python.org/3/library/asyncio.html). | ||
| | Version | hbmqtt compatibility | Supported Python Versions | PyPi Release | | ||
| | ------- | -------------------- | ------------------------- | ------------ | | ||
| | 0.10.x | yes [^2] | 3.7 - 3.9 | 0.10.1 | | ||
| | 0.11.x | no [^3] | 3.10 - 3.13 | 0.11.0 | | ||
| | Version | hbmqtt compatibility | Supported Python Versions | | ||
| | ------- | -------------------- | ------------------------- | | ||
| | 0.10.x | yes [^2] | 3.7 - 3.9 | | ||
| | 0.11.x | no [^3] | 3.10 - 3.13 | | ||
| For a full feature roadmap, see ... | ||
| [^1]: Forked from [HBMQTT](https://github.com/beerfactory/hbmqtt) after it was deprecated by the original author. | ||
| [^2]: drop-in replacement | ||
| [^3]: module renamed and small API differences |
| # markdown-it-container | ||
| [](https://travis-ci.org/markdown-it/markdown-it-container) | ||
| [](https://www.npmjs.org/package/markdown-it-container) | ||
| [](https://coveralls.io/r/markdown-it/markdown-it-container?branch=master) | ||
| > Plugin for creating block-level custom containers for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser. | ||
| __v2.+ requires `markdown-it` v5.+, see changelog.__ | ||
| With this plugin you can create block containers like: | ||
| ``` | ||
| ::: warning | ||
| *here be dragons* | ||
| ::: | ||
| ``` | ||
| .... and specify how they should be rendered. If no renderer defined, `<div>` with | ||
| container name class will be created: | ||
| ```html | ||
| <div class="warning"> | ||
| <em>here be dragons</em> | ||
| </div> | ||
| ``` | ||
| Markup is the same as for [fenced code blocks](http://spec.commonmark.org/0.18/#fenced-code-blocks). | ||
| Difference is, that marker use another character and content is rendered as markdown markup. | ||
| ## Installation | ||
| node.js, browser: | ||
| ```bash | ||
| $ npm install markdown-it-container --save | ||
| $ bower install markdown-it-container --save | ||
| ``` | ||
| ## API | ||
| ```js | ||
| var md = require('markdown-it')() | ||
| .use(require('markdown-it-container'), name [, options]); | ||
| ``` | ||
| Params: | ||
| - __name__ - container name (mandatory) | ||
| - __options:__ | ||
| - __validate__ - optional, function to validate tail after opening marker, should | ||
| return `true` on success. | ||
| - __render__ - optional, renderer function for opening/closing tokens. | ||
| - __marker__ - optional (`:`), character to use in delimiter. | ||
| ## Example | ||
| ```js | ||
| var md = require('markdown-it')(); | ||
| md.use(require('markdown-it-container'), 'spoiler', { | ||
| validate: function(params) { | ||
| return params.trim().match(/^spoiler\s+(.*)$/); | ||
| }, | ||
| render: function (tokens, idx) { | ||
| var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/); | ||
| if (tokens[idx].nesting === 1) { | ||
| // opening tag | ||
| return '<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n'; | ||
| } else { | ||
| // closing tag | ||
| return '</details>\n'; | ||
| } | ||
| } | ||
| }); | ||
| console.log(md.render('::: spoiler click me\n*content*\n:::\n')); | ||
| // Output: | ||
| // | ||
| // <details><summary>click me</summary> | ||
| // <p><em>content</em></p> | ||
| // </details> | ||
| ``` | ||
| ## License | ||
| [MIT](https://github.com/markdown-it/markdown-it-container/blob/master/LICENSE) |
| # markdown-it-deflist | ||
| [](https://travis-ci.org/markdown-it/markdown-it-deflist) | ||
| [](https://www.npmjs.org/package/markdown-it-deflist) | ||
| [](https://coveralls.io/r/markdown-it/markdown-it-deflist?branch=master) | ||
| > Definition list (`<dl>`) tag plugin for [markdown-it](https://github.com/markdown-it/markdown-it) markdown parser. | ||
| __v2.+ requires `markdown-it` v5.+, see changelog.__ | ||
| Syntax is based on [pandoc definition lists](http://johnmacfarlane.net/pandoc/README.html#definition-lists). | ||
| ## Install | ||
| node.js, browser: | ||
| ```bash | ||
| npm install markdown-it-deflist --save | ||
| bower install markdown-it-deflist --save | ||
| ``` | ||
| ## Use | ||
| ```js | ||
| var md = require('markdown-it')() | ||
| .use(require('markdown-it-deflist')); | ||
| md.render(/*...*/); | ||
| ``` | ||
| _Differences in browser._ If you load script directly into the page, without | ||
| package system, module will add itself globally as `window.markdownitDeflist`. | ||
| ## License | ||
| [MIT](https://github.com/markdown-it/markdown-it-deflist/blob/master/LICENSE) |
| [](https://github.com/goessner/markdown-it-texmath/blob/master/licence.txt) | ||
| [](https://www.npmjs.com/package/markdown-it-texmath) | ||
| [](https://www.npmjs.com/package/markdown-it-texmath) | ||
| # markdown-it-texmath | ||
| Add TeX math equations to your Markdown documents rendered by [markdown-it](https://github.com/markdown-it/markdown-it) parser. [KaTeX](https://github.com/Khan/KaTeX) is used as a fast math renderer. | ||
| ## Features | ||
| Simplify the process of authoring markdown documents containing math formulas. | ||
| This extension is a comfortable tool for scientists, engineers and students with markdown as their first choice document format. | ||
| * Macro support | ||
| * Simple formula numbering | ||
| * Inline math with tables, lists and blockquote. | ||
| * User setting delimiters: | ||
| * `'dollars'` (default) | ||
| * inline: `$...$` | ||
| * display: `$$...$$` | ||
| * display + equation number: `$$...$$ (1)` | ||
| * `'brackets'` | ||
| * inline: `\(...\)` | ||
| * display: `\[...\]` | ||
| * display + equation number: `\[...\] (1)` | ||
| * `'gitlab'` | ||
| * inline: ``$`...`$`` | ||
| * display: `` ```math ... ``` `` | ||
| * display + equation number: `` ```math ... ``` (1)`` | ||
| * `'julia'` | ||
| * inline: `$...$` or ``` ``...`` ``` | ||
| * display: `` ```math ... ``` `` | ||
| * display + equation number: `` ```math ... ``` (1)`` | ||
| * `'kramdown'` | ||
| * inline: ``$$...$$`` | ||
| * display: `$$...$$` | ||
| * display + equation number: `$$...$$ (1)` | ||
| ## Show me | ||
| View a [test table](https://goessner.github.io/markdown-it-texmath/index.html). | ||
| [try it out ...](https://goessner.github.io/markdown-it-texmath/markdown-it-texmath-demo.html) | ||
| ## Use with `node.js` | ||
| Install the extension. Verify having `markdown-it` and `katex` already installed . | ||
| ``` | ||
| npm install markdown-it-texmath | ||
| ``` | ||
| Use it with JavaScript. | ||
| ```js | ||
| let kt = require('katex'), | ||
| tm = require('markdown-it-texmath').use(kt), | ||
| md = require('markdown-it')().use(tm,{delimiters:'dollars',macros:{"\\RR": "\\mathbb{R}"}}); | ||
| md.render('Euler\'s identity \(e^{i\pi}+1=0\) is a beautiful formula in $\\RR 2$.') | ||
| ``` | ||
| ## Use in Browser | ||
| ```html | ||
| <html> | ||
| <head> | ||
| <meta charset='utf-8'> | ||
| <link rel="stylesheet" href="katex.min.css"> | ||
| <link rel="stylesheet" href="texmath.css"> | ||
| <script src="markdown-it.min.js"></script> | ||
| <script src="katex.min.js"></script> | ||
| <script src="texmath.js"></script> | ||
| </head> | ||
| <body> | ||
| <div id="out"></div> | ||
| <script> | ||
| let md; | ||
| document.addEventListener("DOMContentLoaded", () => { | ||
| const tm = texmath.use(katex); | ||
| md = markdownit().use(tm,{delimiters:'dollars',macros:{"\\RR": "\\mathbb{R}"}}); | ||
| out.innerHTML = md.render('Euler\'s identity $e^{i\pi}+1=0$ is a beautiful formula in //RR 2.'); | ||
| }) | ||
| </script> | ||
| </body> | ||
| </html> | ||
| ``` | ||
| ## CDN | ||
| Use following links for `texmath.js` and `texmath.css` | ||
| * `https://gitcdn.xyz/cdn/goessner/markdown-it-texmath/master/texmath.js` | ||
| * `https://gitcdn.xyz/cdn/goessner/markdown-it-texmath/master/texmath.css` | ||
| ## Dependencies | ||
| * [`markdown-it`](https://github.com/markdown-it/markdown-it): Markdown parser done right. Fast and easy to extend. | ||
| * [`katex`](https://github.com/Khan/KaTeX): This is where credits for fast rendering TeX math in HTML go to. | ||
| ## ToDo | ||
| nothing yet | ||
| ## FAQ | ||
| * __`markdown-it-texmath` with React Native does not work, why ?__ | ||
| * `markdown-it-texmath` is using regular expressions with `y` [(sticky) property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky) and cannot avoid this. The use of the `y` flag in regular expressions means the plugin is not compatible with React Native (which as of now doesn't support it and throws an error `Invalid flags supplied to RegExp constructor`). | ||
| ## CHANGELOG | ||
| ### [0.6.0] on October 04, 2019 | ||
| * Add support for [Julia Markdown](https://docs.julialang.org/en/v1/stdlib/Markdown/) on [request](https://github.com/goessner/markdown-it-texmath/issues/15). | ||
| ### [0.5.5] on February 07, 2019 | ||
| * Remove [rendering bug with brackets delimiters](https://github.com/goessner/markdown-it-texmath/issues/9). | ||
| ### [0.5.4] on January 20, 2019 | ||
| * Remove pathological [bug within blockquotes](https://github.com/goessner/mdmath/issues/50). | ||
| ### [0.5.3] on November 11, 2018 | ||
| * Add support for Tex macros (https://katex.org/docs/supported.html#macros) . | ||
| * Bug with [brackets delimiters](https://github.com/goessner/markdown-it-texmath/issues/9) . | ||
| ### [0.5.2] on September 07, 2018 | ||
| * Add support for [Kramdown](https://kramdown.gettalong.org/) . | ||
| ### [0.5.0] on August 15, 2018 | ||
| * Fatal blockquote bug investigated. Implemented workaround to vscode bug, which has finally gone with vscode 1.26.0 . | ||
| ### [0.4.6] on January 05, 2018 | ||
| * Escaped underscore bug removed. | ||
| ### [0.4.5] on November 06, 2017 | ||
| * Backslash bug removed. | ||
| ### [0.4.4] on September 27, 2017 | ||
| * Modifying the `block` mode regular expression with `gitlab` delimiters, so removing the `newline` bug. | ||
| ## License | ||
| `markdown-it-texmath` is licensed under the [MIT License](./license.txt) | ||
| © [Stefan Gössner](https://github.com/goessner) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
5589
0.67%287899
-0.14%52
-3.7%