locust 2.43.4.dev1__tar.gz → 2.43.4.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/PKG-INFO +1 -1
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/_version.py +2 -2
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/mqtt.py +95 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/.gitignore +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/LICENSE +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/README.md +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/hatch_build.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/__init__.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/__main__.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/argument_parser.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/clients.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/__init__.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/dns.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/fasthttp.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/milvus.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/mongodb.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/oai.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/postgres.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/contrib/socketio.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/debug.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/dispatch.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/env.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/event.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/exception.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/html.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/input_events.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/log.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/main.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/opentelemetry.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/py.typed +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/rpc/__init__.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/rpc/protocol.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/rpc/zmqrpc.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/runners.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/shape.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/stats.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/__init__.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/inspectuser.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/markov_taskset.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/sequential_taskset.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/task.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/users.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/user/wait_time.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/__init__.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/cache.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/date.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/deprecation.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/directory.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/exception_handler.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/load_locustfile.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/rounding.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/timespan.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/util/url.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/web.py +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/favicon-dark.png +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/favicon-light.png +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/graphs-dark.png +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/graphs-light.png +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/index-Bl8icIRq.js +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/terminal.gif +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/testruns-dark.png +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/assets/testruns-light.png +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/auth.html +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/index.html +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/locust/webui/dist/report.html +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/pyproject.toml +0 -0
- {locust-2.43.4.dev1 → locust-2.43.4.dev5}/pytest_locust/plugin.py +0 -0
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.43.4.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 43, 4, '
|
|
31
|
+
__version__ = version = '2.43.4.dev5'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 43, 4, 'dev5')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -4,10 +4,13 @@ from locust import User
|
|
|
4
4
|
from locust.env import Environment
|
|
5
5
|
|
|
6
6
|
import random
|
|
7
|
+
import selectors
|
|
7
8
|
import time
|
|
8
9
|
import typing
|
|
10
|
+
from contextlib import suppress
|
|
9
11
|
|
|
10
12
|
import paho.mqtt.client as mqtt
|
|
13
|
+
from paho.mqtt.enums import MQTTErrorCode
|
|
11
14
|
|
|
12
15
|
if typing.TYPE_CHECKING:
|
|
13
16
|
from paho.mqtt.client import MQTTMessageInfo
|
|
@@ -73,6 +76,7 @@ class MqttClient(mqtt.Client):
|
|
|
73
76
|
environment: Environment,
|
|
74
77
|
client_id: str | None = None,
|
|
75
78
|
protocol: MQTTProtocolVersion = mqtt.MQTTv311,
|
|
79
|
+
use_loop_selectors: bool = False,
|
|
76
80
|
**kwargs,
|
|
77
81
|
):
|
|
78
82
|
"""Initializes a paho.mqtt.Client for use in Locust swarms.
|
|
@@ -120,6 +124,8 @@ class MqttClient(mqtt.Client):
|
|
|
120
124
|
self._publish_requests: dict[int, PublishedMessageContext] = {}
|
|
121
125
|
self._subscribe_requests: dict[int, tuple[int, str, float]] = {}
|
|
122
126
|
|
|
127
|
+
self._use_loop_selectors = use_loop_selectors
|
|
128
|
+
|
|
123
129
|
def _generate_event_name(self, event_type: str, qos: int, topic: str):
|
|
124
130
|
return _generate_mqtt_event_name(event_type, qos, topic)
|
|
125
131
|
|
|
@@ -323,6 +329,93 @@ class MqttClient(mqtt.Client):
|
|
|
323
329
|
) -> None:
|
|
324
330
|
self._on_connect_cb(client, userdata, {}, reasoncode)
|
|
325
331
|
|
|
332
|
+
def _loop(self, timeout: float = 1.0) -> MQTTErrorCode:
|
|
333
|
+
"""Override the parent's _loop method to optionally use selectors.
|
|
334
|
+
|
|
335
|
+
When use_loop_selectors is True, this uses a selector-based implementation that allows more than 340 connections.
|
|
336
|
+
Otherwise, it falls back to the parent's implementation.
|
|
337
|
+
"""
|
|
338
|
+
if self._use_loop_selectors:
|
|
339
|
+
return self._loop_selectors(timeout)
|
|
340
|
+
else:
|
|
341
|
+
return super()._loop(timeout)
|
|
342
|
+
|
|
343
|
+
def _loop_selectors(self, timeout: float = 1.0) -> MQTTErrorCode:
|
|
344
|
+
if timeout < 0.0:
|
|
345
|
+
raise ValueError("Invalid timeout.")
|
|
346
|
+
|
|
347
|
+
sel = selectors.DefaultSelector()
|
|
348
|
+
|
|
349
|
+
eventmask = selectors.EVENT_READ
|
|
350
|
+
|
|
351
|
+
with suppress(IndexError):
|
|
352
|
+
packet = self._out_packet.popleft()
|
|
353
|
+
self._out_packet.appendleft(packet)
|
|
354
|
+
eventmask = selectors.EVENT_WRITE | eventmask
|
|
355
|
+
|
|
356
|
+
# used to check if there are any bytes left in the (SSL) socket
|
|
357
|
+
pending_bytes = 0
|
|
358
|
+
if hasattr(self._sock, "pending"):
|
|
359
|
+
pending_bytes = self._sock.pending() # type: ignore
|
|
360
|
+
|
|
361
|
+
# if bytes are pending do not wait in select
|
|
362
|
+
if pending_bytes > 0:
|
|
363
|
+
timeout = 0.0
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
if self._sockpairR is None:
|
|
367
|
+
sel.register(self._sock, eventmask) # type: ignore
|
|
368
|
+
else:
|
|
369
|
+
sel.register(self._sock, eventmask) # type: ignore
|
|
370
|
+
sel.register(self._sockpairR, selectors.EVENT_READ)
|
|
371
|
+
|
|
372
|
+
events = sel.select(timeout)
|
|
373
|
+
|
|
374
|
+
except TypeError:
|
|
375
|
+
# Socket isn't correct type, in likelihood connection is lost
|
|
376
|
+
return mqtt.MQTT_ERR_CONN_LOST
|
|
377
|
+
except ValueError:
|
|
378
|
+
# Can occur if we just reconnected but rlist/wlist contain a -1 for
|
|
379
|
+
# some reason.
|
|
380
|
+
return mqtt.MQTT_ERR_CONN_LOST
|
|
381
|
+
except Exception:
|
|
382
|
+
# Note that KeyboardInterrupt, etc. can still terminate since they
|
|
383
|
+
# are not derived from Exception
|
|
384
|
+
return mqtt.MQTT_ERR_UNKNOWN
|
|
385
|
+
|
|
386
|
+
socklist: list[list] = [[], []]
|
|
387
|
+
|
|
388
|
+
for key, _event in events:
|
|
389
|
+
if key.events & selectors.EVENT_READ:
|
|
390
|
+
socklist[0].append(key.fileobj)
|
|
391
|
+
|
|
392
|
+
if key.events & selectors.EVENT_WRITE:
|
|
393
|
+
socklist[1].append(key.fileobj)
|
|
394
|
+
|
|
395
|
+
if self._sock in socklist[0] or pending_bytes > 0:
|
|
396
|
+
rc = self.loop_read()
|
|
397
|
+
if rc or self._sock is None:
|
|
398
|
+
return rc
|
|
399
|
+
|
|
400
|
+
if self._sockpairR and self._sockpairR in socklist[0]:
|
|
401
|
+
# Stimulate output write even though we didn't ask for it, because
|
|
402
|
+
# at that point the publish or other command wasn't present.
|
|
403
|
+
socklist[1].insert(0, self._sock)
|
|
404
|
+
# Clear sockpairR - only ever a single byte written.
|
|
405
|
+
with suppress(BlockingIOError):
|
|
406
|
+
# Read many bytes at once - this allows up to 10000 calls to
|
|
407
|
+
# publish() inbetween calls to loop().
|
|
408
|
+
self._sockpairR.recv(10000)
|
|
409
|
+
|
|
410
|
+
if self._sock in socklist[1]:
|
|
411
|
+
rc = self.loop_write()
|
|
412
|
+
if rc or self._sock is None:
|
|
413
|
+
return rc
|
|
414
|
+
|
|
415
|
+
sel.close()
|
|
416
|
+
|
|
417
|
+
return self.loop_misc()
|
|
418
|
+
|
|
326
419
|
def publish(
|
|
327
420
|
self,
|
|
328
421
|
topic: str,
|
|
@@ -433,6 +526,7 @@ class MqttUser(User):
|
|
|
433
526
|
username = None
|
|
434
527
|
password = None
|
|
435
528
|
protocol = mqtt.MQTTv311
|
|
529
|
+
use_loop_selectors: bool = False
|
|
436
530
|
|
|
437
531
|
def __init__(self, environment: Environment):
|
|
438
532
|
super().__init__(environment)
|
|
@@ -441,6 +535,7 @@ class MqttUser(User):
|
|
|
441
535
|
transport=self.transport,
|
|
442
536
|
client_id=self.client_id,
|
|
443
537
|
protocol=self.protocol,
|
|
538
|
+
use_loop_selectors=self.use_loop_selectors,
|
|
444
539
|
)
|
|
445
540
|
|
|
446
541
|
if self.tls_context:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|