locust-cloud 1.26.4.dev1__tar.gz → 1.26.4.dev9__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.
Potentially problematic release.
This version of locust-cloud might be problematic. Click here for more details.
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/PKG-INFO +1 -1
- locust_cloud-1.26.4.dev1/locust_cloud/cloud.py → locust_cloud-1.26.4.dev9/locust_cloud/__init__.py +4 -12
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/apisession.py +15 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/args.py +22 -35
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/common.py +1 -5
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/import_finder.py +2 -4
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/web_login.py +1 -5
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/pyproject.toml +0 -3
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/tests/args_test.py +9 -30
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/tests/cloud_test.py +3 -7
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/tests/import_finder_test.py +14 -14
- locust_cloud-1.26.4.dev1/locust_cloud/__init__.py +0 -3
- locust_cloud-1.26.4.dev1/locust_cloud/actions.py +0 -22
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.github/workflows/daily-alb-teardown.yml +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.github/workflows/daily-check.yml +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.github/workflows/tests.yml +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.gitignore +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.pre-commit-config.yaml +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.vscode/extensions.json +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.vscode/launch.json +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.vscode/settings.json +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/LICENSE +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/README.md +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/alb-teardown.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/docs/.gitignore +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/docs/1-first-run.rst +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/docs/2-examples.rst +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/docs/images/locust-cloud-screenshot.png +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/docs/locust-cloud.rst +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/input_events.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locust_cloud/websocket.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/locustfile.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/testdata/autodetected.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/testdata/extra-files/extra.txt +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/testdata/extra-package/example/__init__.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/testdata/extra-package/setup.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/testdata/requirements.txt +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/tests/browser_tests.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/tests/web_login_test.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/tests/websocket_test.py +0 -0
- {locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/uv.lock +0 -0
locust_cloud-1.26.4.dev1/locust_cloud/cloud.py → locust_cloud-1.26.4.dev9/locust_cloud/__init__.py
RENAMED
|
@@ -7,7 +7,6 @@ from argparse import ArgumentTypeError
|
|
|
7
7
|
from threading import Thread
|
|
8
8
|
|
|
9
9
|
import requests
|
|
10
|
-
from locust_cloud.actions import delete
|
|
11
10
|
from locust_cloud.apisession import ApiSession
|
|
12
11
|
from locust_cloud.args import (
|
|
13
12
|
combined_cloud_parser,
|
|
@@ -34,13 +33,6 @@ def configure_logging(loglevel: str) -> None:
|
|
|
34
33
|
logging.getLogger("urllib3").setLevel(logging.INFO)
|
|
35
34
|
|
|
36
35
|
|
|
37
|
-
def deprecated_main():
|
|
38
|
-
import sys
|
|
39
|
-
|
|
40
|
-
print("`locust-cloud` is deprecated; use `locust --cloud` instead.", file=sys.stderr)
|
|
41
|
-
return 1
|
|
42
|
-
|
|
43
|
-
|
|
44
36
|
def main(locustfiles: list[str] | None = None):
|
|
45
37
|
options, locust_options = combined_cloud_parser.parse_known_args()
|
|
46
38
|
|
|
@@ -175,7 +167,7 @@ def main(locustfiles: list[str] | None = None):
|
|
|
175
167
|
if options.local_instance:
|
|
176
168
|
os.system("pkill -TERM -f bootstrap")
|
|
177
169
|
else:
|
|
178
|
-
|
|
170
|
+
session.teardown()
|
|
179
171
|
try:
|
|
180
172
|
websocket.wait(timeout=True)
|
|
181
173
|
except (WebsocketTimeout, SessionMismatchError) as e:
|
|
@@ -183,7 +175,7 @@ def main(locustfiles: list[str] | None = None):
|
|
|
183
175
|
return 1
|
|
184
176
|
except WebsocketTimeout as e:
|
|
185
177
|
logger.error(str(e))
|
|
186
|
-
|
|
178
|
+
session.teardown()
|
|
187
179
|
return 1
|
|
188
180
|
except SessionMismatchError as e:
|
|
189
181
|
# In this case we do not trigger the teardown since the running instance is not ours
|
|
@@ -191,7 +183,7 @@ def main(locustfiles: list[str] | None = None):
|
|
|
191
183
|
return 1
|
|
192
184
|
except Exception as e:
|
|
193
185
|
logger.exception(e)
|
|
194
|
-
|
|
186
|
+
session.teardown()
|
|
195
187
|
return 1
|
|
196
188
|
else:
|
|
197
|
-
|
|
189
|
+
session.teardown()
|
|
@@ -110,3 +110,18 @@ class ApiSession(requests.Session):
|
|
|
110
110
|
def request(self, method, url, *args, **kwargs) -> requests.Response:
|
|
111
111
|
self.__ensure_valid_authorization_header()
|
|
112
112
|
return super().request(method, f"{self.api_url}{url}", *args, **kwargs)
|
|
113
|
+
|
|
114
|
+
def teardown(self):
|
|
115
|
+
try:
|
|
116
|
+
logger.info("Tearing down Locust cloud...")
|
|
117
|
+
response = self.delete("/teardown")
|
|
118
|
+
if response.status_code == 200:
|
|
119
|
+
logger.debug(f"Response message from teardown: {response.json()['message']}")
|
|
120
|
+
else:
|
|
121
|
+
logger.info(
|
|
122
|
+
f"Could not automatically tear down Locust Cloud: HTTP {response.status_code}/{response.reason} - Response: {response.text} - URL: {response.request.url}"
|
|
123
|
+
)
|
|
124
|
+
except KeyboardInterrupt:
|
|
125
|
+
pass # don't show nasty callstack
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.error(f"Could not automatically tear down Locust Cloud: {e.__class__.__name__}:{e}")
|
|
@@ -3,15 +3,14 @@ import base64
|
|
|
3
3
|
import gzip
|
|
4
4
|
import io
|
|
5
5
|
import os
|
|
6
|
-
import pathlib
|
|
7
6
|
import shutil
|
|
8
7
|
import sys
|
|
9
8
|
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
10
|
|
|
11
|
-
from locust_cloud.actions import delete
|
|
12
11
|
from locust_cloud.apisession import ApiSession
|
|
13
|
-
from locust_cloud.common import
|
|
14
|
-
from locust_cloud.web_login import
|
|
12
|
+
from locust_cloud.common import delete_cloud_config
|
|
13
|
+
from locust_cloud.web_login import web_login
|
|
15
14
|
|
|
16
15
|
if sys.version_info >= (3, 11):
|
|
17
16
|
import tomllib
|
|
@@ -20,7 +19,7 @@ else:
|
|
|
20
19
|
|
|
21
20
|
from argparse import ArgumentTypeError
|
|
22
21
|
from collections import OrderedDict
|
|
23
|
-
from collections.abc import
|
|
22
|
+
from collections.abc import Generator, Iterable
|
|
24
23
|
from typing import IO, Any, cast
|
|
25
24
|
from zipfile import ZipFile
|
|
26
25
|
|
|
@@ -49,29 +48,22 @@ class LocustTomlConfigParser(configargparse.TomlConfigParser):
|
|
|
49
48
|
return result
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
def
|
|
53
|
-
for function in functions:
|
|
54
|
-
value = function(value)
|
|
55
|
-
|
|
56
|
-
return value
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def valid_project_path(path: str | pathlib.Path) -> pathlib.Path:
|
|
51
|
+
def valid_project_path(path: str | Path) -> Path:
|
|
60
52
|
try:
|
|
61
53
|
# Expand '~' and eliminate '..', '.', and symlinks
|
|
62
|
-
p =
|
|
54
|
+
p = Path(path).expanduser().resolve(strict=True)
|
|
63
55
|
|
|
64
|
-
return p.relative_to(
|
|
56
|
+
return p.relative_to(Path.cwd())
|
|
65
57
|
except FileNotFoundError:
|
|
66
58
|
raise ArgumentTypeError(f"{path!r} does not exist")
|
|
67
59
|
except OSError as exc: # Bad characters, too long, etc
|
|
68
60
|
raise ArgumentTypeError(f"{path!r} is not a valid path: {exc}")
|
|
69
61
|
except ValueError:
|
|
70
|
-
raise ArgumentTypeError(f"{path!r} is not under current working directory: {
|
|
62
|
+
raise ArgumentTypeError(f"{path!r} is not under current working directory: {Path.cwd()}")
|
|
71
63
|
|
|
72
64
|
|
|
73
|
-
def valid_extra_packages_path(file_path: str) ->
|
|
74
|
-
p =
|
|
65
|
+
def valid_extra_packages_path(file_path: str) -> Path:
|
|
66
|
+
p = Path(file_path).resolve()
|
|
75
67
|
|
|
76
68
|
if not p.exists():
|
|
77
69
|
raise ArgumentTypeError(f"Path not found: {file_path}")
|
|
@@ -84,12 +76,7 @@ def valid_extra_packages_path(file_path: str) -> pathlib.Path:
|
|
|
84
76
|
def transfer_encode(file_name: str, stream: IO[bytes]) -> dict[str, str]:
|
|
85
77
|
return {
|
|
86
78
|
"filename": file_name,
|
|
87
|
-
"data":
|
|
88
|
-
stream.read(),
|
|
89
|
-
gzip.compress,
|
|
90
|
-
base64.b64encode,
|
|
91
|
-
bytes.decode,
|
|
92
|
-
),
|
|
79
|
+
"data": bytes.decode(base64.b64encode(gzip.compress(stream.read()))),
|
|
93
80
|
}
|
|
94
81
|
|
|
95
82
|
|
|
@@ -101,21 +88,21 @@ def transfer_encoded_file(file_path: str) -> dict[str, str]:
|
|
|
101
88
|
raise ArgumentTypeError(f"File not found: {file_path}")
|
|
102
89
|
|
|
103
90
|
|
|
104
|
-
def expanded(paths: Iterable[
|
|
91
|
+
def expanded(paths: Iterable[Path], skip_folders: list[str] = []) -> Generator[Path, None, None]:
|
|
105
92
|
for path in paths:
|
|
106
|
-
path =
|
|
93
|
+
path = Path(path)
|
|
107
94
|
|
|
108
95
|
if path.is_dir():
|
|
109
96
|
for root, _, file_names in os.walk(path):
|
|
110
97
|
if root.split("/")[-1] in skip_folders:
|
|
111
98
|
continue
|
|
112
99
|
for file_name in file_names:
|
|
113
|
-
yield
|
|
100
|
+
yield Path(root) / file_name
|
|
114
101
|
else:
|
|
115
102
|
yield path
|
|
116
103
|
|
|
117
104
|
|
|
118
|
-
def zip_project_paths(paths: Iterable[
|
|
105
|
+
def zip_project_paths(paths: Iterable[Path], to_file: str = "project"):
|
|
119
106
|
buffer = io.BytesIO()
|
|
120
107
|
skip_folders = ["__pycache__"]
|
|
121
108
|
|
|
@@ -127,14 +114,14 @@ def zip_project_paths(paths: Iterable[pathlib.Path], to_file: str = "project"):
|
|
|
127
114
|
return transfer_encode(f"{to_file}.zip", buffer)
|
|
128
115
|
|
|
129
116
|
|
|
130
|
-
def flat_transfer_encoded_args_files(paths: list[
|
|
117
|
+
def flat_transfer_encoded_args_files(paths: list[Path], to_file: str | None) -> dict[str, str]:
|
|
131
118
|
buffer = io.BytesIO()
|
|
132
119
|
|
|
133
120
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
134
|
-
tmp_path =
|
|
121
|
+
tmp_path = Path(tmpdir)
|
|
135
122
|
|
|
136
123
|
for src in paths:
|
|
137
|
-
src_path =
|
|
124
|
+
src_path = Path(src)
|
|
138
125
|
dest_path = tmp_path / src_path.name
|
|
139
126
|
|
|
140
127
|
if src_path.is_file():
|
|
@@ -152,7 +139,7 @@ def flat_transfer_encoded_args_files(paths: list[pathlib.Path], to_file: str | N
|
|
|
152
139
|
elif item.is_dir():
|
|
153
140
|
for root, _, files in os.walk(item):
|
|
154
141
|
for file in files:
|
|
155
|
-
file_path =
|
|
142
|
+
file_path = Path(root) / file
|
|
156
143
|
arcname = file_path.relative_to(tmp_path)
|
|
157
144
|
zf.write(file_path, arcname)
|
|
158
145
|
|
|
@@ -162,7 +149,7 @@ def flat_transfer_encoded_args_files(paths: list[pathlib.Path], to_file: str | N
|
|
|
162
149
|
|
|
163
150
|
class MergeToTransferEncodedZipFlat(argparse.Action):
|
|
164
151
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
165
|
-
paths = cast(list[
|
|
152
|
+
paths = cast(list[Path], values)
|
|
166
153
|
value = flat_transfer_encoded_args_files(paths, option_string.lstrip("-"))
|
|
167
154
|
setattr(namespace, self.dest, value)
|
|
168
155
|
|
|
@@ -175,14 +162,14 @@ class WebLogin(argparse.Action):
|
|
|
175
162
|
|
|
176
163
|
class WebLogout(argparse.Action):
|
|
177
164
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
178
|
-
|
|
165
|
+
delete_cloud_config()
|
|
179
166
|
parser.exit()
|
|
180
167
|
|
|
181
168
|
|
|
182
169
|
class StackTeardown(argparse.Action):
|
|
183
170
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
184
171
|
session = ApiSession(namespace.non_interactive)
|
|
185
|
-
|
|
172
|
+
session.teardown()
|
|
186
173
|
parser.exit()
|
|
187
174
|
|
|
188
175
|
|
|
@@ -8,9 +8,6 @@ import platformdirs
|
|
|
8
8
|
|
|
9
9
|
__version__ = importlib.metadata.version("locust-cloud")
|
|
10
10
|
|
|
11
|
-
CWD = pathlib.Path.cwd()
|
|
12
|
-
|
|
13
|
-
|
|
14
11
|
VALID_REGIONS = ["us-east-1", "eu-north-1"]
|
|
15
12
|
CLOUD_CONF_FILE = pathlib.Path(platformdirs.user_config_dir(appname="locust-cloud")) / "config"
|
|
16
13
|
|
|
@@ -45,5 +42,4 @@ def write_cloud_config(config: CloudConfig) -> None:
|
|
|
45
42
|
|
|
46
43
|
|
|
47
44
|
def delete_cloud_config() -> None:
|
|
48
|
-
|
|
49
|
-
CLOUD_CONF_FILE.unlink()
|
|
45
|
+
CLOUD_CONF_FILE.unlink(missing_ok=True)
|
|
@@ -4,8 +4,6 @@ import logging
|
|
|
4
4
|
import site
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from locust_cloud.common import CWD
|
|
8
|
-
|
|
9
7
|
logger = logging.getLogger(__name__)
|
|
10
8
|
|
|
11
9
|
SITE_PACKAGES_PATHS = [Path(p) for p in [site.getusersitepackages(), *site.getsitepackages()]]
|
|
@@ -44,7 +42,7 @@ def get_imported_files(file_path: Path) -> set[Path]:
|
|
|
44
42
|
p = Path(spec.origin).resolve()
|
|
45
43
|
if (
|
|
46
44
|
p != current
|
|
47
|
-
and p.is_relative_to(
|
|
45
|
+
and p.is_relative_to(Path.cwd())
|
|
48
46
|
and all(parent not in SITE_PACKAGES_PATHS for parent in p.parents)
|
|
49
47
|
and all(parent not in imports for parent in p.parents)
|
|
50
48
|
and not "site-packages" in str(p)
|
|
@@ -61,4 +59,4 @@ def get_imported_files(file_path: Path) -> set[Path]:
|
|
|
61
59
|
else:
|
|
62
60
|
pass # logger.debug(f"Unable to find spec for module: {mod}")
|
|
63
61
|
|
|
64
|
-
return set([i.relative_to(
|
|
62
|
+
return set([i.relative_to(Path.cwd()) for i in imports])
|
|
@@ -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,
|
|
6
|
+
from locust_cloud.common import VALID_REGIONS, CloudConfig, get_api_url, write_cloud_config
|
|
7
7
|
|
|
8
8
|
POLLING_FREQUENCY = 1
|
|
9
9
|
|
|
@@ -77,7 +77,3 @@ If the browser does not open or you wish to use a different device to authorize
|
|
|
77
77
|
region=region,
|
|
78
78
|
)
|
|
79
79
|
write_cloud_config(config)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def logout():
|
|
83
|
-
delete_cloud_config()
|
|
@@ -18,9 +18,6 @@ dependencies = [
|
|
|
18
18
|
homepage = "https://locust.cloud"
|
|
19
19
|
repository = "https://github.com/locustcloud/locust-cloud"
|
|
20
20
|
|
|
21
|
-
[project.scripts]
|
|
22
|
-
locust-cloud = "locust_cloud.cloud:deprecated_main"
|
|
23
|
-
|
|
24
21
|
[build-system]
|
|
25
22
|
requires = ["hatchling", "hatch-vcs"]
|
|
26
23
|
build-backend = "hatchling.build"
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import gzip
|
|
3
3
|
import io
|
|
4
|
-
import pathlib
|
|
5
4
|
import tempfile
|
|
6
5
|
from argparse import ArgumentTypeError
|
|
6
|
+
from pathlib import Path
|
|
7
7
|
from zipfile import ZipFile
|
|
8
8
|
|
|
9
9
|
import pytest
|
|
10
10
|
from locust_cloud.args import (
|
|
11
|
-
CWD,
|
|
12
11
|
combined_cloud_parser,
|
|
13
12
|
expanded,
|
|
14
|
-
pipe,
|
|
15
13
|
transfer_encode,
|
|
16
14
|
transfer_encoded_file,
|
|
17
15
|
valid_project_path,
|
|
@@ -19,20 +17,14 @@ from locust_cloud.args import (
|
|
|
19
17
|
)
|
|
20
18
|
|
|
21
19
|
|
|
22
|
-
def test_pipe():
|
|
23
|
-
one = lambda x: x * 3
|
|
24
|
-
two = lambda x: x + 3
|
|
25
|
-
assert pipe(4, one, two) == 15
|
|
26
|
-
|
|
27
|
-
|
|
28
20
|
def test_valid_project_path():
|
|
29
21
|
with tempfile.NamedTemporaryFile() as tmp:
|
|
30
22
|
with pytest.raises(ArgumentTypeError) as exception:
|
|
31
23
|
valid_project_path(tmp.name)
|
|
32
24
|
|
|
33
|
-
assert str(exception.value) == f"'{tmp.name}' is not under current working directory: {
|
|
25
|
+
assert str(exception.value) == f"'{tmp.name}' is not under current working directory: {Path.cwd()}"
|
|
34
26
|
|
|
35
|
-
bad_path = str(
|
|
27
|
+
bad_path = str(Path.cwd() / "does-not-exist")
|
|
36
28
|
|
|
37
29
|
with pytest.raises(ArgumentTypeError) as exception:
|
|
38
30
|
valid_project_path(bad_path)
|
|
@@ -45,12 +37,7 @@ def test_transfer_encode():
|
|
|
45
37
|
data = b"pineapple"
|
|
46
38
|
result = transfer_encode(file_name, io.BytesIO(data))
|
|
47
39
|
assert file_name == result["filename"]
|
|
48
|
-
assert data ==
|
|
49
|
-
result["data"],
|
|
50
|
-
str.encode,
|
|
51
|
-
base64.b64decode,
|
|
52
|
-
gzip.decompress,
|
|
53
|
-
)
|
|
40
|
+
assert data == gzip.decompress(base64.b64decode(str.encode(result["data"])))
|
|
54
41
|
|
|
55
42
|
|
|
56
43
|
def test_transfer_encoded_file():
|
|
@@ -61,22 +48,14 @@ def test_transfer_encoded_file():
|
|
|
61
48
|
|
|
62
49
|
|
|
63
50
|
def test_expanded():
|
|
64
|
-
result = list(expanded([
|
|
65
|
-
assert result == [
|
|
51
|
+
result = list(expanded([Path("locustfile.py"), Path("testdata/extra-files")]))
|
|
52
|
+
assert result == [Path("locustfile.py"), Path("testdata/extra-files/extra.txt")]
|
|
66
53
|
|
|
67
54
|
|
|
68
55
|
def test_project_zip():
|
|
69
|
-
result = zip_project_paths([
|
|
56
|
+
result = zip_project_paths([Path("testdata/extra-files")])
|
|
70
57
|
assert result["filename"] == "project.zip"
|
|
71
|
-
|
|
72
|
-
buffer = pipe(
|
|
73
|
-
result["data"],
|
|
74
|
-
str.encode,
|
|
75
|
-
base64.b64decode,
|
|
76
|
-
gzip.decompress,
|
|
77
|
-
io.BytesIO,
|
|
78
|
-
)
|
|
79
|
-
|
|
58
|
+
buffer = io.BytesIO(gzip.decompress(base64.b64decode(str.encode(result["data"]))))
|
|
80
59
|
with ZipFile(buffer) as zf:
|
|
81
60
|
assert zf.namelist() == ["testdata/extra-files/extra.txt"]
|
|
82
61
|
|
|
@@ -86,7 +65,7 @@ def test_parser_extra_files(capsys):
|
|
|
86
65
|
with tempfile.NamedTemporaryFile() as tmp:
|
|
87
66
|
combined_cloud_parser.parse_known_args(f"locust-cloud --extra-files {tmp.name}")
|
|
88
67
|
|
|
89
|
-
expected = f"error: argument --extra-files: '{tmp.name}' is not under current working directory: {
|
|
68
|
+
expected = f"error: argument --extra-files: '{tmp.name}' is not under current working directory: {Path.cwd()}"
|
|
90
69
|
assert expected in capsys.readouterr().err
|
|
91
70
|
|
|
92
71
|
with pytest.raises(SystemExit):
|
|
@@ -34,10 +34,8 @@ def backup_cloud_config_file():
|
|
|
34
34
|
finally:
|
|
35
35
|
if do_backup:
|
|
36
36
|
backup_file.rename(CLOUD_CONFIG_FILE)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if CLOUD_CONFIG_FILE.exists():
|
|
40
|
-
CLOUD_CONFIG_FILE.unlink()
|
|
37
|
+
else:
|
|
38
|
+
locust_cloud.common.delete_cloud_config()
|
|
41
39
|
|
|
42
40
|
|
|
43
41
|
def check_for_output(stream, regex, timeout=None) -> re.Match | None:
|
|
@@ -58,9 +56,7 @@ def check_for_output(stream, regex, timeout=None) -> re.Match | None:
|
|
|
58
56
|
|
|
59
57
|
|
|
60
58
|
def test_cli_auth() -> None:
|
|
61
|
-
|
|
62
|
-
if CLOUD_CONFIG_FILE.exists():
|
|
63
|
-
CLOUD_CONFIG_FILE.unlink()
|
|
59
|
+
locust_cloud.common.delete_cloud_config()
|
|
64
60
|
assert not CLOUD_CONFIG_FILE.exists()
|
|
65
61
|
|
|
66
62
|
env = {
|
|
@@ -4,11 +4,11 @@ import textwrap
|
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from locust_cloud.import_finder import
|
|
7
|
+
from locust_cloud.import_finder import get_imported_files
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@contextmanager
|
|
11
|
-
def temporary_file(content, dir=
|
|
11
|
+
def temporary_file(content, dir=Path.cwd(), suffix=".py"):
|
|
12
12
|
with tempfile.NamedTemporaryFile(dir=dir, suffix=suffix) as f:
|
|
13
13
|
f.write(content.encode())
|
|
14
14
|
f.seek(0)
|
|
@@ -16,7 +16,7 @@ def temporary_file(content, dir=CWD, suffix=".py"):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def import_name(path):
|
|
19
|
-
return str(Path(path).relative_to(
|
|
19
|
+
return str(Path(path).relative_to(Path.cwd())).removesuffix(".py")
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def test_ignore_external_packages_imports():
|
|
@@ -35,7 +35,7 @@ def test_import_file():
|
|
|
35
35
|
) as to_import:
|
|
36
36
|
with temporary_file(f"import {import_name(to_import)}\n") as f:
|
|
37
37
|
imports = get_imported_files(Path(f))
|
|
38
|
-
assert imports == {Path(to_import).relative_to(
|
|
38
|
+
assert imports == {Path(to_import).relative_to(Path.cwd())}
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def test_from_import_file():
|
|
@@ -48,7 +48,7 @@ def test_from_import_file():
|
|
|
48
48
|
)
|
|
49
49
|
) as to_import:
|
|
50
50
|
with temporary_file(f"from {import_name(to_import)} import foo") as f:
|
|
51
|
-
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(
|
|
51
|
+
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(Path.cwd())}
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def test_all_imports():
|
|
@@ -69,7 +69,7 @@ def test_all_imports():
|
|
|
69
69
|
"""
|
|
70
70
|
)
|
|
71
71
|
) as f:
|
|
72
|
-
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(
|
|
72
|
+
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(Path.cwd())}
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def test_second_level_imports():
|
|
@@ -89,7 +89,7 @@ def test_second_level_imports():
|
|
|
89
89
|
"""
|
|
90
90
|
)
|
|
91
91
|
) as f:
|
|
92
|
-
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(
|
|
92
|
+
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(Path.cwd())}
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
def test_recursive_imports():
|
|
@@ -104,13 +104,13 @@ def test_recursive_imports():
|
|
|
104
104
|
with temporary_file(f"import {import_name(to_import_1)}") as to_import:
|
|
105
105
|
with temporary_file(f"import {import_name(to_import)}") as f:
|
|
106
106
|
assert get_imported_files(Path(f)) == {
|
|
107
|
-
Path(to_import).relative_to(
|
|
108
|
-
Path(to_import_1).relative_to(
|
|
107
|
+
Path(to_import).relative_to(Path.cwd()),
|
|
108
|
+
Path(to_import_1).relative_to(Path.cwd()),
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
def test_package_imports():
|
|
113
|
-
test_package =
|
|
113
|
+
test_package = Path.cwd() / "test_package"
|
|
114
114
|
test_package.mkdir()
|
|
115
115
|
|
|
116
116
|
(test_package / "__init__.py").write_text(
|
|
@@ -132,15 +132,15 @@ def test_package_imports():
|
|
|
132
132
|
)
|
|
133
133
|
try:
|
|
134
134
|
with temporary_file("import test_package") as f:
|
|
135
|
-
assert get_imported_files(Path(f)) == {test_package.relative_to(
|
|
135
|
+
assert get_imported_files(Path(f)) == {test_package.relative_to(Path.cwd())}
|
|
136
136
|
|
|
137
137
|
with temporary_file("import test_package.test") as f:
|
|
138
|
-
assert get_imported_files(Path(f)) == {(test_package / "test.py").relative_to(
|
|
138
|
+
assert get_imported_files(Path(f)) == {(test_package / "test.py").relative_to(Path.cwd())}
|
|
139
139
|
|
|
140
140
|
with temporary_file("from test_package import bar") as f:
|
|
141
|
-
assert get_imported_files(Path(f)) == {test_package.relative_to(
|
|
141
|
+
assert get_imported_files(Path(f)) == {test_package.relative_to(Path.cwd())}
|
|
142
142
|
|
|
143
143
|
with temporary_file("from test_package.test import bar") as f:
|
|
144
|
-
assert get_imported_files(Path(f)) == {(test_package / "test.py").relative_to(
|
|
144
|
+
assert get_imported_files(Path(f)) == {(test_package / "test.py").relative_to(Path.cwd())}
|
|
145
145
|
finally:
|
|
146
146
|
shutil.rmtree(test_package, ignore_errors=True)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
logger = logging.getLogger(__name__)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def delete(session):
|
|
7
|
-
try:
|
|
8
|
-
logger.info("Tearing down Locust cloud...")
|
|
9
|
-
response = session.delete(
|
|
10
|
-
"/teardown",
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
if response.status_code == 200:
|
|
14
|
-
logger.debug(f"Response message from teardown: {response.json()['message']}")
|
|
15
|
-
else:
|
|
16
|
-
logger.info(
|
|
17
|
-
f"Could not automatically tear down Locust Cloud: HTTP {response.status_code}/{response.reason} - Response: {response.text} - URL: {response.request.url}"
|
|
18
|
-
)
|
|
19
|
-
except KeyboardInterrupt:
|
|
20
|
-
pass # don't show nasty callstack
|
|
21
|
-
except Exception as e:
|
|
22
|
-
logger.error(f"Could not automatically tear down Locust Cloud: {e.__class__.__name__}:{e}")
|
{locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/.github/workflows/daily-alb-teardown.yml
RENAMED
|
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
|
{locust_cloud-1.26.4.dev1 → locust_cloud-1.26.4.dev9}/testdata/extra-package/example/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|