locust-cloud 1.16.0__py3-none-any.whl → 1.17.0.dev0__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)
@@ -66,7 +66,7 @@ class ApiSession(requests.Session):
66
66
  self.headers["X-Client-Version"] = __version__
67
67
 
68
68
  def __configure_for_region(self, region: str) -> None:
69
- self.__region = region
69
+ self.region = region
70
70
  self.api_url = get_api_url(region)
71
71
  self.__login_url = f"{self.api_url}/auth/login"
72
72
 
@@ -76,8 +76,6 @@ class ApiSession(requests.Session):
76
76
  if self.__expiry_time > time.time():
77
77
  return
78
78
 
79
- logger.info(f"Authenticating ({self.__region}, v{__version__})")
80
-
81
79
  response = requests.post(
82
80
  self.__login_url,
83
81
  json={"user_sub_id": self.__sub, "refresh_token": self.__refresh_token},
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,11 +109,8 @@ class MergeToTransferEncodedZip(argparse.Action):
103
109
  setattr(namespace, self.dest, value)
104
110
 
105
111
 
106
- parser = configargparse.ArgumentParser(
112
+ cloud_parser = configargparse.ArgumentParser(
107
113
  default_config_files=[
108
- "~/.locust.conf",
109
- "locust.conf",
110
- "pyproject.toml",
111
114
  "~/.cloud.conf",
112
115
  "cloud.conf",
113
116
  ],
@@ -119,100 +122,55 @@ parser = configargparse.ArgumentParser(
119
122
  configargparse.DefaultConfigFileParser,
120
123
  ]
121
124
  ),
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
125
  add_help=False,
131
126
  )
132
- parser.add_argument(
133
- "-h",
134
- "--help",
135
- action="help",
136
- help=configargparse.SUPPRESS,
137
- )
138
- parser.add_argument(
139
- "-V",
140
- "--version",
127
+ cloud_parser.add_argument(
128
+ "--cloud",
141
129
  action="store_true",
142
- help=configargparse.SUPPRESS,
130
+ help="Run Locust in cloud mode.",
143
131
  )
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,
152
- )
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",
132
+ cloud_parser.add_argument(
133
+ "--login",
134
+ action="store_true",
135
+ 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
136
  )
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",
137
+ cloud_parser.add_argument(
138
+ "--delete",
139
+ action="store_true",
140
+ help="Delete a running cluster. Useful if locust-cloud was killed/disconnected or if there was an error.",
169
141
  )
170
- advanced.add_argument(
142
+ cloud_parser.add_argument(
171
143
  "--requirements",
144
+ metavar="<filename>",
172
145
  type=transfer_encoded_file,
173
146
  help="Optional requirements.txt file that contains your external libraries.",
174
147
  )
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(
148
+ cloud_parser.add_argument(
182
149
  "--non-interactive",
183
150
  action="store_true",
184
151
  default=False,
185
152
  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
153
  )
187
- parser.add_argument(
154
+ cloud_parser.add_argument(
188
155
  "--workers",
156
+ metavar="<int>",
189
157
  type=int,
190
158
  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
159
  default=None,
192
160
  )
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(
161
+ cloud_parser.add_argument(
199
162
  "--image-tag",
200
163
  type=str,
201
164
  default=None,
202
165
  help=configargparse.SUPPRESS, # overrides the locust-cloud docker image tag. for internal use
203
166
  )
204
- parser.add_argument(
167
+ cloud_parser.add_argument(
205
168
  "--mock-server",
206
169
  action="store_true",
207
170
  default=False,
208
171
  help="Start a demo mock service and set --host parameter to point Locust towards it",
209
172
  )
210
- parser.add_argument(
211
- "--profile",
212
- type=str,
213
- help="Set a profile to group the testruns together",
214
- )
215
- parser.add_argument(
173
+ cloud_parser.add_argument(
216
174
  "--extra-files",
217
175
  action=MergeToTransferEncodedZip,
218
176
  nargs="*",
@@ -220,6 +178,25 @@ parser.add_argument(
220
178
  help="A list of extra files or directories to upload. Space-separated, e.g. --extra-files testdata.csv *.py my-directory/",
221
179
  )
222
180
 
223
-
224
- def parse_known_args(args: Any | None = None) -> tuple[Namespace, list[str]]:
225
- return parser.parse_known_args(args)
181
+ combined_cloud_parser = configargparse.ArgumentParser(parents=[cloud_parser])
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,33 +27,31 @@ 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
- logger.info("Deploying load generators")
54
+ logger.info(f"Deploying ({session.region}, {__version__})")
58
55
  locust_env_variables = [
59
56
  {"name": env_variable, "value": os.environ[env_variable]}
60
57
  for env_variable in os.environ
@@ -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:
@@ -179,9 +176,7 @@ def delete(session):
179
176
  logger.info(
180
177
  f"Could not automatically tear down Locust Cloud: HTTP {response.status_code}/{response.reason} - Response: {response.text} - URL: {response.request.url}"
181
178
  )
179
+ except KeyboardInterrupt:
180
+ pass # don't show nasty callstack
182
181
  except Exception as e:
183
182
  logger.error(f"Could not automatically tear down Locust Cloud: {e.__class__.__name__}:{e}")
184
-
185
-
186
- if __name__ == "__main__":
187
- main()
@@ -1,15 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: locust-cloud
3
- Version: 1.16.0
3
+ Version: 1.17.0.dev0
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=zf8Cowg1aMnDdPkujKDlMHYUjvadF_wPVkxOxt2fQpw,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.dev0.dist-info/METADATA,sha256=bvZTd_asbLJ9xxS7Bsx7-Xr2pKGvsJ-lsi3p3HJuUUA,638
10
+ locust_cloud-1.17.0.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ locust_cloud-1.17.0.dev0.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
12
+ locust_cloud-1.17.0.dev0.dist-info/licenses/LICENSE,sha256=Ow6fY6ta4KIjdlWalmxGvRP8yLmetvkbkl-SdHMjPIs,1093
13
+ locust_cloud-1.17.0.dev0.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=AoU0FGQbyH2qbaTmdyoIMBd_lwZimLtihK0gnpAV6c0,4091
2
- locust_cloud/args.py,sha256=4U_bZaEH1UJR8C7_bH_sX1DRB8m6WBcJh-ewhilFQX0,7118
3
- locust_cloud/cloud.py,sha256=3sTw5FSLZBtZkdzHj9VisbAUe_jmQH7yOR7oytFEO-M,6052
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.0.dist-info/METADATA,sha256=ZKDDbnD72lqnGEgZ-89MfsSJbSvApn_VH2Co7DpX2Yk,528
9
- locust_cloud-1.16.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- locust_cloud-1.16.0.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
11
- locust_cloud-1.16.0.dist-info/RECORD,,