locust-cloud 1.16.1__py3-none-any.whl → 1.17.0__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.
@@ -0,0 +1,18 @@
1
+ from locust import events
2
+ from locust_cloud.args import cloud_parser
3
+ from locust_cloud.cloud import main
4
+
5
+ __all__ = ["main"]
6
+
7
+
8
+ @events.init_command_line_parser.add_listener
9
+ def _(parser):
10
+ cloud_group = parser.add_argument_group(
11
+ "Locust Cloud",
12
+ """Launches a distributed Locust run on locust.cloud infrastructure.
13
+
14
+ Example: locust --cloud -f my_locustfile.py --users 1000 ...""",
15
+ )
16
+
17
+ for action in cloud_parser._actions:
18
+ cloud_group._add_action(action)
locust_cloud/args.py CHANGED
@@ -4,8 +4,14 @@ import gzip
4
4
  import io
5
5
  import os
6
6
  import pathlib
7
- import tomllib
8
- from argparse import ArgumentTypeError, Namespace
7
+ import sys
8
+
9
+ if sys.version_info >= (3, 11):
10
+ import tomllib
11
+ else:
12
+ import tomli as tomllib
13
+
14
+ from argparse import ArgumentTypeError
9
15
  from collections import OrderedDict
10
16
  from collections.abc import Callable, Generator
11
17
  from typing import IO, Any, cast
@@ -103,116 +109,54 @@ class MergeToTransferEncodedZip(argparse.Action):
103
109
  setattr(namespace, self.dest, value)
104
110
 
105
111
 
106
- parser = configargparse.ArgumentParser(
107
- default_config_files=[
108
- "~/.locust.conf",
109
- "locust.conf",
110
- "pyproject.toml",
111
- "~/.cloud.conf",
112
- "cloud.conf",
113
- ],
114
- auto_env_var_prefix="LOCUSTCLOUD_",
115
- formatter_class=configargparse.RawTextHelpFormatter,
116
- config_file_parser_class=configargparse.CompositeConfigParser(
117
- [
118
- LocustTomlConfigParser(["tool.locust"]),
119
- configargparse.DefaultConfigFileParser,
120
- ]
121
- ),
122
- description="""Launches a distributed Locust runs on locust.cloud infrastructure.
123
-
124
- Example: locust-cloud -f my_locustfile.py --users 1000 ...""",
125
- epilog="""Any parameters not listed here are forwarded to locust master unmodified, so go ahead and use things like --users, --host, --run-time, ...
126
- Locust config can also be set using config file (~/.locust.conf, locust.conf, pyproject.toml, ~/.cloud.conf or cloud.conf).
127
- Parameters specified on command line override env vars, which in turn override config files.""",
128
- add_config_file_help=False,
129
- add_env_var_help=False,
130
- add_help=False,
131
- )
132
- parser.add_argument(
133
- "-h",
134
- "--help",
135
- action="help",
136
- help=configargparse.SUPPRESS,
137
- )
138
- parser.add_argument(
139
- "-V",
140
- "--version",
112
+ cloud_parser = configargparse.ArgumentParser(add_help=False)
113
+ cloud_parser.add_argument(
114
+ "--cloud",
141
115
  action="store_true",
142
- help=configargparse.SUPPRESS,
143
- )
144
- parser.add_argument(
145
- "-f",
146
- "--locustfile",
147
- metavar="<filename>",
148
- default="locustfile.py",
149
- help="The Python file that contains your test. Defaults to 'locustfile.py'.",
150
- env_var="LOCUST_LOCUSTFILE",
151
- type=transfer_encoded_file,
116
+ help="Run Locust in cloud mode.",
152
117
  )
153
- parser.add_argument(
154
- "-u",
155
- "--users",
156
- type=int,
157
- default=1,
158
- help="Number of users to launch. This is the same as the regular Locust argument, but also affects how many workers to launch.",
159
- env_var="LOCUST_USERS",
118
+ cloud_parser.add_argument(
119
+ "--login",
120
+ action="store_true",
121
+ help="Launch an interactive session to authenticate your user.\nOnce completed your credentials will be stored and automatically refreshed for quite a long time.\nOnce those expires you will be prompted to perform another login.",
160
122
  )
161
- advanced = parser.add_argument_group("advanced")
162
- advanced.add_argument(
163
- "--loglevel",
164
- "-L",
165
- type=str.upper,
166
- help="Set --loglevel DEBUG for extra info.",
167
- choices=["DEBUG", "INFO", "WARNING", "ERROR"],
168
- default="INFO",
123
+ cloud_parser.add_argument(
124
+ "--delete",
125
+ action="store_true",
126
+ help="Delete a running cluster. Useful if locust-cloud was killed/disconnected or if there was an error.",
169
127
  )
170
- advanced.add_argument(
128
+ cloud_parser.add_argument(
171
129
  "--requirements",
130
+ metavar="<filename>",
172
131
  type=transfer_encoded_file,
173
132
  help="Optional requirements.txt file that contains your external libraries.",
174
133
  )
175
- advanced.add_argument(
176
- "--login",
177
- action="store_true",
178
- default=False,
179
- help="Launch an interactive session to authenticate your user.\nOnce completed your credentials will be stored and automatically refreshed for quite a long time.\nOnce those expires you will be prompted to perform another login.",
180
- )
181
- advanced.add_argument(
134
+ cloud_parser.add_argument(
182
135
  "--non-interactive",
183
136
  action="store_true",
184
137
  default=False,
185
138
  help="This can be set when, for example, running in a CI/CD environment to ensure no interactive steps while executing.\nRequires that LOCUSTCLOUD_USERNAME, LOCUSTCLOUD_PASSWORD and LOCUSTCLOUD_REGION environment variables are set.",
186
139
  )
187
- parser.add_argument(
140
+ cloud_parser.add_argument(
188
141
  "--workers",
142
+ metavar="<int>",
189
143
  type=int,
190
144
  help="Number of workers to use for the deployment. Defaults to number of users divided by 500, but the default may be customized for your account.",
191
145
  default=None,
192
146
  )
193
- parser.add_argument(
194
- "--delete",
195
- action="store_true",
196
- help="Delete a running cluster. Useful if locust-cloud was killed/disconnected or if there was an error.",
197
- )
198
- parser.add_argument(
147
+ cloud_parser.add_argument(
199
148
  "--image-tag",
200
149
  type=str,
201
150
  default=None,
202
151
  help=configargparse.SUPPRESS, # overrides the locust-cloud docker image tag. for internal use
203
152
  )
204
- parser.add_argument(
153
+ cloud_parser.add_argument(
205
154
  "--mock-server",
206
155
  action="store_true",
207
156
  default=False,
208
157
  help="Start a demo mock service and set --host parameter to point Locust towards it",
209
158
  )
210
- parser.add_argument(
211
- "--profile",
212
- type=str,
213
- help="Set a profile to group the testruns together",
214
- )
215
- parser.add_argument(
159
+ cloud_parser.add_argument(
216
160
  "--extra-files",
217
161
  action=MergeToTransferEncodedZip,
218
162
  nargs="*",
@@ -220,6 +164,39 @@ parser.add_argument(
220
164
  help="A list of extra files or directories to upload. Space-separated, e.g. --extra-files testdata.csv *.py my-directory/",
221
165
  )
222
166
 
223
-
224
- def parse_known_args(args: Any | None = None) -> tuple[Namespace, list[str]]:
225
- return parser.parse_known_args(args)
167
+ combined_cloud_parser = configargparse.ArgumentParser(
168
+ default_config_files=[
169
+ "~/.cloud.conf",
170
+ "cloud.conf",
171
+ ],
172
+ auto_env_var_prefix="LOCUSTCLOUD_",
173
+ formatter_class=configargparse.RawTextHelpFormatter,
174
+ config_file_parser_class=configargparse.CompositeConfigParser(
175
+ [
176
+ LocustTomlConfigParser(["tool.locust"]),
177
+ configargparse.DefaultConfigFileParser,
178
+ ]
179
+ ),
180
+ parents=[cloud_parser],
181
+ )
182
+ combined_cloud_parser.add_argument(
183
+ "-f",
184
+ "--locustfile",
185
+ default="locustfile.py",
186
+ env_var="LOCUST_LOCUSTFILE",
187
+ type=transfer_encoded_file,
188
+ )
189
+ combined_cloud_parser.add_argument(
190
+ "-u",
191
+ "--users",
192
+ type=int,
193
+ default=1,
194
+ env_var="LOCUST_USERS",
195
+ )
196
+ combined_cloud_parser.add_argument(
197
+ "--loglevel",
198
+ "-L",
199
+ type=str.upper,
200
+ choices=["DEBUG", "INFO", "WARNING", "ERROR"],
201
+ default="INFO",
202
+ )
locust_cloud/cloud.py CHANGED
@@ -1,13 +1,12 @@
1
1
  import logging
2
2
  import os
3
- import sys
4
3
  import time
5
4
  import webbrowser
6
5
  from threading import Thread
7
6
 
8
7
  import requests
9
8
  from locust_cloud.apisession import ApiSession
10
- from locust_cloud.args import parse_known_args
9
+ from locust_cloud.args import combined_cloud_parser
11
10
  from locust_cloud.common import __version__
12
11
  from locust_cloud.input_events import input_listener
13
12
  from locust_cloud.web_login import web_login
@@ -28,30 +27,28 @@ def configure_logging(loglevel: str) -> None:
28
27
  logging.getLogger("urllib3").setLevel(logging.INFO)
29
28
 
30
29
 
31
- def main() -> None:
32
- options, locust_options = parse_known_args()
30
+ def main():
31
+ options, locust_options = combined_cloud_parser.parse_known_args()
32
+
33
33
  configure_logging(options.loglevel)
34
34
 
35
- if options.version:
36
- print(f"locust-cloud version {__version__}")
37
- sys.exit(0)
38
35
  if not options.locustfile:
39
36
  logger.error("A locustfile is required to run a test.")
40
- sys.exit(1)
37
+ return 1
41
38
 
42
39
  if options.login:
43
40
  try:
44
41
  web_login()
45
42
  except KeyboardInterrupt:
46
43
  pass
47
- sys.exit()
44
+ return
48
45
 
49
46
  session = ApiSession(options.non_interactive)
50
47
  websocket = Websocket()
51
48
 
52
49
  if options.delete:
53
50
  delete(session)
54
- sys.exit()
51
+ return
55
52
 
56
53
  try:
57
54
  logger.info(f"Deploying ({session.region}, {__version__})")
@@ -67,12 +64,12 @@ def main() -> None:
67
64
  "LOCUST_SKIP_MONKEY_PATCH",
68
65
  ]
69
66
  ]
67
+
70
68
  payload = {
71
69
  "locust_args": [
72
70
  {"name": "LOCUST_USERS", "value": str(options.users)},
73
71
  {"name": "LOCUST_FLAGS", "value": " ".join(locust_options)},
74
72
  {"name": "LOCUSTCLOUD_DEPLOYER_URL", "value": session.api_url},
75
- {"name": "LOCUSTCLOUD_PROFILE", "value": options.profile},
76
73
  *locust_env_variables,
77
74
  ],
78
75
  "locustfile": options.locustfile,
@@ -107,10 +104,10 @@ def main() -> None:
107
104
  time.sleep(2)
108
105
  except requests.exceptions.RequestException as e:
109
106
  logger.error(f"Failed to deploy the load generators: {e}")
110
- sys.exit(1)
107
+ return 1
111
108
  else:
112
- logger.error("Your Locust instance is still running, run locust-cloud --delete")
113
- sys.exit(1)
109
+ logger.error("Your Locust instance is still running, run locust --cloud --delete")
110
+ return 1
114
111
 
115
112
  if response.status_code != 200:
116
113
  try:
@@ -119,7 +116,7 @@ def main() -> None:
119
116
  logger.error(
120
117
  f"HTTP {response.status_code}/{response.reason} - Response: {response.text} - URL: {response.request.url}"
121
118
  )
122
- sys.exit(1)
119
+ return 1
123
120
 
124
121
  log_ws_url = js["log_ws_url"]
125
122
  session_id = js["session_id"]
@@ -146,19 +143,19 @@ def main() -> None:
146
143
  websocket.wait(timeout=True)
147
144
  except (WebsocketTimeout, SessionMismatchError) as e:
148
145
  logger.error(str(e))
149
- sys.exit(1)
146
+ return 1
150
147
  except WebsocketTimeout as e:
151
148
  logger.error(str(e))
152
149
  delete(session)
153
- sys.exit(1)
150
+ return 1
154
151
  except SessionMismatchError as e:
155
152
  # In this case we do not trigger the teardown since the running instance is not ours
156
153
  logger.error(str(e))
157
- sys.exit(1)
154
+ return 1
158
155
  except Exception as e:
159
156
  logger.exception(e)
160
157
  delete(session)
161
- sys.exit(1)
158
+ return 1
162
159
  else:
163
160
  delete(session)
164
161
  finally:
@@ -183,7 +180,3 @@ def delete(session):
183
180
  pass # don't show nasty callstack
184
181
  except Exception as e:
185
182
  logger.error(f"Could not automatically tear down Locust Cloud: {e.__class__.__name__}:{e}")
186
-
187
-
188
- if __name__ == "__main__":
189
- main()
@@ -1,15 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: locust-cloud
3
- Version: 1.16.1
3
+ Version: 1.17.0
4
4
  Summary: Locust Cloud
5
5
  Project-URL: Homepage, https://locust.cloud
6
- Requires-Python: >=3.11
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
7
8
  Requires-Dist: configargparse==1.7
8
9
  Requires-Dist: gevent>=24.11.1
10
+ Requires-Dist: locust>=2.34.0
9
11
  Requires-Dist: platformdirs>=4.3.6
10
12
  Requires-Dist: pyjwt>=2.0
11
13
  Requires-Dist: python-socketio[client]==5.11.4
12
14
  Requires-Dist: requests==2.32.3
15
+ Requires-Dist: tomli>=1.1.0; python_version < '3.11'
13
16
  Description-Content-Type: text/markdown
14
17
 
15
18
  # Locust Cloud
@@ -0,0 +1,13 @@
1
+ locust_cloud/__init__.py,sha256=NPggKqjx13UKmmgoG6dpoXOyA1liGBAUP8pz6OSpBvI,488
2
+ locust_cloud/apisession.py,sha256=kCr271_l0IeMGw0L563mOecqPJj4OD9h2J3vxCM5zYQ,4015
3
+ locust_cloud/args.py,sha256=oyl6Uv5CV3mTMLos_-3ChVwyHt6ElxLAaanlbQn5SaU,6056
4
+ locust_cloud/cloud.py,sha256=RbW7p4pQWIcvaYe_80BwNiBC8YHmrX-1tE5X8Bi0dH0,5912
5
+ locust_cloud/common.py,sha256=cFrDVKpi9OEmH6giOuj9HoIUFSBArixNtNHzZIgDvPE,992
6
+ locust_cloud/input_events.py,sha256=MyxccgboHByICuK6VpQCCJhZQqTZAacNmkSpw-gxBEw,3420
7
+ locust_cloud/web_login.py,sha256=1j2AQoEM6XVSDtE1q0Ryrs4jFEx07r9IQfZCoFAQXJg,2400
8
+ locust_cloud/websocket.py,sha256=9Q7nTFuAwVhgW74DlJNcHTZXOQ1drsXi8hX9ciZhWlQ,8998
9
+ locust_cloud-1.17.0.dist-info/METADATA,sha256=HTj3BaXSF4pqcuJB9Y83M0F2rgonl3sXMzCzYm8HmXs,633
10
+ locust_cloud-1.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ locust_cloud-1.17.0.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
12
+ locust_cloud-1.17.0.dist-info/licenses/LICENSE,sha256=Ow6fY6ta4KIjdlWalmxGvRP8yLmetvkbkl-SdHMjPIs,1093
13
+ locust_cloud-1.17.0.dist-info/RECORD,,
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2024-present, Locust Technologies Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -1,11 +0,0 @@
1
- locust_cloud/apisession.py,sha256=kCr271_l0IeMGw0L563mOecqPJj4OD9h2J3vxCM5zYQ,4015
2
- locust_cloud/args.py,sha256=4U_bZaEH1UJR8C7_bH_sX1DRB8m6WBcJh-ewhilFQX0,7118
3
- locust_cloud/cloud.py,sha256=kih2Qtddry0dxmGylxXQdrw4SPTwx6k6YN0ur3u47Qc,6144
4
- locust_cloud/common.py,sha256=cFrDVKpi9OEmH6giOuj9HoIUFSBArixNtNHzZIgDvPE,992
5
- locust_cloud/input_events.py,sha256=MyxccgboHByICuK6VpQCCJhZQqTZAacNmkSpw-gxBEw,3420
6
- locust_cloud/web_login.py,sha256=1j2AQoEM6XVSDtE1q0Ryrs4jFEx07r9IQfZCoFAQXJg,2400
7
- locust_cloud/websocket.py,sha256=9Q7nTFuAwVhgW74DlJNcHTZXOQ1drsXi8hX9ciZhWlQ,8998
8
- locust_cloud-1.16.1.dist-info/METADATA,sha256=4EHCv-Hpo9NR-IThvTMiJR9t5EWfdjARKBt2V2QApBQ,528
9
- locust_cloud-1.16.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- locust_cloud-1.16.1.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
11
- locust_cloud-1.16.1.dist-info/RECORD,,