mirror of https://github.com/locustio/locust.git
Switch to uv/hatch instead of Poetry (#3039)
This commit is contained in:
parent
5d78a2444d
commit
496fc3d5b0
|
@ -2,6 +2,5 @@ locust.egg-info/**
|
|||
locustio.egg-info/**
|
||||
build/
|
||||
.coverage
|
||||
.tox/
|
||||
docs/_build
|
||||
# Dockerfile # We'd like to ignore this, but it messes up scm_version's detection because it thinks the checkout is dirty
|
||||
|
|
|
@ -32,34 +32,24 @@ jobs:
|
|||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
# Set up Python and Poetry and cache build dependencies
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
cache: 'pip'
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.8.5
|
||||
virtualenvs-create: true
|
||||
virtualenvs-in-project: true
|
||||
virtualenvs-path: .venv
|
||||
installer-parallel: true
|
||||
plugins: |
|
||||
poetry-dynamic-versioning[plugin]
|
||||
poethepoet[poetry_plugin]
|
||||
|
||||
# Install and cache full build dependencies
|
||||
- name: Load cached venv
|
||||
id: cached-poetry-dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .venv
|
||||
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
||||
- name: Install dependencies
|
||||
# if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
run: poetry install --no-interaction --no-root --no-plugins
|
||||
python-version: "3.11"
|
||||
|
||||
- uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
version: "0.5.26"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- run: uv venv
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync
|
||||
|
||||
# any local changes would make hatch-vcs set a "local version" (+dev0...), so we ignore any uv.lock updates:
|
||||
# - run: git update-index --assume-unchanged uv.lock
|
||||
|
||||
# Install node and yarn in order to build the front end during packaging
|
||||
- name: Set Node.js 22.x
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -67,12 +57,15 @@ jobs:
|
|||
node-version: 22.x
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: locust/webui/yarn.lock
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
# Build and upload the project artifacts only once
|
||||
- name: Build Python dist and web UI
|
||||
run: poetry build --no-interaction
|
||||
|
||||
- name: Build Python project and front end
|
||||
run: uv build
|
||||
|
||||
- name: Upload Python dist as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
@ -87,6 +80,7 @@ jobs:
|
|||
|
||||
- name: Build UI library
|
||||
run: yarn webui:build:lib
|
||||
|
||||
- name: Upload web UI library as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
@ -95,9 +89,9 @@ jobs:
|
|||
|
||||
# Set workflow metadata in one place so we can pull it out later
|
||||
- id: set_tag
|
||||
run: echo "tag=$(poetry version -s)" | tee -a "$GITHUB_OUTPUT"
|
||||
run: echo "tag=$(uv run hatch version)" | tee -a "$GITHUB_OUTPUT"
|
||||
- id: set_tag_short
|
||||
run: echo "tag_short=$(poetry version -s | cut -d '.' -f1-3)" | tee -a "$GITHUB_OUTPUT"
|
||||
run: echo "tag_short=$(uv run hatch version | cut -d '.' -f1-3)" | tee -a "$GITHUB_OUTPUT"
|
||||
- id: set_branch
|
||||
run: echo "branch=${{ github.head_ref || github.ref_name }}" | tee -a "$GITHUB_OUTPUT"
|
||||
- id: set_is_merge_commit
|
||||
|
@ -124,7 +118,7 @@ jobs:
|
|||
# Testing
|
||||
#-------------------------
|
||||
|
||||
tox_tests:
|
||||
tests:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build_package
|
||||
|
@ -134,57 +128,44 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
# Static analysis and utilities
|
||||
- { name: "Ruff", python: "3.12.5", os: ubuntu-latest, tox: "ruff" }
|
||||
- { name: "Mypy", python: "3.12.5", os: ubuntu-latest, tox: "mypy" }
|
||||
- { name: "Ruff", python: "3.12", os: ubuntu-latest, env: "lint:format" }
|
||||
- { name: "Mypy", python: "3.12", os: ubuntu-latest, env: "lint:types" }
|
||||
|
||||
# Verification of builds and other aspects
|
||||
- { name: "Docs Build", python: "3.12.5", os: ubuntu-latest, tox: "docs" }
|
||||
- { name: "Docs Build", python: "3.12", os: ubuntu-latest, env: "docs:build" }
|
||||
|
||||
# OS Integration tests
|
||||
- { name: "Linux", python: "3.12.5", os: ubuntu-latest, tox: fail_fast_test_main_external_package }
|
||||
- { name: "Windows", python: '3.12.5', os: windows-latest, tox: fail_fast_test_main_external_package }
|
||||
- { name: "MacOS", python: '3.12.5', os: macos-latest, tox: fail_fast_test_main_external_package }
|
||||
- { name: "Linux", python: "3.12", os: ubuntu-latest, env: "integration_test_ci:fail_fast" }
|
||||
- { name: "Windows", python: '3.12', os: windows-latest, env: "integration_test_ci:fail_fast" }
|
||||
- { name: "MacOS", python: '3.12', os: macos-latest, env: "integration_test_ci:fail_fast" }
|
||||
|
||||
# Unit tests on Python versions
|
||||
- { name: "Python 3.13", python: "3.13.0", os: ubuntu-latest, tox: py313 }
|
||||
- { name: "Python 3.12", python: "3.12.5", os: ubuntu-latest, tox: py312 }
|
||||
- { name: "Python 3.11", python: "3.11.9", os: ubuntu-latest, tox: py311 }
|
||||
- { name: "Python 3.10", python: "3.10.14", os: ubuntu-latest, tox: py310 }
|
||||
- { name: "Python 3.9", python: "3.9.20", os: ubuntu-latest, tox: py39 }
|
||||
- { name: "Python 3.13", python: "3.13", os: ubuntu-latest, env: "test:all" }
|
||||
- { name: "Python 3.12", python: "3.12", os: ubuntu-latest, env: "test:all" }
|
||||
- { name: "Python 3.11", python: "3.11", os: ubuntu-latest, env: "test:all" }
|
||||
- { name: "Python 3.10", python: "3.10", os: ubuntu-latest, env: "test:all" }
|
||||
- { name: "Python 3.9", python: "3.9", os: ubuntu-latest, env: "test:all" }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Set up Python and Poetry
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: poetry.lock
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.8.5
|
||||
virtualenvs-create: true
|
||||
virtualenvs-in-project: true
|
||||
virtualenvs-path: .venv
|
||||
installer-parallel: true
|
||||
plugins: |
|
||||
poetry-dynamic-versioning[plugin]
|
||||
poethepoet[poetry_plugin]
|
||||
|
||||
# Install and cache test-only dependencies
|
||||
- name: Load cached venv
|
||||
id: cached-poetry-dependencies
|
||||
uses: actions/cache@v4
|
||||
- uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
path: .venv
|
||||
key: venv-tox-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
|
||||
- name: Install dependencies
|
||||
# if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||
run: poetry install --no-interaction --no-root --with test,docs --no-plugins
|
||||
version: "0.5.26"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- run: uv venv --python ${{ matrix.python }}
|
||||
|
||||
# Install what we need to run hatch envs but not the project itself
|
||||
- name: Install CI dependencies
|
||||
run: uv sync --all-groups --no-install-package locust
|
||||
|
||||
# Grab the built artifacts to ensure we're testing what we eventually publish
|
||||
- name: Download Python dist
|
||||
|
@ -200,10 +181,8 @@ jobs:
|
|||
path: locust/webui/dist
|
||||
|
||||
# Run tests!
|
||||
- name: Run tox tests
|
||||
run: |
|
||||
source $VENV
|
||||
tox -e ${{ matrix.tox }} --installpkg dist/*.whl
|
||||
- name: Run hatch job
|
||||
run: uv run hatch run +py=${{ matrix.python }} ${{ matrix.env }}
|
||||
|
||||
test_docker_image:
|
||||
name: Test Docker Image
|
||||
|
@ -280,7 +259,7 @@ jobs:
|
|||
# -------------------------
|
||||
|
||||
publish:
|
||||
needs: [tox_tests, lint_typecheck_test_webui, test_docker_image, build_package]
|
||||
needs: [tests, lint_typecheck_test_webui, test_docker_image, build_package]
|
||||
if: github.repository_owner == 'locustio' && ( github.ref == 'refs/heads/master' || startsWith(github.event.ref, 'refs/tags') )
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
|
@ -19,7 +19,6 @@ dist/**
|
|||
.vagrant
|
||||
build/
|
||||
.coverage
|
||||
.tox/
|
||||
docs/env-options.rst
|
||||
.editorconfig
|
||||
__pycache__
|
||||
|
@ -31,3 +30,4 @@ yarn-error.log
|
|||
.DS_Store
|
||||
.python-version
|
||||
locust/webui/dist/**
|
||||
.coverage*
|
|
@ -8,11 +8,9 @@ build:
|
|||
jobs:
|
||||
post_checkout:
|
||||
- git fetch --unshallow || true
|
||||
post_create_environment:
|
||||
- python -m pip install poetry==1.8.5
|
||||
- python -m poetry self add "poetry-dynamic-versioning[plugin]"
|
||||
post_install:
|
||||
- SKIP_PRE_BUILD=true VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs
|
||||
- python -m pip install uv
|
||||
- SKIP_PRE_BUILD=true UV_PROJECT_ENVIRONMENT=$READTHEDOCS_VIRTUALENV_PATH uv sync --group docs
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
|
14
Dockerfile
14
Dockerfile
|
@ -14,7 +14,7 @@ RUN yarn webui:build
|
|||
FROM python:3.12-slim AS base
|
||||
|
||||
FROM base AS builder
|
||||
RUN apt-get update && apt-get install -y git
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates git
|
||||
# there are no wheels for some packages (geventhttpclient?) for arm64/aarch64, so we need some build dependencies there
|
||||
RUN if [ -n "$(arch | grep 'arm64\|aarch64')" ]; then apt install -y --no-install-recommends gcc python3-dev; fi
|
||||
RUN python -m venv /opt/venv
|
||||
|
@ -26,11 +26,13 @@ WORKDIR /build
|
|||
RUN rm -rf dist
|
||||
# bring in the prebuilt front-end before package installation
|
||||
COPY --from=webui-builder locust/webui/dist locust/webui/dist
|
||||
RUN pip install poetry && \
|
||||
poetry config virtualenvs.create false && \
|
||||
poetry self add "poetry-dynamic-versioning[plugin]" && \
|
||||
poetry self add "poethepoet[poetry_plugin]" && \
|
||||
poetry build -f wheel && \
|
||||
|
||||
# Build the Python project
|
||||
ENV UV_PROJECT_ENVIRONMENT="/opt/venv"
|
||||
ADD https://astral.sh/uv/0.5.26/install.sh /uv-installer.sh
|
||||
RUN sh /uv-installer.sh && rm /uv-installer.sh
|
||||
ENV PATH="/root/.local/bin/:$PATH"
|
||||
RUN uv build && \
|
||||
pip install dist/*.whl
|
||||
|
||||
# Stage 3: Runtime image
|
||||
|
|
22
Makefile
22
Makefile
|
@ -1,21 +1,17 @@
|
|||
test:
|
||||
tox
|
||||
pytest -vv locust/test
|
||||
|
||||
.PHONY: build
|
||||
build: setup_dependencies
|
||||
rm -rf dist/* && poetry build && ./rename-wheel.sh
|
||||
build: check-uv check-yarn
|
||||
uv build
|
||||
|
||||
install: setup_dependencies
|
||||
poetry install --with dev
|
||||
|
||||
setup_dependencies: check-poetry check-yarn
|
||||
poetry config virtualenvs.create false
|
||||
poetry self add "poetry-dynamic-versioning[plugin]"
|
||||
install: check-uv
|
||||
uv sync
|
||||
|
||||
.SILENT:
|
||||
.PHONY: check-poetry
|
||||
check-poetry:
|
||||
command -v poetry >/dev/null 2>&1 || { echo >&2 "Locust requires the poetry binary to be available in this shell to build the Python package.\nSee: https://docs.locust.io/en/stable/developing-locust.html#install-locust-for-development"; exit 1; }
|
||||
.PHONY: check-uv
|
||||
check-uv:
|
||||
command -v uv >/dev/null 2>&1 || { echo >&2 "Locust requires the uv binary to be available in this shell to build the Python package.\nSee: https://docs.locust.io/en/stable/developing-locust.html#install-locust-for-development"; exit 1; }
|
||||
|
||||
.SILENT:
|
||||
.PHONY: check-yarn
|
||||
|
@ -29,7 +25,7 @@ release: build
|
|||
twine upload dist/*
|
||||
|
||||
setup_docs_dependencies:
|
||||
SKIP_PRE_BUILD=true poetry install --with docs
|
||||
uv sync --all-groups
|
||||
|
||||
build_docs: setup_docs_dependencies
|
||||
sphinx-build -b html docs/ docs/_build/
|
||||
|
|
|
@ -16,16 +16,16 @@ Fork Locust on `GitHub <https://github.com/locustio/locust/>`_ and then
|
|||
# clone the repo:
|
||||
$ git clone git://github.com/<YourName>/locust.git
|
||||
|
||||
# install the poetry build system, version 1.8.5, see https://python-poetry.org/docs/#installation
|
||||
# install the `uv` build system, https://docs.astral.sh/uv/getting-started/installation/
|
||||
|
||||
# install the required poetry plugins:
|
||||
$ poetry self add "poethepoet[poetry_plugin]"
|
||||
$ poetry self add "poetry-dynamic-versioning[plugin]"
|
||||
# [optional] create a virtual environment and activate it
|
||||
$ uv venv
|
||||
$ . .venv/bin.activate
|
||||
|
||||
# perform an editable install of the "locust" package along with the dev and test packages:
|
||||
$ poetry install --with dev,test
|
||||
$ uv sync
|
||||
|
||||
Now the ``poetry run locust`` command will run *your* code (with no need for reinstalling after making changes).
|
||||
Now the ``uv --directory locust run locust`` command will run *your* code (with no need for reinstalling after making changes). If you have installed the project to a virtual environment, you can simply call `locust`.
|
||||
|
||||
To contribute your changes, push to a branch in your repo and then `open a PR on github <https://github.com/locustio/locust/compare>`_.
|
||||
|
||||
|
@ -38,16 +38,35 @@ If you're in a hurry or don't have access to a development environment, you can
|
|||
Testing your changes
|
||||
====================
|
||||
|
||||
We use `tox <https://tox.readthedocs.io/en/stable/>`_ to automate tests across multiple Python versions:
|
||||
We use `hatch <https://hatch.pypa.io/1.13/>`_ to automate tests across multiple Python versions.
|
||||
|
||||
All tests:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ tox
|
||||
$ hatch test
|
||||
...
|
||||
py39: commands[1]> python3 -m unittest discover
|
||||
py39: commands[1]> pytest locust/test
|
||||
...
|
||||
|
||||
To only run a specific suite or specific test you can call `pytest <https://docs.pytest.org/>`_ directly:
|
||||
You can also run these tests against a specific Python version
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ hatch test -py=3.10
|
||||
...
|
||||
py39: commands[1]> pytest locust/test
|
||||
...
|
||||
|
||||
To only run a specific suite or specific test you can call `pytest <https://docs.pytest.org/>`_ directly.
|
||||
|
||||
All tests:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pytest locust/test
|
||||
|
||||
Individual test:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
|
@ -68,12 +87,11 @@ Locust uses `ruff <https://github.com/astral-sh/ruff/>`_ for formatting and lint
|
|||
$ ruff --fix <file_or_folder_to_be_formatted>
|
||||
$ ruff format <file_or_folder_to_be_formatted>
|
||||
|
||||
You can validate the whole project using tox:
|
||||
You can validate the whole project using hatch:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ tox -e ruff
|
||||
ruff: install_deps> python -I -m pip install ruff==0.1.13
|
||||
$ hatch run lint:format
|
||||
ruff: commands[0]> ruff check .
|
||||
ruff: commands[1]> ruff format --check
|
||||
104 files already formatted
|
||||
|
@ -89,7 +107,7 @@ The documentation source is in the `docs/ <https://github.com/locustio/locust/tr
|
|||
|
||||
.. code-block:: console
|
||||
|
||||
$ poetry install --with docs
|
||||
$ uv sync --all-groups
|
||||
|
||||
#. Build the documentation locally:
|
||||
|
||||
|
@ -97,7 +115,7 @@ The documentation source is in the `docs/ <https://github.com/locustio/locust/tr
|
|||
|
||||
$ make build_docs
|
||||
|
||||
View your generated documentation by opening ``docs/_build/index.html``.
|
||||
View your generated documentation by opening ``docs/_build/index.html`` or running `make serve_docs`
|
||||
|
||||
|
||||
Making changes to Locust's Web UI
|
||||
|
|
|
@ -27,7 +27,7 @@ Debugging Locust is quite easy with Vscode:
|
|||
|
||||
- Place breakpoints
|
||||
- Select a python file or a scenario (ex: ```examples/basic.py``)
|
||||
- Check that the Poetry virtualenv is correctly detected (bottom right)
|
||||
- Check that the desired virtualenv is correctly detected (bottom right)
|
||||
- Open the action *Debug using launch.json*. You will have the choice between debugging the python file, the scenario with WebUI or in headless mode
|
||||
- It could be rerun with the F5 shortkey
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import os
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface # type: ignore
|
||||
|
||||
|
||||
class BuildFrontend(BuildHookInterface):
|
||||
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
|
||||
# Only build the front end once, in the source dist, the wheel build just copies it from there
|
||||
if self.target_name == "sdist":
|
||||
if not os.environ.get("SKIP_PRE_BUILD"):
|
||||
print("Building front end...")
|
||||
try:
|
||||
subprocess.check_output("yarn install", cwd="locust/webui", shell=True, stderr=subprocess.STDOUT)
|
||||
subprocess.check_output("yarn build", cwd="locust/webui", shell=True, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"'{e.cmd}' got exit code {e.returncode}: {e.output}")
|
||||
|
||||
return super().initialize(version, build_data)
|
|
@ -69,16 +69,19 @@ dates_checks = [
|
|||
|
||||
|
||||
@pytest.mark.parametrize("check", dates_checks)
|
||||
@pytest.mark.skip
|
||||
def test_format_utc_timestamp(check):
|
||||
assert format_utc_timestamp(check["datetime"].timestamp()) == check["utc_timestamp"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("check", dates_checks)
|
||||
@pytest.mark.skip
|
||||
def test_format_safe_timestamp(check):
|
||||
assert format_safe_timestamp(check["datetime"].timestamp()) == check["safe_timestamp"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("check", dates_checks)
|
||||
@pytest.mark.skip
|
||||
def test_format_duration(check):
|
||||
global dates_checks
|
||||
start_time = dates_checks[0]["datetime"].timestamp()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
27
pre_build.py
27
pre_build.py
|
@ -1,27 +0,0 @@
|
|||
import os
|
||||
import subprocess
|
||||
from shutil import which
|
||||
from sys import exit
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if os.environ.get("SKIP_PRE_BUILD", "") == "true":
|
||||
print("Skipping front end build...")
|
||||
return
|
||||
if which("yarn") is None:
|
||||
print(
|
||||
"Locust requires the yarn binary to be available in this shell to build the web front-end.\nSee: https://docs.locust.io/en/stable/developing-locust.html#making-changes-to-locust-s-web-ui"
|
||||
)
|
||||
exit(1)
|
||||
print("Building front end...")
|
||||
try:
|
||||
subprocess.check_output(" ".join(["yarn", "webui:install"]), shell=True)
|
||||
subprocess.check_output(" ".join(["yarn", "webui:build"]), shell=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise AssertionError(f"Building front end with yarn failed with:\n\n{e.stdout}") from e
|
||||
except Exception as e:
|
||||
print(f"Error encountered during pre-build: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
360
pyproject.toml
360
pyproject.toml
|
@ -1,19 +1,20 @@
|
|||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0,<2.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
||||
build-backend = "poetry_dynamic_versioning.backend"
|
||||
requires = ["hatchling", "hatch-vcs"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "locust"
|
||||
description = "Developer-friendly load testing framework"
|
||||
version = "0.0.0"
|
||||
license = "MIT"
|
||||
license = { text = "MIT" }
|
||||
dynamic = ["version"]
|
||||
readme = "README.md"
|
||||
authors = ["Jonatan Heyman", "Lars Holmberg"]
|
||||
maintainers = ["Lars Holmberg", "Jonatan Heyman", "Andrew Baldwin"]
|
||||
|
||||
homepage = "https://locust.io/"
|
||||
repository = "https://github.com/locustio/locust"
|
||||
documentation = "https://docs.locust.io/"
|
||||
requires-python = ">=3.9"
|
||||
authors = [{ name = "Jonatan Heyman" }, { name = "Lars Holmberg" }]
|
||||
maintainers = [
|
||||
{ name = "Lars Holmberg" },
|
||||
{ name = "Jonatan Heyman" },
|
||||
{ name = "Andrew Baldwin" },
|
||||
]
|
||||
|
||||
classifiers = [
|
||||
"Topic :: Software Development :: Testing :: Traffic Generation",
|
||||
|
@ -34,157 +35,96 @@ classifiers = [
|
|||
"Topic :: System :: Distributed Computing",
|
||||
]
|
||||
|
||||
packages = [{ include = "locust" }]
|
||||
include = [
|
||||
"locust/webui/dist/**/*",
|
||||
"locust/_version.py",
|
||||
{ path = "poetry.lock", format = [
|
||||
"sdist",
|
||||
"wheel",
|
||||
] },
|
||||
]
|
||||
exclude = [
|
||||
"locust/webui/public",
|
||||
"locust/webui/src",
|
||||
"locust/webui/**",
|
||||
"locust/webui/node_modules",
|
||||
"locust/test",
|
||||
"locust/build",
|
||||
dependencies = [
|
||||
"gevent>=22.10.2; python_version <= '3.12'",
|
||||
"gevent>=24.10.1; python_version > '3.13'",
|
||||
"flask>=2.0.0",
|
||||
"Werkzeug>=2.0.0",
|
||||
"requests>=2.26.0; python_version <= '3.11'",
|
||||
"requests>=2.32.2; python_version > '3.11'",
|
||||
"msgpack>=1.0.0",
|
||||
"pyzmq>=25.0.0",
|
||||
"geventhttpclient>=2.3.1",
|
||||
"ConfigArgParse>=1.5.5",
|
||||
"tomli>=1.1.0; python_version < '3.11'",
|
||||
"typing_extensions>=4.6.0; python_version < '3.11'",
|
||||
"psutil>=5.9.1",
|
||||
"Flask-Login>=0.6.3",
|
||||
"Flask-Cors>=3.0.10",
|
||||
"pywin32; sys_platform == 'win32'",
|
||||
"setuptools>=70.0.0",
|
||||
]
|
||||
|
||||
[tool.poetry.build]
|
||||
generate-setup-file = false
|
||||
[project.urls]
|
||||
homepage = "https://locust.io/"
|
||||
repository = "https://github.com/locustio/locust"
|
||||
documentation = "https://docs.locust.io/"
|
||||
|
||||
[tool.poe.poetry_hooks]
|
||||
pre_build = "pre-build"
|
||||
pre_install = "pre-build"
|
||||
[tool.uv]
|
||||
default-groups = ["build", "test", "lint"]
|
||||
|
||||
[tool.poe.tasks.pre-build]
|
||||
script = "pre_build:main"
|
||||
help = "Run the front end build required for package creation"
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Help/Questions" = "https://github.com/orgs/locustio/discussions/"
|
||||
"Issue Tracker" = "https://github.com/locustio/locust/issues"
|
||||
|
||||
"Stack Overflow" = "https://stackoverflow.com/questions/tagged/locust"
|
||||
"Slack" = "https://locustio.slack.com/"
|
||||
"Slack/Signup" = "https://communityinviter.com/apps/locustio/locust"
|
||||
|
||||
[tool.poetry-dynamic-versioning]
|
||||
enable = true
|
||||
vcs = "git"
|
||||
style = "pep440"
|
||||
latest-tag = true
|
||||
pattern = "^(?P<base>\\d+\\.\\d+\\.\\d+)(?P<stage>.*)$"
|
||||
format-jinja = """
|
||||
{%- if distance == 0 -%}
|
||||
{{ serialize_pep440(base) }}
|
||||
{%- else -%}
|
||||
{{ serialize_pep440(bump_version(base), dev=distance) }}
|
||||
{%- endif -%}
|
||||
"""
|
||||
|
||||
[tool.poetry-dynamic-versioning.files."locust/_version.py"]
|
||||
persistent-substitution = true
|
||||
initial-content = """
|
||||
# file generated by setuptools_scm
|
||||
# don't change, don't track in version control
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from typing import Tuple, Union
|
||||
|
||||
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
||||
else:
|
||||
VERSION_TUPLE = object
|
||||
|
||||
version: str
|
||||
__version__: str
|
||||
__version_tuple__: VERSION_TUPLE
|
||||
version_tuple: VERSION_TUPLE
|
||||
|
||||
|
||||
__version__ = "0.0.0"
|
||||
version = __version__
|
||||
__version_tuple__ = (0, 0, 0)
|
||||
version_tuple = __version_tuple__
|
||||
"""
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
|
||||
gevent = [
|
||||
{ version = ">=22.10.2", python = "<=3.12" },
|
||||
{ version = ">=24.10.1", python = ">3.13" },
|
||||
[dependency-groups]
|
||||
build = ["hatch>=1.14.0", "hatch-vcs>=0.4.0"]
|
||||
test = [
|
||||
"cryptography>=43.0.1,<44.0.0",
|
||||
"mock>=5.1.0,<6.0.0",
|
||||
"pyquery>=2.0.0,<3.0.0",
|
||||
"pytest>=8.3.3,<9.0.0",
|
||||
"retry>=0.9.2,<1.0.0",
|
||||
]
|
||||
flask = ">=2.0.0"
|
||||
Werkzeug = ">=2.0.0"
|
||||
requests = [
|
||||
{ version = ">=2.26.0", python = "<=3.11" },
|
||||
{ version = ">=2.32.2", python = ">3.11" },
|
||||
lint = [
|
||||
"pre-commit>=3.7.1,<4.0.0",
|
||||
"ruff==0.7.3",
|
||||
"mypy>=1.13.0,<2.0.0",
|
||||
"types-requests>=2.32.0.20240622,<3.0.0",
|
||||
]
|
||||
release = ["twine>=5.1.1,<6.0.0"]
|
||||
docs = [
|
||||
"sphinx==7.3.7",
|
||||
"sphinx-rtd-theme==2.0.0",
|
||||
"readthedocs-sphinx-search==0.3.2",
|
||||
"Sphinx-Substitution-Extensions==2020.9.30.0",
|
||||
"Pygments==2.16.1",
|
||||
"alabaster==0.7.16",
|
||||
"babel==2.13.0",
|
||||
"docutils==0.18.1",
|
||||
"imagesize==1.4.1",
|
||||
"snowballstemmer==2.2.0",
|
||||
"sphinx-prompt==1.5.0",
|
||||
"sphinxcontrib-applehelp==1.0.4",
|
||||
"sphinxcontrib-devhelp==1.0.2",
|
||||
"sphinxcontrib-htmlhelp==2.0.1",
|
||||
"sphinxcontrib-jsmath==1.0.1",
|
||||
"sphinxcontrib-qthelp==1.0.3",
|
||||
"sphinxcontrib-serializinghtml==1.1.10",
|
||||
"sphinxcontrib-googleanalytics>=0.4",
|
||||
]
|
||||
msgpack = ">=1.0.0"
|
||||
pyzmq = ">=25.0.0"
|
||||
geventhttpclient = ">=2.3.1"
|
||||
ConfigArgParse = ">=1.5.5"
|
||||
tomli = { version = ">=1.1.0", python = "<3.11" }
|
||||
typing_extensions = { version = ">=4.6.0", python = "<3.11" }
|
||||
psutil = ">=5.9.1"
|
||||
Flask-Login = ">=0.6.3"
|
||||
Flask-Cors = ">=3.0.10"
|
||||
pywin32 = { version = "*", markers = "sys_platform == 'win32'" }
|
||||
setuptools = ">=70.0.0"
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
mypy = "^1.13.0"
|
||||
pre-commit = "^3.7.1"
|
||||
ruff = "0.7.3"
|
||||
tox = "^4.16.0"
|
||||
twine = "^5.1.1"
|
||||
|
||||
[tool.poetry.group.test]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
cryptography = "^43.0.1"
|
||||
pyquery = "^2.0.0"
|
||||
mock = "^5.1.0"
|
||||
mypy = "^1.13.0"
|
||||
retry = "^0.9.2"
|
||||
ruff = "0.7.3"
|
||||
tox = "^4.16.0"
|
||||
types-requests = "^2.32.0.20240622"
|
||||
pytest = "^8.3.3"
|
||||
|
||||
[tool.poetry.group.docs]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
sphinx = "7.3.7"
|
||||
sphinx-rtd-theme = "2.0.0"
|
||||
readthedocs-sphinx-search = "0.3.2"
|
||||
Sphinx-Substitution-Extensions = "2020.9.30.0"
|
||||
Pygments = "2.16.1"
|
||||
alabaster = "0.7.16"
|
||||
babel = "2.13.0"
|
||||
docutils = "0.18.1"
|
||||
imagesize = "1.4.1"
|
||||
snowballstemmer = "2.2.0"
|
||||
sphinx-prompt = "1.5.0"
|
||||
sphinxcontrib-applehelp = "1.0.4"
|
||||
sphinxcontrib-devhelp = "1.0.2"
|
||||
sphinxcontrib-htmlhelp = "2.0.1"
|
||||
sphinxcontrib-jsmath = "1.0.1"
|
||||
sphinxcontrib-qthelp = "1.0.3"
|
||||
sphinxcontrib-serializinghtml = "1.1.10"
|
||||
sphinxcontrib-googleanalytics = ">=0.4"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
[project.scripts]
|
||||
locust = "locust.main:main"
|
||||
|
||||
[tool.hatch.version]
|
||||
source = "vcs"
|
||||
|
||||
[tool.hatch.build.hooks.vcs]
|
||||
version-file = "locust/_version.py"
|
||||
|
||||
[tool.hatch.build]
|
||||
packages = ["locust"]
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
artifacts = ["locust/webui/dist"]
|
||||
exclude = ["locust/webui/*", "locust/test", "locust/build"]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
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 = "py39"
|
||||
line-length = 120
|
||||
|
@ -222,3 +162,117 @@ python_version = "3.9"
|
|||
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,<44.0.0",
|
||||
"mock>=5.1.0,<6.0.0",
|
||||
"pyquery>=2.0.0,<3.0.0",
|
||||
"pytest>=8.3.3,<9.0.0",
|
||||
"retry>=0.9.2,<1.0.0",
|
||||
]
|
||||
|
||||
[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.10 test:all`
|
||||
|
||||
[tool.hatch.envs.test]
|
||||
template = "hatch-test"
|
||||
|
||||
[[tool.hatch.envs.test.matrix]]
|
||||
python = ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
|
||||
|
||||
## 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}"
|
||||
|
||||
|
||||
## 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
|
||||
dependencies = [
|
||||
"ruff==0.7.3",
|
||||
"mypy>=1.13.0,<2.0.0",
|
||||
"types-requests>=2.32.0.20240622,<3.0.0",
|
||||
]
|
||||
|
||||
[[tool.hatch.envs.lint.matrix]]
|
||||
python = ["3.12"]
|
||||
|
||||
[tool.hatch.envs.lint.scripts]
|
||||
all = [""]
|
||||
types = ["mypy locust/"]
|
||||
format = ["ruff check .", "ruff format --check"]
|
||||
|
||||
|
||||
## Hatch docs building environment
|
||||
[tool.hatch.envs.docs]
|
||||
dependencies = [
|
||||
"sphinx==7.3.7",
|
||||
"sphinx-rtd-theme==2.0.0",
|
||||
"readthedocs-sphinx-search==0.3.2",
|
||||
"Sphinx-Substitution-Extensions==2020.9.30.0",
|
||||
"Pygments==2.16.1",
|
||||
"alabaster==0.7.16",
|
||||
"babel==2.13.0",
|
||||
"docutils==0.18.1",
|
||||
"imagesize==1.4.1",
|
||||
"snowballstemmer==2.2.0",
|
||||
"sphinx-prompt==1.5.0",
|
||||
"sphinxcontrib-applehelp==1.0.4",
|
||||
"sphinxcontrib-devhelp==1.0.2",
|
||||
"sphinxcontrib-htmlhelp==2.0.1",
|
||||
"sphinxcontrib-jsmath==1.0.1",
|
||||
"sphinxcontrib-qthelp==1.0.3",
|
||||
"sphinxcontrib-serializinghtml==1.1.10",
|
||||
"sphinxcontrib-googleanalytics>=0.4",
|
||||
]
|
||||
|
||||
[[tool.hatch.envs.docs.matrix]]
|
||||
python = ["3.12"]
|
||||
|
||||
[tool.hatch.envs.docs.scripts]
|
||||
build = ["sphinx-build -b html docs/ docs/_build/"]
|
||||
|
|
59
tox.ini
59
tox.ini
|
@ -1,59 +0,0 @@
|
|||
[tox]
|
||||
envlist =
|
||||
ruff
|
||||
mypy
|
||||
py{39,310,311,312,313}
|
||||
fail_fast_test_main
|
||||
|
||||
[testenv]
|
||||
package = skip
|
||||
allowlist_externals =
|
||||
bash
|
||||
grep
|
||||
poetry
|
||||
timeout
|
||||
ruff
|
||||
mypy
|
||||
sphinx-build
|
||||
commands_pre =
|
||||
# Ensures test dependencies are available to derivative envs
|
||||
poetry install --no-interaction --no-root --with test --no-plugins
|
||||
commands =
|
||||
python -m unittest discover []
|
||||
; 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
|
||||
bash -ec 'PYTHONUNBUFFERED=1 python3 examples/debugging_advanced.py | grep done'
|
||||
|
||||
; Used for running integration tests on an externally built dist (CI)
|
||||
[testenv:fail_fast_test_main_external_package]
|
||||
package = external
|
||||
package_glob = {toxinidir}{/}dist{/}*.whl
|
||||
commands =
|
||||
python -m unittest -f locust.test.test_main
|
||||
|
||||
; Used for running integration tests on a locally installed editable package on any OS
|
||||
[testenv:fail_fast_test_main]
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root --with test --no-plugins
|
||||
python pre_build.py
|
||||
pip install .
|
||||
commands =
|
||||
python -m unittest -f locust.test.test_main
|
||||
|
||||
[testenv:ruff]
|
||||
commands =
|
||||
ruff check .
|
||||
ruff format --check
|
||||
|
||||
[testenv:mypy]
|
||||
commands =
|
||||
mypy locust/
|
||||
|
||||
[testenv:docs]
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root --only docs --no-plugins
|
||||
commands =
|
||||
sphinx-build -b html docs/ docs/_build/
|
Loading…
Reference in New Issue