[build-system] requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] name = "locust" description = "Developer-friendly load testing framework" license = { text = "MIT" } dynamic = ["version"] readme = "README.md" requires-python = ">=3.11" authors = [{ name = "Jonatan Heyman" }, { name = "Lars Holmberg" }] maintainers = [ { name = "Lars Holmberg" }, { name = "Jonatan Heyman" }, { name = "Andrew Baldwin" }, ] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.15", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Testing :: Traffic Generation", "Topic :: System :: Distributed Computing", ] dependencies = [ "flask>=2.0.0", "Werkzeug>=2.0.0", "requests>=2.33.1", "msgpack>=1.0.0", "pyzmq>=25.0.0", "geventhttpclient>=2.5.1", "configargparse>=1.7.1", "typing_extensions>=4.6.0; python_version < '3.12'", "psutil>=5.9.1", "Flask-Login>=0.6.3", "Flask-Cors>=3.0.10", "pywin32; sys_platform == 'win32'", "gevent>=24.11.1", "python-socketio[client]>=5.13.0", "python-engineio>=4.12.2", "pytest>=8.3.3,<10", # these are already indirect dependencies, but good to have because of eMudhra "certifi>=2026.10.19", "urllib3>=2.8.0", ] [project.optional-dependencies] milvus = ["pymilvus>=2.5.0"] mqtt = ["paho-mqtt>=2.1.0"] dns = ["dnspython>=2.8.0"] otel = [ "opentelemetry-sdk>=1.38.0", "opentelemetry-exporter-otlp-proto-grpc>=1.38.0", "opentelemetry-exporter-otlp-proto-http>=1.38.0", "opentelemetry-instrumentation-requests>=0.59b0", "opentelemetry-instrumentation-urllib3>=0.59b0", ] qdrant = ["qdrant-client>=1.16.2"] [project.urls] Homepage = "https://locust.cloud/" Repository = "https://github.com/locustio/locust" Documentation = "https://docs.locust.io/" [tool.uv] default-groups = ["build", "test", "lint"] [dependency-groups] build = ["hatch==1.16.5", "hatch-vcs>=0.4.0"] test = [ "cryptography>=43.0.1,<51.0.0", "pyquery>=2.0.0,<3.0.0", "retry>=0.9.2,<1.0.0", ] lint = [ "pre-commit>=4.7.0,<5.0.0", "ruff==0.17.2", "mypy>=1.13.0,<1.15.0", "types-requests==2.33.0.20260906", "typos>=1.51.1" ] release = ["twine>=5.1.1,<6.0.0"] docs = [ "sphinx==7.4.7", "sphinx-rtd-theme==3.1.0", "readthedocs-sphinx-search==0.3.2", "Sphinx-Substitution-Extensions==2020.9.30.0", "pygments>=2.21.0", "alabaster==0.7.16", "babel==2.13.0", "docutils==0.21.2", "imagesize==1.4.1", "snowballstemmer==3.0.1", "sphinx-prompt==1.5.0", "sphinxcontrib-applehelp==2.0.0", "sphinxcontrib-devhelp==1.0.2", "sphinxcontrib-htmlhelp==2.1.0", "sphinxcontrib-jsmath==1.0.1", "sphinxcontrib-qthelp==1.0.3", "sphinxcontrib-serializinghtml==2.0.0", "sphinxcontrib-googleanalytics>=0.4", "sphinx-markdown-builder>=0.6.0", # these optional dependencies are needed to build some contrib modules "pymilvus>=2.5.0", "psycopg[binary]>=3.2.1", "pymongo>=4.8.0", "qdrant-client>=1.16.2", ] milvus = ["pymilvus>=2.5.0"] mqtt = ["paho-mqtt>=2.1.0"] dns = ["dnspython>=2.8.0"] otel = [ "opentelemetry-sdk>=1.45.1", "opentelemetry-exporter-otlp-proto-grpc>=1.45.1", "opentelemetry-exporter-otlp-proto-http>=1.45.1", "opentelemetry-instrumentation-requests>=0.66b1", "opentelemetry-instrumentation-urllib3>=0.66b1", "opentelemetry-api>=1.45.1", ] qdrant = ["qdrant-client>=1.16.2"] pymongo = [ "pymongo>=4.18.2", ] [project.scripts] locust = "locust.main:main" [tool.hatch.version] source = "vcs" [tool.hatch.build.hooks.vcs] version-file = "locust/_version.py" [tool.hatch.build.targets.sdist] include = ["locust", "pytest_locust"] exclude = ["locust/webui/*", "locust/test", "locust/build"] artifacts = ["locust/webui/dist"] [tool.hatch.build.targets.wheel] include = ["locust", "pytest_locust"] artifacts = ["locust/webui/dist"] [tool.hatch.version.raw-options] local_scheme = "no-local-version" [tool.hatch.build.hooks.custom] [tool.hatch.build.targets.BuildFrontend.hooks.custom] [tool.ruff] target-version = "py311" line-length = 120 force-exclude = true extend-exclude = [ "build", "examples/issue_*.py", "src/readthedocs-sphinx-search/", "examples/grpc/hello_pb2*.py", ] lint.ignore = ["E402", "E501", "E713", "E731", "E741", "UP031", "PERF203", "RUF012", "RUF015"] lint.select = ["E", "F", "W", "UP", "FA102", "I001", "FURB", "PERF", "RUF"] lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|resp)$" [tool.ruff.lint.per-file-ignores] "examples/*" = ["F841"] [tool.ruff.lint.isort] section-order = [ "future", "locust", "standard-library", "third-party", "first-party", "local-folder", ] # Custom selection-order: to ensure locust is imported as first in locustfiles (for successful gevent monkey patching) [tool.ruff.lint.isort.sections] locust = ["locust"] [tool.mypy] # missing type stubs ignore_missing_imports = true python_version = "3.11" [tool.pyright] exclude = ["locust/test"] reportOptionalMemberAccess = "none" # analyzeUnannotatedFunctions = false # Hatch environments # List environments using `hatch env show` # https://hatch.pypa.io/1.13/tutorials/environment/basic-usage/ ## Local Testing # Default hatch environment, used during local development # List tests using `hatch test -s` # https://hatch.pypa.io/1.13/tutorials/testing/overview/ [tool.hatch.envs.hatch-test] default-args = ["locust/test"] dependencies = [ "cryptography>=43.0.1,<51.0.0", "pyquery>=2.0.1,<3.0.0", "pytest>=8.3.3,<10", "retry>=0.9.2,<1.0.0", "opentelemetry-sdk>=1.38.0", "opentelemetry-exporter-otlp-proto-grpc>=1.38.0", "opentelemetry-exporter-otlp-proto-http>=1.38.0", "opentelemetry-instrumentation-requests>=0.59b0", "opentelemetry-instrumentation-urllib3>=0.59b0", ] [tool.hatch.envs.hatch-test.scripts] run = [ "pytest{env:HATCH_TEST_ARGS:} {args}", "bash -ec 'PYTHONUNBUFFERED=1 python3 examples/debugging_advanced.py | grep done'", ] ## CI testing # Run all combinations: `hatch run test:all` # Run single env: `hatch run +py=3.14 test:all` [tool.hatch.envs.test] template = "hatch-test" [[tool.hatch.envs.test.matrix]] python = ["3.11", "3.12", "3.13", "3.14", "3.15"] ## Run all unit tests and advanced debugging [tool.hatch.envs.test.scripts] # Disable these tests for now, because they are breaking. When someone has time they should be converted into regular unit tests # bash -ec "PYTHONUNBUFFERED=1 timeout 20s python3 examples/rest.py >{temp_dir}/out.txt 2>/{temp_dir}/err.txt || true" # grep -qm 1 'my custom error message with response text, response was {"args"' {temp_dir}/out.txt # grep -qm 1 'ZeroDivisionError: division by zero at.*Response was {"ar' {temp_dir}/out.txt # bash -ec '! grep . {temp_dir}/err.txt' # should be empty all = [ "pytest {args:locust/test}", "bash -ec 'PYTHONUNBUFFERED=1 python3 examples/debugging_advanced.py | grep done'", ] # Run main test and bail on first error fail_fast = "pytest -x {args:locust/test/test_main.py}" # Run pytest-style example locustfile in pytest test_pytest = "pytest -H https://www.locust.io examples/test_pytest.py" ## CI integration testing ## Integration testing of build packages on CI, using a pre-built package [tool.hatch.envs.integration_test_ci] template = "test" skip-install = true path = ".venv" [[tool.hatch.envs.integration_test_ci.matrix]] python = ["3.12"] ## Linting [tool.hatch.envs.lint] detached = true [[tool.hatch.envs.lint.matrix]] python = ["3.12"] [tool.hatch.envs.lint.scripts] all = [""] types = ["mypy locust/"] format = ["ruff check .", "ruff format --check"] spelling = ["typos ."] ## Hatch docs building environment [tool.hatch.envs.docs] [[tool.hatch.envs.docs.matrix]] python = ["3.12"] [tool.hatch.envs.docs.scripts] build = ["sphinx-build -b html docs/ docs/_build/"] [project.entry-points.pytest11] locust = "pytest_locust.plugin" [tool.pytest] filterwarnings = [ # sometimes, monkey patching happens too late # the proper fix for this is to run python -m gevent.monkey --module pytest ... # but that isn't always possible "ignore::gevent.monkey.MonkeyPatchWarning", # "This process is multi-threaded, use of fork() may lead to deadlocks in the child." started happening in Python 3.12 # It could potentially be a real issue, but I don't think "ignore::DeprecationWarning:gevent", ]