[build-system] requires = ["hatchling", "hatch-vcs", "uv-dynamic-versioning"] build-backend = "hatchling.build" [project] name = "amqtt" description = "Python's asyncio-native MQTT broker and client." classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: POSIX", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Topic :: Communications", "Topic :: Internet", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13" ] version = "0.11.2" requires-python = ">=3.10.0" readme = "README.md" license = { text = "MIT" } authors = [{ name = "aMQTT Contributors" }] dependencies = [ "transitions==0.9.2", # https://pypi.org/project/transitions "websockets==15.0.1", # https://pypi.org/project/websockets "passlib==1.7.4", # https://pypi.org/project/passlib "PyYAML==6.0.2", # https://pypi.org/project/PyYAML "typer==0.15.4", "dacite>=1.9.2", "psutil>=7.0.0", ] [dependency-groups] dev = [ "hatch>=1.14.1", "hypothesis>=6.130.8", "mypy>=1.15.0", "paho-mqtt>=2.1.0", "poethepoet>=0.34.0", "pre-commit>=4.2.0", # https://pypi.org/project/pre-commit "psutil>=7.0.0", # https://pypi.org/project/psutil "pylint>=3.3.6", # https://pypi.org/project/pylint "pytest-asyncio>=0.26.0", # https://pypi.org/project/pytest-asyncio "pytest-cov>=6.1.0", # https://pypi.org/project/pytest-cov "pytest-logdog>=0.1.0", # https://pypi.org/project/pytest-logdog "pytest-timeout>=2.3.1", # https://pypi.org/project/pytest-timeout "pytest>=8.3.5", # https://pypi.org/project/pytest "ruff>=0.11.3", # https://pypi.org/project/ruff "setuptools>=78.1.0", "types-mock>=5.2.0.20250306", # https://pypi.org/project/types-mock "types-PyYAML>=6.0.12.20250402", # https://pypi.org/project/types-PyYAML "types-setuptools>=78.1.0.20250329", # https://pypi.org/project/types-setuptools ] docs = [ "markdown-callouts>=0.4", "markdown-exec>=1.8", "mkdocs>=1.6", "mkdocs-coverage>=1.0", "mkdocs-git-revision-date-localized-plugin>=1.2", "mkdocs-llmstxt>=0.1", "mkdocs-material>=9.5", "mkdocs-minify-plugin>=0.8", "mkdocs-redirects>=1.2.1", "mkdocs-section-index>=0.3", "mkdocstrings-python>=1.16.2", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", "mkdocs-typer2>=0.1.4", "mkdocs-open-in-new-tab>=1.0.8", "mkdocs-exclude>=1.0.2", ] [project.optional-dependencies] ci = ["coveralls==4.0.1"] [project.scripts] amqtt = "amqtt.scripts.broker_script:main" amqtt_pub = "amqtt.scripts.pub_script:main" amqtt_sub = "amqtt.scripts.sub_script:main" [tool.hatch.build] exclude = [ ".venv*", "tests/", "**/tests/", "**/*.pyc", "**/__pycache__/", "**/README.md", ] [tool.hatch.build.targets.sdist] include = ["/amqtt", "README.md", "docs/assets/amqtt.svg"] [tool.hatch.version] source = "vcs" [tool.hatch.publish.indexes.testpypi] url = "https://test.pypi.org/legacy/" # ___________________________________ PLUGINS __________________________________ [project.entry-points."amqtt.test.plugins"] test_plugin = "tests.plugins.test_manager:EmptyTestPlugin" event_plugin = "tests.plugins.test_manager:EventTestPlugin" packet_logger_plugin = "amqtt.plugins.logging_amqtt:PacketLoggerPlugin" # --8<-- [start:included] [project.entry-points."amqtt.broker.plugins"] event_logger_plugin = "amqtt.plugins.logging_amqtt:EventLoggerPlugin" packet_logger_plugin = "amqtt.plugins.logging_amqtt:PacketLoggerPlugin" auth_anonymous = "amqtt.plugins.authentication:AnonymousAuthPlugin" auth_file = "amqtt.plugins.authentication:FileAuthPlugin" topic_taboo = "amqtt.plugins.topic_checking:TopicTabooPlugin" topic_acl = "amqtt.plugins.topic_checking:TopicAccessControlListPlugin" broker_sys = "amqtt.plugins.sys.broker:BrokerSysPlugin" # --8<-- [end:included] [project.entry-points."amqtt.client.plugins"] packet_logger_plugin = "amqtt.plugins.logging_amqtt:PacketLoggerPlugin" # ____________________________________ RUFF ____________________________________ # https://docs.astral.sh/ruff/settings/ [tool.ruff] line-length = 130 fix = true extend-exclude = ["docs/", "samples/"] [tool.ruff.format] indent-style = "space" docstring-code-format = true [tool.ruff.lint] select = ["ALL"] extend-select = [ "UP", # pyupgrade "D", # pydocstyle ] ignore = [ "FBT001", # Checks for the use of boolean positional arguments in function definitions. "FBT002", # Checks for the use of boolean positional arguments in function definitions. "G004", # Logging statement uses f-string "D100", # Missing docstring in public module "D101", # Missing docstring in public class "D102", # Missing docstring in public method "D107", # Missing docstring in `__init__` "D203", # Incorrect blank line before class (mutually exclusive D211) "D213", # Multi-line summary second line (mutually exclusive D212) "FIX002", # Checks for "TODO" comments. "TD002", # TODO Missing author. "TD003", # TODO Missing issue link for this TODO. "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ARG002", # Unused method argument "PERF203",# try-except penalty within loops (3.10 only), "COM812" # rule causes conflicts when used with the formatter ] [tool.ruff.lint.per-file-ignores] "tests/**" = ["ALL"] "amqtt/scripts/*_script.py" = ["FBT003", "E501"] [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false [tool.ruff.lint.flake8-quotes] docstring-quotes = "double" [tool.ruff.lint.isort] combine-as-imports = true force-sort-within-sections = true case-sensitive = true extra-standard-library = ["typing_extensions"] [tool.ruff.lint.mccabe] max-complexity = 42 [tool.ruff.lint.pylint] max-args = 12 max-branches = 42 max-statements = 143 max-returns = 10 # ----------------------------------- PYTEST ----------------------------------- [tool.pytest.ini_options] addopts = ["--cov=amqtt", "--cov-report=term-missing", "--cov-report=html"] testpaths = ["tests"] asyncio_mode = "auto" timeout = 10 asyncio_default_fixture_loop_scope = "function" #addopts = ["--tb=short", "--capture=tee-sys"] #log_cli = true log_level = "DEBUG" # ------------------------------------ MYPY ------------------------------------ [tool.mypy] exclude = ["^tests/.*", "^docs/.*", "^samples/.*"] follow_imports = "silent" show_error_codes = true ignore_missing_imports = true strict_equality = true warn_incomplete_stub = true warn_redundant_casts = true warn_unused_configs = true warn_unused_ignores = true check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true disallow_untyped_calls = true disallow_untyped_decorators = true disallow_untyped_defs = true no_implicit_optional = true warn_return_any = true warn_unreachable = true strict = true # ----------------------------------- PYLINT ----------------------------------- [tool.pylint.MAIN] jobs = 2 ignore = ["tests"] fail-on = ["I"] max-line-length = 130 [tool.pylint.BASIC] # Good variable names which should always be accepted, separated by a comma. good-names = ["i", "j", "k", "e", "ex", "f", "_", "T", "x", "y", "id", "tg"] [tool.pylint."MESSAGES CONTROL"] # Reasons disabled: # duplicate-code - unavoidable # too-many-* - are not enforced for the sake of readability disable = [ "broad-exception-caught", # TODO: improve later "duplicate-code", "fixme", "invalid-name", "line-too-long", "logging-fstring-interpolation", "missing-class-docstring", "missing-function-docstring", "missing-module-docstring", "protected-access", "redefined-slots-in-subclass", "too-few-public-methods", "too-many-arguments", "too-many-instance-attributes", "unused-argument", ] [tool.pylint.REPORTS] score = false [tool.pylint.FORMAT] expected-line-ending-format = "LF" [tool.pylint.EXCEPTIONS] overgeneral-exceptions = ["builtins.BaseException", "builtins.Exception"] [tool.pylint.REFACTORING] max-nested-blocks = 5 never-returning-functions = ["sys.exit", "argparse.parse_error"] [tool.pylint.DESIGN] max-branches = 32 # too-many-branches max-locals = 20 # too-many-locals max-module-lines = 1500 # too-many-lines max-parents = 10 # too-many-parents max-positional-arguments = 10 # too-many-positional-arguments max-public-methods = 25 # too-many-public-methods max-returns = 7 # too-many-returns max-statements = 90 # too-many-statements # ---------------------------------- COVERAGE ---------------------------------- [tool.coverage.run] branch = true source = ["amqtt"] [tool.coverage.report] show_missing = true skip_covered = true