You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

amqtt

Package Overview
Dependencies
Maintainers
2
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

amqtt - pypi Package Compare versions

Comparing version
0.11.0
to
0.11.1
+34
docs/assets/amqtt.svg
<?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 -------

+1
-1
"""INIT."""
__version__ = "0.11.0"
__version__ = "0.11.1"

@@ -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:

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

[![CodeQL](https://github.com/Yakifo/amqtt/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Yakifo/amqtt/actions/workflows/codeql-analysis.yml)
[![Documentation Status](https://img.shields.io/readthedocs/amqtt?style=plastic&logo=readthedocs)](https://amqtt.readthedocs.io/en/latest/)
[![](https://dcbadge.limes.pink/api/server/https://discord.gg/S3sP6dDaF3?style=plastic)](https://discord.gg/S3sP6dDaF3)
[![Read the Docs](https://img.shields.io/readthedocs/amqtt/v0.11.0?style=plastic&logo=readthedocs)](https://amqtt.readthedocs.io/)
[![Discord](https://dcbadge.limes.pink/api/server/https://discord.gg/S3sP6dDaF3?style=plastic)](https://discord.gg/S3sP6dDaF3)
![Python Version](https://img.shields.io/pypi/pyversions/amqtt?style=plastic&logo=python&logoColor=yellow)

@@ -40,3 +41,3 @@ ![Python Wheel](https://img.shields.io/pypi/wheel/amqtt?style=plastic)

![docs/assets/amqtt.svg](https://github.com/Yakifo/amqtt/blob/v0.11.0/docs/assets/amqtt.svg)
![docs/assets/amqtt.svg](https://raw.githubusercontent.com/Yakifo/amqtt/refs/tags/v0.11.0/docs/assets/amqtt.svg)

@@ -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

@@ -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]

[![MIT licensed](https://img.shields.io/github/license/Yakifo/amqtt?style=plastic)](https://amqtt.readthedocs.io/en/latest/)
[![CI](https://github.com/Yakifo/amqtt/actions/workflows/ci.yml/badge.svg?branch=rc)](https://github.com/Yakifo/amqtt/actions/workflows/ci.yml)
[![CodeQL](https://github.com/Yakifo/amqtt/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Yakifo/amqtt/actions/workflows/codeql-analysis.yml)
[![Documentation Status](https://img.shields.io/readthedocs/amqtt?style=plastic&logo=readthedocs)](https://amqtt.readthedocs.io/en/latest/)
[![](https://dcbadge.limes.pink/api/server/https://discord.gg/S3sP6dDaF3?style=plastic)](https://discord.gg/S3sP6dDaF3)
[![Read the Docs](https://img.shields.io/readthedocs/amqtt/v0.11.0?style=plastic&logo=readthedocs)](https://amqtt.readthedocs.io/)
[![Discord](https://dcbadge.limes.pink/api/server/https://discord.gg/S3sP6dDaF3?style=plastic)](https://discord.gg/S3sP6dDaF3)
![Python Version](https://img.shields.io/pypi/pyversions/amqtt?style=plastic&logo=python&logoColor=yellow)

@@ -10,3 +10,3 @@ ![Python Wheel](https://img.shields.io/pypi/wheel/amqtt?style=plastic)

![docs/assets/amqtt.svg](https://github.com/Yakifo/amqtt/blob/v0.11.0/docs/assets/amqtt.svg)
![docs/assets/amqtt.svg](https://raw.githubusercontent.com/Yakifo/amqtt/refs/tags/v0.11.0/docs/assets/amqtt.svg)

@@ -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
[![Build Status](https://img.shields.io/travis/markdown-it/markdown-it-container/master.svg?style=flat)](https://travis-ci.org/markdown-it/markdown-it-container)
[![NPM version](https://img.shields.io/npm/v/markdown-it-container.svg?style=flat)](https://www.npmjs.org/package/markdown-it-container)
[![Coverage Status](https://img.shields.io/coveralls/markdown-it/markdown-it-container/master.svg?style=flat)](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
[![Build Status](https://img.shields.io/travis/markdown-it/markdown-it-deflist/master.svg?style=flat)](https://travis-ci.org/markdown-it/markdown-it-deflist)
[![NPM version](https://img.shields.io/npm/v/markdown-it-deflist.svg?style=flat)](https://www.npmjs.org/package/markdown-it-deflist)
[![Coverage Status](https://img.shields.io/coveralls/markdown-it/markdown-it-deflist/master.svg?style=flat)](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)
[![License](https://img.shields.io/github/license/goessner/markdown-it-texmath.svg)](https://github.com/goessner/markdown-it-texmath/blob/master/licence.txt)
[![npm](https://img.shields.io/npm/v/markdown-it-texmath.svg)](https://www.npmjs.com/package/markdown-it-texmath)
[![npm](https://img.shields.io/npm/dt/markdown-it-texmath.svg)](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)