locust 2.29.1.dev10__py3-none-any.whl → 2.29.1.dev34__py3-none-any.whl

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/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.29.1.dev10'
16
- __version_tuple__ = version_tuple = (2, 29, 1, 'dev10')
15
+ __version__ = version = '2.29.1.dev34'
16
+ __version_tuple__ = version_tuple = (2, 29, 1, 'dev34')
locust/clients.py CHANGED
@@ -2,12 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  import time
5
- from collections.abc import Generator
6
5
  from contextlib import contextmanager
6
+ from typing import TYPE_CHECKING
7
7
  from urllib.parse import urlparse, urlunparse
8
8
 
9
9
  import requests
10
- from requests import Request, Response
10
+ from requests import Response
11
11
  from requests.adapters import HTTPAdapter
12
12
  from requests.auth import HTTPBasicAuth
13
13
  from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema, RequestException
@@ -15,12 +15,49 @@ from urllib3 import PoolManager
15
15
 
16
16
  from .exception import CatchResponseError, LocustError, ResponseError
17
17
 
18
+ if TYPE_CHECKING:
19
+ import sys
20
+ from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping
21
+ from typing import Any, TypedDict
22
+
23
+ from requests.cookies import RequestsCookieJar
24
+
25
+ if sys.version_info >= (3, 11):
26
+ from typing import Unpack
27
+ else:
28
+ from typing_extensions import Unpack
29
+
30
+ # Annotations below were generated using output from mypy.
31
+ # Mypy underneath uses information from the https://github.com/python/typeshed repo.
32
+
33
+ class RequestKwargs(TypedDict, total=False):
34
+ params: Any | None # simplified signature
35
+ headers: Mapping[str, str | bytes | None] | None
36
+ cookies: RequestsCookieJar | MutableMapping[str, str] | None
37
+ files: Any | None # simplified signature
38
+ auth: Any | None # simplified signature
39
+ timeout: float | tuple[float, float] | tuple[float, None] | None
40
+ allow_redirects: bool
41
+ proxies: MutableMapping[str, str] | None
42
+ hooks: Mapping[str, Iterable[Callable[[Response], Any]] | Callable[[Response], Any]] | None
43
+ stream: bool | None
44
+ verify: bool | str | None
45
+ cert: str | tuple[str, str] | None
46
+
47
+ class RESTKwargs(RequestKwargs, total=False):
48
+ name: str | None
49
+ catch_response: bool
50
+ context: dict
51
+
52
+
18
53
  absolute_http_url_regexp = re.compile(r"^https?://", re.I)
19
54
 
20
55
 
21
56
  class LocustResponse(Response):
22
- def raise_for_status(self):
23
- if hasattr(self, "error") and self.error:
57
+ error: Exception | None = None
58
+
59
+ def raise_for_status(self) -> None:
60
+ if self.error:
24
61
  raise self.error
25
62
  Response.raise_for_status(self)
26
63
 
@@ -77,7 +114,7 @@ class HttpSession(requests.Session):
77
114
  self.mount("https://", LocustHttpAdapter(pool_manager=pool_manager))
78
115
  self.mount("http://", LocustHttpAdapter(pool_manager=pool_manager))
79
116
 
80
- def _build_url(self, path):
117
+ def _build_url(self, path) -> str:
81
118
  """prepend url with hostname unless it's already an absolute URL"""
82
119
  if absolute_http_url_regexp.match(path):
83
120
  return path
@@ -94,7 +131,18 @@ class HttpSession(requests.Session):
94
131
  finally:
95
132
  self.request_name = None
96
133
 
97
- def request(self, method, url, name=None, catch_response=False, context={}, **kwargs):
134
+ def request( # type: ignore[override]
135
+ self,
136
+ method: str | bytes,
137
+ url: str | bytes,
138
+ name: str | None = None,
139
+ catch_response: bool = False,
140
+ context: dict = {},
141
+ *,
142
+ data: Any | None = None,
143
+ json: Any | None = None,
144
+ **kwargs: Unpack[RequestKwargs],
145
+ ):
98
146
  """
99
147
  Constructs and sends a :py:class:`requests.Request`.
100
148
  Returns :py:class:`requests.Response` object.
@@ -108,7 +156,8 @@ class HttpSession(requests.Session):
108
156
  response, even if the response code is ok (2xx). The opposite also works, one can use catch_response to catch a request
109
157
  and then mark it as successful even if the response code was not (i.e 500 or 404).
110
158
  :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
111
- :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
159
+ :param data: (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the :class:`Request`.
160
+ :param json: (optional) json to send in the body of the :class:`Request`.
112
161
  :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
113
162
  :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
114
163
  :param files: (optional) Dictionary of ``'filename': file-like-objects`` for multipart encoding upload.
@@ -117,9 +166,17 @@ class HttpSession(requests.Session):
117
166
  :type timeout: float or tuple
118
167
  :param allow_redirects: (optional) Set to True by default.
119
168
  :type allow_redirects: bool
120
- :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
169
+ :param proxies: (optional) Dictionary mapping protocol or protocol and hostname to the URL of the proxy.
170
+ :param hooks: (optional) Dictionary mapping hook name to one event or list of events, event must be callable.
121
171
  :param stream: (optional) whether to immediately download the response content. Defaults to ``False``.
122
- :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
172
+ :param verify: (optional) Either a boolean, in which case it controls whether we verify
173
+ the server's TLS certificate, or a string, in which case it must be a path
174
+ to a CA bundle to use. Defaults to ``True``. When set to
175
+ ``False``, requests will accept any TLS certificate presented by
176
+ the server, and will ignore hostname mismatches and/or expired
177
+ certificates, which will make your application vulnerable to
178
+ man-in-the-middle (MitM) attacks. Setting verify to ``False``
179
+ may be useful during local development or testing.
123
180
  :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
124
181
  """
125
182
 
@@ -132,11 +189,11 @@ class HttpSession(requests.Session):
132
189
 
133
190
  start_time = time.time()
134
191
  start_perf_counter = time.perf_counter()
135
- response = self._send_request_safe_mode(method, url, **kwargs)
192
+ response = self._send_request_safe_mode(method, url, data=data, json=json, **kwargs)
136
193
  response_time = (time.perf_counter() - start_perf_counter) * 1000
137
194
 
138
195
  request_before_redirect = (response.history and response.history[0] or response).request
139
- url = request_before_redirect.url
196
+ url = request_before_redirect.url # type: ignore
140
197
 
141
198
  if not name:
142
199
  name = request_before_redirect.path_url
@@ -170,7 +227,7 @@ class HttpSession(requests.Session):
170
227
  pass
171
228
  return response
172
229
 
173
- def _send_request_safe_mode(self, method, url, **kwargs):
230
+ def _send_request_safe_mode(self, method, url, **kwargs) -> Response | LocustResponse:
174
231
  """
175
232
  Send an HTTP request, and catch any exception that might occur due to connection problems.
176
233
 
@@ -183,10 +240,84 @@ class HttpSession(requests.Session):
183
240
  except RequestException as e:
184
241
  r = LocustResponse()
185
242
  r.error = e
186
- r.status_code = 0 # with this status_code, content returns None
187
- r.request = Request(method, url).prepare()
243
+ r.status_code = 0
244
+ r.request = e.request # type: ignore
188
245
  return r
189
246
 
247
+ def get(
248
+ self, url: str | bytes, *, data: Any | None = None, json: Any | None = None, **kwargs: Unpack[RESTKwargs]
249
+ ) -> ResponseContextManager | Response | LocustResponse:
250
+ """Sends a GET request"""
251
+ kwargs.setdefault("allow_redirects", True)
252
+ return self.request("GET", url, data=data, json=json, **kwargs)
253
+
254
+ def options(
255
+ self,
256
+ url: str | bytes,
257
+ *,
258
+ data: Any | None = None,
259
+ json: Any | None = None,
260
+ **kwargs: Unpack[RESTKwargs],
261
+ ) -> ResponseContextManager | Response | LocustResponse:
262
+ """Sends a OPTIONS request"""
263
+ kwargs.setdefault("allow_redirects", True)
264
+ return self.request("OPTIONS", url, data=data, json=json, **kwargs)
265
+
266
+ def head(
267
+ self,
268
+ url: str | bytes,
269
+ *,
270
+ data: Any | None = None,
271
+ json: Any | None = None,
272
+ **kwargs: Unpack[RESTKwargs],
273
+ ) -> ResponseContextManager | Response | LocustResponse:
274
+ """Sends a HEAD request"""
275
+ kwargs.setdefault("allow_redirects", False)
276
+ return self.request("HEAD", url, data=data, json=json, **kwargs)
277
+
278
+ def post(
279
+ self,
280
+ url: str | bytes,
281
+ data: Any | None = None,
282
+ json: Any | None = None,
283
+ **kwargs: Unpack[RESTKwargs],
284
+ ) -> ResponseContextManager | Response | LocustResponse:
285
+ """Sends a POST request"""
286
+ return self.request("POST", url, data=data, json=json, **kwargs)
287
+
288
+ def put(
289
+ self,
290
+ url: str | bytes,
291
+ data: Any | None = None,
292
+ *,
293
+ json: Any | None = None,
294
+ **kwargs: Unpack[RESTKwargs],
295
+ ) -> ResponseContextManager | Response | LocustResponse:
296
+ """Sends a PUT request"""
297
+ return self.request("PUT", url, data=data, json=json, **kwargs)
298
+
299
+ def patch(
300
+ self,
301
+ url: str | bytes,
302
+ data: Any | None = None,
303
+ *,
304
+ json: Any | None = None,
305
+ **kwargs: Unpack[RESTKwargs],
306
+ ) -> ResponseContextManager | Response | LocustResponse:
307
+ """Sends a PATCH request"""
308
+ return self.request("PATCH", url, data=data, json=json, **kwargs)
309
+
310
+ def delete(
311
+ self,
312
+ url: str | bytes,
313
+ *,
314
+ data: Any | None = None,
315
+ json: Any | None = None,
316
+ **kwargs: Unpack[RESTKwargs],
317
+ ) -> ResponseContextManager | Response | LocustResponse:
318
+ """Sends a DELETE request"""
319
+ return self.request("DELETE", url, data=data, json=json, **kwargs)
320
+
190
321
 
191
322
  class ResponseContextManager(LocustResponse):
192
323
  """
@@ -211,7 +342,7 @@ class ResponseContextManager(LocustResponse):
211
342
  self._entered = True
212
343
  return self
213
344
 
214
- def __exit__(self, exc, value, traceback):
345
+ def __exit__(self, exc, value, traceback): # type: ignore[override]
215
346
  # if the user has already manually marked this response as failure or success
216
347
  # we can ignore the default behaviour of letting the response code determine the outcome
217
348
  if self._manual_result is not None:
@@ -311,17 +442,11 @@ class LocustHttpAdapter(HTTPAdapter):
311
442
 
312
443
 
313
444
  # Monkey patch Response class to give some guidance
314
- def _success(self):
315
- raise LocustError(
316
- "If you want to change the state of the request, you must pass catch_response=True. See http://docs.locust.io/en/stable/writing-a-locustfile.html#validating-responses"
317
- )
318
-
319
-
320
- def _failure(self):
445
+ def _missing_catch_response_True(self, *_args, **_kwargs):
321
446
  raise LocustError(
322
- "If you want to change the state of the request, you must pass catch_response=True. See http://docs.locust.io/en/stable/writing-a-locustfile.html#validating-responses"
447
+ "If you want to change the state of the request using .success() or .failure(), you must pass catch_response=True. See http://docs.locust.io/en/stable/writing-a-locustfile.html#validating-responses"
323
448
  )
324
449
 
325
450
 
326
- Response.success = _success # type: ignore[attr-defined]
327
- Response.failure = _failure # type: ignore[attr-defined]
451
+ Response.success = _missing_catch_response_True # type: ignore[attr-defined]
452
+ Response.failure = _missing_catch_response_True # type: ignore[attr-defined]
@@ -324,6 +324,12 @@ class FastHttpUser(User):
324
324
  Note that setting this value has no effect when custom client_pool was given, and you need to spawn a your own gevent pool
325
325
  to use it (as Users only have one greenlet). See test_fasthttp.py / test_client_pool_concurrency for an example."""
326
326
 
327
+ proxy_host: str | None = None
328
+ """Parameter passed to FastHttpSession"""
329
+
330
+ proxy_port: int | None = None
331
+ """Parameter passed to FastHttpSession"""
332
+
327
333
  client_pool: HTTPClientPool | None = None
328
334
  """HTTP client pool to use. If not given, a new pool is created per single user."""
329
335
 
@@ -355,6 +361,8 @@ class FastHttpUser(User):
355
361
  client_pool=self.client_pool,
356
362
  ssl_context_factory=self.ssl_context_factory,
357
363
  headers=self.default_headers,
364
+ proxy_host=self.proxy_host,
365
+ proxy_port=self.proxy_port,
358
366
  )
359
367
  """
360
368
  Instance of HttpSession that is created upon instantiation of User.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: locust
3
- Version: 2.29.1.dev10
3
+ Version: 2.29.1.dev34
4
4
  Summary: Developer-friendly load testing framework
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/locustio/locust
@@ -39,6 +39,7 @@ Requires-Dist: Flask-Login >=0.6.3
39
39
  Requires-Dist: Flask-Cors >=3.0.10
40
40
  Requires-Dist: pywin32 ; platform_system == "Windows"
41
41
  Requires-Dist: tomli >=1.1.0 ; python_version < "3.11"
42
+ Requires-Dist: typing-extensions >=4.6.0 ; python_version < "3.11"
42
43
  Requires-Dist: requests >=2.32.2 ; python_version > "3.11"
43
44
 
44
45
  # Locust
@@ -1,8 +1,8 @@
1
1
  locust/__init__.py,sha256=Hmw2vNf75eLQ1mQIPXAwlQrJ_XFY65MOb92fGsNCukQ,1458
2
2
  locust/__main__.py,sha256=vBQ82334kX06ImDbFlPFgiBRiLIinwNk3z8Khs6hd74,31
3
- locust/_version.py,sha256=MIiWo8hUHI5FT3CkPQhbABB0HIF2NSxKuXtteNfmn-8,428
3
+ locust/_version.py,sha256=2F1cntyJqemK99KHeQVA-RUjqJ8W7rT7FyU0O5becYk,428
4
4
  locust/argument_parser.py,sha256=sjQoJ1NTac9LdNYT7zn8RajlWqBQs8YFNv6uRExb2gg,28941
5
- locust/clients.py,sha256=YKuAyMAbxs8_-w7XJw0hc67KFBNNLxibsw6FwiS01Q8,14781
5
+ locust/clients.py,sha256=OHPv6hBAt4gt3HI67yqyT1qrSsF8uMdCwIRu0kIsRWI,19491
6
6
  locust/debug.py,sha256=We6Z9W0btkKSc7PxWmrZx-xMynvOOsKhG6jmDgQin0g,5134
7
7
  locust/dispatch.py,sha256=vYh0QEDFgJ3hY0HgSk-EiNO7IP9ffzXF_Et8wB9JvsI,16995
8
8
  locust/env.py,sha256=sP-fCnZs0e2xodRemLHgTgyyUt5eezwtdA9WsCoqJkQ,12767
@@ -18,7 +18,7 @@ locust/shape.py,sha256=t-lwBS8LOjWcKXNL7j2U3zroIXJ1b0fazUwpRYQOKXw,1973
18
18
  locust/stats.py,sha256=5jx9aD9Sky-kCZ3E-MjRT3xbwvxo9xyDtfcfP56zclo,45875
19
19
  locust/web.py,sha256=rN1NVeZ9LKSEeDwvpRbOJ0bcy8U1U4VjP-7vK7ejlwM,27367
20
20
  locust/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- locust/contrib/fasthttp.py,sha256=vByPepw35DF84qZ2xK89yHgjOA_8btV0wig_rSR6p4g,26757
21
+ locust/contrib/fasthttp.py,sha256=n6arQP1IH3CVP8PUaIhD-X5xWMAOj9F0JEJXPklun0Y,26999
22
22
  locust/rpc/__init__.py,sha256=nVGoHWFQxZjnhCDWjbgXIbmFbN9sizAjkhvSs9_642c,58
23
23
  locust/rpc/protocol.py,sha256=n-rb3GZQcAlldYDj4E4GuFGylYj_26GSS5U29meft5Y,1282
24
24
  locust/rpc/zmqrpc.py,sha256=AAY6w7wSFHsW34qqN28666boHFf6dTSAXPQJnAO6iUI,2707
@@ -71,9 +71,9 @@ locust/webui/dist/report.html,sha256=sOdZZVgZbqgu86BBCSQf3uQUYXgmgSnXF32JpnyAII8
71
71
  locust/webui/dist/assets/favicon.ico,sha256=IUl-rYqfpHdV38e-s0bkmFIeLS-n3Ug0DQxk-h202hI,8348
72
72
  locust/webui/dist/assets/index-84c63e70.js,sha256=cwyH4ju0OCRvFhg3O-0SYVBTFlN_XQeQ6YkymAO4Hco,1647185
73
73
  locust/webui/dist/assets/logo.png,sha256=EIVPqr6wE_yqguHaqFHIsH0ZACLSrvNWyYO7PbyIj4w,19299
74
- locust-2.29.1.dev10.dist-info/LICENSE,sha256=78XGpIn3fHVBfaxlPNUfjVufSN7QsdhpJMRJHv2AFpo,1095
75
- locust-2.29.1.dev10.dist-info/METADATA,sha256=Tt43tTkczmIluw-UW10AjlhdnT2Yd341W7Z1ms4maF8,7323
76
- locust-2.29.1.dev10.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
77
- locust-2.29.1.dev10.dist-info/entry_points.txt,sha256=RAdt8Ku-56m7bFjmdj-MBhbF6h4NX7tVODR9QNnOg0E,44
78
- locust-2.29.1.dev10.dist-info/top_level.txt,sha256=XSsjgPA8Ggf9TqKVbkwSqZFuPlZ085X13M9orDycE20,7
79
- locust-2.29.1.dev10.dist-info/RECORD,,
74
+ locust-2.29.1.dev34.dist-info/LICENSE,sha256=78XGpIn3fHVBfaxlPNUfjVufSN7QsdhpJMRJHv2AFpo,1095
75
+ locust-2.29.1.dev34.dist-info/METADATA,sha256=e5FxTmyepgg_kDpLKwq-OgLmi70r3ykCX4NxCfh4YU4,7390
76
+ locust-2.29.1.dev34.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
77
+ locust-2.29.1.dev34.dist-info/entry_points.txt,sha256=RAdt8Ku-56m7bFjmdj-MBhbF6h4NX7tVODR9QNnOg0E,44
78
+ locust-2.29.1.dev34.dist-info/top_level.txt,sha256=XSsjgPA8Ggf9TqKVbkwSqZFuPlZ085X13M9orDycE20,7
79
+ locust-2.29.1.dev34.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (70.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5