[build-system] requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] name = "amqtt" description = "MQTT client/broker using Python asyncio" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Operating System :: POSIX", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Topic :: Communications", "Topic :: Internet", ] version = "0.11.0-beta3" requires-python = ">=3.12.0" readme = "README.rst" 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 "docopt==0.6.2", # https://pypi.org/project/docopt "PyYAML==6.0.2", # https://pypi.org/project/PyYAML ] [dependency-groups] dev = [ "mypy>=1.15.0", # https://pypi.org/project/mypy "pre-commit>=4.2.0", # https://pypi.org/project/pre-commit "pycountry>=24.6.1", # https://pypi.org/project/pycountry "pylint>=3.3.6", # https://pypi.org/project/pylint "pytest-aiofiles>=0.2.0", # https://pypi.org/project/pytest-aiofiles "pytest-aiohttp>=1.1.0", # https://pypi.org/project/pytest-aiohttp "pytest-asyncio>=0.26.0", # https://pypi.org/project/pytest-asyncio "pytest-cov>=6.1.0", # https://pypi.org/project/pytest-cov "pytest-docker-fixtures>=1.3.19", # https://pypi.org/project/pytest-docker-fixtures "pytest-env>=1.1.5", # https://pypi.org/project/pytest-env "pytest-timeout>=2.3.1", # https://pypi.org/project/pytest-timeout "pytest>=8.3.5", # https://pypi.org/project/pytest "ruff>=0.11.2", # https://pypi.org/project/ruff "testfixtures>=8.3.0", # https://pypi.org/project/testfixtures "types-aiofiles>=24.1.0.20250326", # https://pypi.org/project/types-aiofiles "types-cachetools>=5.5.0.20240820", # https://pypi.org/project/types-cachetools "types-mock>=5.2.0.20250306", # https://pypi.org/project/types-mock "types-pillow>=10.2.0.20240822", # https://pypi.org/project/types-pillow "types-pytz>=2025.2.0.20250326", # https://pypi.org/project/types-pytz "types-PyYAML>=6.0.12.20250326", # https://pypi.org/project/types-PyYAML "hypothesis>=6.130.6", # https://pypi.org/project/hypothesis "pytest-logdog>=0.1.0", # https://pypi.org/project/pytest-logdog "psutil>=7.0.0", # https://pypi.org/project/psutil "setuptools>=78.1.0", # https://pypi.org/project/setuptools "types-setuptools>=78.1.0.20250329", # https://pypi.org/project/types-setuptools ] [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.targets.sdist] include = ["/amqtt"] [tool.hatch.version] source = "vcs" # ___________________________________ 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" [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" [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] # quote-style = "single" indent-style = "space" docstring-code-format = true [tool.ruff.lint] select = ["ALL"] extend-select = [ "UP", # pyupgrade "D", # pydocstyle ] ignore = [ "ERA001", # Checks for commented-out Python code. "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 "TRY300", # Checks for return statements in try blocks. "D100", # Missing docstring in public module "D101", # Missing docstring in public class "D102", # Missing docstring in public method "D103", # Missing docstring in public function "D105", # Missing docstring in magic method "D107", # Missing docstring in `__init__` "FIX002", # Checks for "TODO" comments. "TD002", # Checks that a TODO comment includes an author. "TD003", # Checks that a TODO comment is associated with a link to a relevant issue or ticket. "SLF001", # Private member accessed "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ARG002", # Unused method argument "ARG003", # Unused class method argument "PLR2004", # Magic value used in comparison, consider replacing with a constant variable ] [tool.ruff.lint.per-file-ignores] "tests/**" = [ "ALL", "D100", # Missing docstring in public module "D101", # "D102", # Missing docstring in public method "D103", # Missing docstring in public function "D104", # Missing docstring in public package "N802", # Function name {name} should be lowercase "N806", # Variable `userId` in function should be lowercase "N816", # Variable {name} in global scope should not be mixedCase "S101", # Use of assert detected "S106", # Possible hardcoded password assigned to argument: "password_file" "SLF001", # Private member accessed ] [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=./", "--cov-report=xml"] testpaths = ["tests"] pythonpath = "amqtt" env = [] asyncio_mode = "auto" timeout = 10 # log_cli = true # log_level = "INFO" # ------------------------------------ MYPY ------------------------------------ [tool.mypy] # mypy_path = "amqtt" 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 = [ "duplicate-code", "too-few-public-methods", "too-many-arguments", "too-many-instance-attributes", "too-many-locals", "too-many-ancestors", "logging-fstring-interpolation", "broad-exception-caught", "broad-exception-raised", "fixme", "import-error", "missing-module-docstring", "missing-function-docstring", "missing-class-docstring", "unused-argument", "protected-access", "line-too-long", "too-many-branches", "too-many-statements", "too-many-nested-blocks", "too-many-public-methods", "invalid-name", "redefined-slots-in-subclass", ] # enable useless-suppression temporarily every now and then to clean them up enable = [ "useless-suppression", "use-symbolic-message-instead", "c-extension-no-member", ] [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 = 20 # too-many-branches max-parents = 10 max-positional-arguments = 10 # too-many-positional-arguments max-returns = 7 max-statements = 61 # too-many-statements