locust-cloud 1.20.3__py3-none-any.whl → 1.20.4__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_cloud/__init__.py CHANGED
@@ -1,26 +1,3 @@
1
- from locust import events
2
- from locust_cloud.args import cloud_parser
3
1
  from locust_cloud.cloud import main
4
2
 
5
3
  __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
- # This arguments is defined here because only makes sense when
18
- # running from locust core
19
- cloud_group.add_argument(
20
- "--cloud",
21
- action="store_true",
22
- help="Run Locust in cloud mode.",
23
- )
24
-
25
- for action in cloud_parser._actions:
26
- cloud_group._add_action(action)
@@ -3,7 +3,6 @@ import os
3
3
  import sys
4
4
  import time
5
5
 
6
- import jwt
7
6
  import requests
8
7
  from locust_cloud.common import VALID_REGIONS, __version__, get_api_url, read_cloud_config, write_cloud_config
9
8
 
@@ -42,7 +41,8 @@ class ApiSession(requests.Session):
42
41
 
43
42
  self.__refresh_token = response.json()["refresh_token"]
44
43
  id_token = response.json()["cognito_client_id_token"]
45
-
44
+ user_sub_id = response.json()["user_sub_id"]
45
+ id_token_expires = response.json()["id_token_expires"]
46
46
  else:
47
47
  config = read_cloud_config()
48
48
 
@@ -55,14 +55,14 @@ class ApiSession(requests.Session):
55
55
  self.__configure_for_region(config.region)
56
56
  self.__refresh_token = config.refresh_token
57
57
  id_token = config.id_token
58
+ user_sub_id = config.user_sub_id
59
+ id_token_expires = config.id_token_expires
58
60
 
59
61
  assert id_token
60
62
 
61
- decoded = jwt.decode(id_token, options={"verify_signature": False})
62
- self.__expiry_time = decoded["exp"] - 60 # Refresh 1 minute before expiry
63
+ self.__user_sub_id = user_sub_id
64
+ self.__id_token_expires = id_token_expires - 60 # Refresh 1 minute before expiry
63
65
  self.headers["Authorization"] = f"Bearer {id_token}"
64
-
65
- self.__sub = decoded["sub"]
66
66
  self.headers["X-Client-Version"] = __version__
67
67
 
68
68
  def __configure_for_region(self, region: str) -> None:
@@ -73,12 +73,12 @@ class ApiSession(requests.Session):
73
73
  logger.debug(f"Lambda url: {self.api_url}")
74
74
 
75
75
  def __ensure_valid_authorization_header(self) -> None:
76
- if self.__expiry_time > time.time():
76
+ if self.__id_token_expires > time.time():
77
77
  return
78
78
 
79
79
  response = requests.post(
80
80
  self.__login_url,
81
- json={"user_sub_id": self.__sub, "refresh_token": self.__refresh_token},
81
+ json={"user_sub_id": self.__user_sub_id, "refresh_token": self.__refresh_token},
82
82
  headers={"X-Client-Version": __version__},
83
83
  )
84
84
 
@@ -92,13 +92,14 @@ class ApiSession(requests.Session):
92
92
  # do a locust-cloud --login if we get that.
93
93
 
94
94
  id_token = response.json()["cognito_client_id_token"]
95
- decoded = jwt.decode(id_token, options={"verify_signature": False})
96
- self.__expiry_time = decoded["exp"] - 60 # Refresh 1 minute before expiry
95
+ id_token_expires = response.json()["id_token_expires"]
96
+ self.__id_token_expires = id_token_expires - 60 # Refresh 1 minute before expiry
97
97
  self.headers["Authorization"] = f"Bearer {id_token}"
98
98
 
99
99
  if not self.non_interactive:
100
100
  config = read_cloud_config()
101
101
  config.id_token = id_token
102
+ config.id_token_expires = id_token_expires
102
103
  write_cloud_config(config)
103
104
 
104
105
  def request(self, method, url, *args, **kwargs) -> requests.Response:
locust_cloud/args.py CHANGED
@@ -115,6 +115,11 @@ cloud_parser.add_argument(
115
115
  action="store_true",
116
116
  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 expire you will be prompted to perform another login.",
117
117
  )
118
+ cloud_parser.add_argument(
119
+ "--logout",
120
+ action="store_true",
121
+ help="Removes the authentication credentials",
122
+ )
118
123
  cloud_parser.add_argument(
119
124
  "--delete",
120
125
  action="store_true",
@@ -213,3 +218,23 @@ combined_cloud_parser.add_argument(
213
218
  choices=["DEBUG", "INFO", "WARNING", "ERROR"],
214
219
  default="INFO",
215
220
  )
221
+
222
+
223
+ def add_locust_cloud_argparse(parser):
224
+ cloud_group = parser.add_argument_group(
225
+ "Locust Cloud",
226
+ """Launches a distributed Locust run on locust.cloud infrastructure.
227
+
228
+ Example: locust --cloud -f my_locustfile.py --users 1000 ...""",
229
+ )
230
+
231
+ # This arguments is defined here because only makes sense when
232
+ # running from locust core
233
+ cloud_group.add_argument(
234
+ "--cloud",
235
+ action="store_true",
236
+ help="Run Locust in cloud mode.",
237
+ )
238
+
239
+ for action in cloud_parser._actions:
240
+ cloud_group._add_action(action)
locust_cloud/cloud.py CHANGED
@@ -9,7 +9,7 @@ from locust_cloud.apisession import ApiSession
9
9
  from locust_cloud.args import combined_cloud_parser
10
10
  from locust_cloud.common import __version__
11
11
  from locust_cloud.input_events import input_listener
12
- from locust_cloud.web_login import web_login
12
+ from locust_cloud.web_login import logout, web_login
13
13
  from locust_cloud.websocket import SessionMismatchError, Websocket, WebsocketTimeout
14
14
 
15
15
  logger = logging.getLogger(__name__)
@@ -39,7 +39,13 @@ def main():
39
39
  if options.login:
40
40
  try:
41
41
  web_login()
42
- except KeyboardInterrupt:
42
+ except Exception:
43
+ pass
44
+ return
45
+ if options.logout:
46
+ try:
47
+ logout()
48
+ except Exception:
43
49
  pass
44
50
  return
45
51
 
locust_cloud/common.py CHANGED
@@ -16,9 +16,11 @@ CLOUD_CONF_FILE = pathlib.Path(platformdirs.user_config_dir(appname="locust-clou
16
16
  @dataclass
17
17
  class CloudConfig:
18
18
  id_token: str | None = None
19
+ user_sub_id: str | None = None
19
20
  refresh_token: str | None = None
20
21
  refresh_token_expires: int = 0
21
22
  region: str | None = None
23
+ id_token_expires: int = 0
22
24
 
23
25
 
24
26
  def get_api_url(region):
@@ -38,3 +40,8 @@ def write_cloud_config(config: CloudConfig) -> None:
38
40
 
39
41
  with open(CLOUD_CONF_FILE, "w") as f:
40
42
  json.dump(config.__dict__, f)
43
+
44
+
45
+ def delete_cloud_config() -> None:
46
+ if CLOUD_CONF_FILE.exists():
47
+ CLOUD_CONF_FILE.unlink()
locust_cloud/web_login.py CHANGED
@@ -3,7 +3,7 @@ import time
3
3
  import webbrowser
4
4
 
5
5
  import requests
6
- from locust_cloud.common import VALID_REGIONS, CloudConfig, get_api_url, write_cloud_config
6
+ from locust_cloud.common import VALID_REGIONS, CloudConfig, delete_cloud_config, get_api_url, write_cloud_config
7
7
 
8
8
  POLLING_FREQUENCY = 1
9
9
 
@@ -71,7 +71,13 @@ If the browser does not open or you wish to use a different device to authorize
71
71
  config = CloudConfig(
72
72
  id_token=data["id_token"],
73
73
  refresh_token=data["refresh_token"],
74
+ user_sub_id=data["user_sub_id"],
74
75
  refresh_token_expires=data["refresh_token_expires"],
76
+ id_token_expires=data["id_token_expires"],
75
77
  region=region,
76
78
  )
77
79
  write_cloud_config(config)
80
+
81
+
82
+ def logout():
83
+ delete_cloud_config()
@@ -1,16 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: locust-cloud
3
- Version: 1.20.3
3
+ Version: 1.20.4
4
4
  Summary: Locust Cloud
5
5
  Project-URL: homepage, https://locust.cloud
6
6
  Project-URL: repository, https://github.com/locustcloud/locust-cloud
7
7
  License: MIT
8
8
  License-File: LICENSE
9
9
  Requires-Python: >=3.10
10
- Requires-Dist: locust>=2.36.1
10
+ Requires-Dist: configargparse>=1.5.5
11
+ Requires-Dist: gevent<25.0.0,>=24.10.1
11
12
  Requires-Dist: platformdirs<5.0.0,>=4.3.6
12
- Requires-Dist: pyjwt<3.0,>=2.0
13
13
  Requires-Dist: python-socketio[client]==5.13.0
14
+ Requires-Dist: tomli>=1.1.0; python_version < '3.11'
14
15
  Description-Content-Type: text/markdown
15
16
 
16
17
  # Locust Cloud
@@ -0,0 +1,18 @@
1
+ locust_cloud/__init__.py,sha256=6z2hE5rUP9WJyYgr-7XC2GhIV-05m8XxjOsnb8ae1WY,56
2
+ locust_cloud/apisession.py,sha256=gWqNADGL3H745fefm_tE1O1516OBfOhqZRlc-BgIudM,4224
3
+ locust_cloud/args.py,sha256=warMJQvUg_liowJWHS0XF400rQQ0PUigPSutfwG9BLY,7661
4
+ locust_cloud/cloud.py,sha256=LFO_Ykst0bOBYPqfxS6ll7abAvCCqyUpD3ql4nem9ys,6264
5
+ locust_cloud/common.py,sha256=GVKkWcbbqd9n8oU-fHZRVZw3jGtuIVGSCLD2ZizeEo0,1160
6
+ locust_cloud/input_events.py,sha256=MyxccgboHByICuK6VpQCCJhZQqTZAacNmkSpw-gxBEw,3420
7
+ locust_cloud/web_login.py,sha256=Byu62DKyf8NSbPdP9I2Q3lJh_lZgxjn0OZcRgnMIlXg,2555
8
+ locust_cloud/websocket.py,sha256=9Q7nTFuAwVhgW74DlJNcHTZXOQ1drsXi8hX9ciZhWlQ,8998
9
+ locust_cloud/docs/.gitignore,sha256=ghNPcjYkjQXNS_eVmu2hQFhq6FIUliAD1O2CJhulS2o,10
10
+ locust_cloud/docs/1-first-run.rst,sha256=dYYVxVb1RlWPNvhE98H1D_mzTJ1H2NWNn435nag3_jA,691
11
+ locust_cloud/docs/2-examples.rst,sha256=RublnAK3jHRsEtbJAr1y2NEEoN0YAjkzP1np-7tJ3aE,9565
12
+ locust_cloud/docs/locust-cloud.rst,sha256=NMfWGZdT0YPa0gPPuOcKplzBcqiEK7buPzXHOlzY5T0,98
13
+ locust_cloud/docs/images/locust-cloud-screenshot.png,sha256=ag0IxBi-40VexC84MApol1GCgRCL2h-l8NQDTMaeTyE,477350
14
+ locust_cloud-1.20.4.dist-info/METADATA,sha256=dEwbv0pE4L1vUKIfC9yoMFAGLuqafCiODj_HUW3-HlU,644
15
+ locust_cloud-1.20.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ locust_cloud-1.20.4.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
17
+ locust_cloud-1.20.4.dist-info/licenses/LICENSE,sha256=Ow6fY6ta4KIjdlWalmxGvRP8yLmetvkbkl-SdHMjPIs,1093
18
+ locust_cloud-1.20.4.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- locust_cloud/__init__.py,sha256=9USe4yHuvpP5xrXm0dVsi08pu2l5TcjxQl6Pei8w3lk,713
2
- locust_cloud/apisession.py,sha256=kCr271_l0IeMGw0L563mOecqPJj4OD9h2J3vxCM5zYQ,4015
3
- locust_cloud/args.py,sha256=jBAKDNckEfP5v_2rKEMWWiWK_sjW6kX3sNgzgQkGk0Q,6974
4
- locust_cloud/cloud.py,sha256=esKhV4BXIkn2-1kPyUkW-o8k7GpevgaJz8CrA0S89VE,6149
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/docs/.gitignore,sha256=ghNPcjYkjQXNS_eVmu2hQFhq6FIUliAD1O2CJhulS2o,10
10
- locust_cloud/docs/1-first-run.rst,sha256=dYYVxVb1RlWPNvhE98H1D_mzTJ1H2NWNn435nag3_jA,691
11
- locust_cloud/docs/2-examples.rst,sha256=RublnAK3jHRsEtbJAr1y2NEEoN0YAjkzP1np-7tJ3aE,9565
12
- locust_cloud/docs/locust-cloud.rst,sha256=NMfWGZdT0YPa0gPPuOcKplzBcqiEK7buPzXHOlzY5T0,98
13
- locust_cloud/docs/images/locust-cloud-screenshot.png,sha256=ag0IxBi-40VexC84MApol1GCgRCL2h-l8NQDTMaeTyE,477350
14
- locust_cloud-1.20.3.dist-info/METADATA,sha256=ifRrqs3KTmgT6Rt230IXc_5Clb6xwRL7jJPV-lfQ5wk,576
15
- locust_cloud-1.20.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- locust_cloud-1.20.3.dist-info/entry_points.txt,sha256=PGyAb4e3aTsGS3N3VGShDl6VzJaXy7QwsEgsLOC7V00,57
17
- locust_cloud-1.20.3.dist-info/licenses/LICENSE,sha256=Ow6fY6ta4KIjdlWalmxGvRP8yLmetvkbkl-SdHMjPIs,1093
18
- locust_cloud-1.20.3.dist-info/RECORD,,