locust-cloud 1.25.2.dev3__tar.gz → 1.25.3.dev5__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.25.2.dev3 → locust_cloud-1.25.3.dev5}/PKG-INFO +1 -1
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/args.py +1 -2
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/cloud.py +11 -4
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/common.py +2 -0
- locust_cloud-1.25.3.dev5/locust_cloud/import_finder.py +63 -0
- locust_cloud-1.25.3.dev5/tests/import_finder_test.py +145 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.github/workflows/daily-alb-teardown.yml +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.github/workflows/daily-check.yml +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.github/workflows/tests.yml +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.gitignore +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.pre-commit-config.yaml +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.vscode/extensions.json +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.vscode/launch.json +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.vscode/settings.json +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/LICENSE +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/README.md +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/alb-teardown.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/__init__.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/actions.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/apisession.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/docs/.gitignore +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/docs/1-first-run.rst +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/docs/2-examples.rst +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/docs/images/locust-cloud-screenshot.png +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/docs/locust-cloud.rst +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/input_events.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/web_login.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locust_cloud/websocket.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/locustfile.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/pyproject.toml +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/testdata/extra-files/extra.txt +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/testdata/extra-package/example/__init__.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/testdata/extra-package/setup.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/testdata/requirements.txt +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/tests/args_test.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/tests/browser_tests.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/tests/cloud_test.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/tests/web_login_test.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/tests/websocket_test.py +0 -0
- {locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/uv.lock +0 -0
|
@@ -10,6 +10,7 @@ import tempfile
|
|
|
10
10
|
|
|
11
11
|
from locust_cloud.actions import delete
|
|
12
12
|
from locust_cloud.apisession import ApiSession
|
|
13
|
+
from locust_cloud.common import CWD
|
|
13
14
|
from locust_cloud.web_login import logout, web_login
|
|
14
15
|
|
|
15
16
|
if sys.version_info >= (3, 11):
|
|
@@ -25,8 +26,6 @@ from zipfile import ZipFile
|
|
|
25
26
|
|
|
26
27
|
import configargparse
|
|
27
28
|
|
|
28
|
-
CWD = pathlib.Path.cwd()
|
|
29
|
-
|
|
30
29
|
|
|
31
30
|
class LocustTomlConfigParser(configargparse.TomlConfigParser):
|
|
32
31
|
def parse(self, stream: IO[str]) -> OrderedDict[str, Any]:
|
|
@@ -15,6 +15,7 @@ from locust_cloud.args import (
|
|
|
15
15
|
zip_project_paths,
|
|
16
16
|
)
|
|
17
17
|
from locust_cloud.common import __version__
|
|
18
|
+
from locust_cloud.import_finder import get_imported_files
|
|
18
19
|
from locust_cloud.input_events import input_listener
|
|
19
20
|
from locust_cloud.websocket import SessionMismatchError, Websocket, WebsocketTimeout
|
|
20
21
|
|
|
@@ -55,11 +56,18 @@ def main(locustfiles: list[str] | None = None):
|
|
|
55
56
|
logger.error(e)
|
|
56
57
|
return
|
|
57
58
|
|
|
58
|
-
project_data = zip_project_paths(set(relative_locustfiles + (options.extra_files or [])))
|
|
59
|
-
|
|
60
59
|
session = ApiSession(options.non_interactive)
|
|
61
60
|
websocket = Websocket()
|
|
62
61
|
|
|
62
|
+
auto_extra_files = set()
|
|
63
|
+
for lf in relative_locustfiles:
|
|
64
|
+
auto_extra_files.update(get_imported_files(lf))
|
|
65
|
+
|
|
66
|
+
project_files = set(relative_locustfiles + (options.extra_files or []) + list(auto_extra_files))
|
|
67
|
+
logger.debug(f"Project files: {project_files}")
|
|
68
|
+
|
|
69
|
+
project_data = zip_project_paths(project_files)
|
|
70
|
+
|
|
63
71
|
try:
|
|
64
72
|
logger.info(f"Deploying ({session.region}, locust-cloud {__version__})")
|
|
65
73
|
locust_env_variables = [
|
|
@@ -147,10 +155,9 @@ def main(locustfiles: list[str] | None = None):
|
|
|
147
155
|
|
|
148
156
|
log_ws_url = js["log_ws_url"]
|
|
149
157
|
session_id = js["session_id"]
|
|
150
|
-
webui_url = log_ws_url.replace("/socket-logs", "")
|
|
151
158
|
|
|
152
159
|
def open_ui():
|
|
153
|
-
webbrowser.open_new_tab(
|
|
160
|
+
webbrowser.open_new_tab("https://auth.locust.cloud/load-test")
|
|
154
161
|
|
|
155
162
|
Thread(target=input_listener({"\r": open_ui, "\n": open_ui}), daemon=True).start()
|
|
156
163
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import importlib.util
|
|
3
|
+
import logging
|
|
4
|
+
import site
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from locust_cloud.common import CWD
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
SITE_PACKAGES_PATHS = [Path(p) for p in [site.getusersitepackages(), *site.getsitepackages()]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def imported_modules(tree):
|
|
15
|
+
for node in ast.walk(tree):
|
|
16
|
+
if isinstance(node, ast.Import):
|
|
17
|
+
for alias in node.names:
|
|
18
|
+
yield alias.name.split(".")[0]
|
|
19
|
+
if isinstance(node, ast.ImportFrom):
|
|
20
|
+
if node.level: # relative import
|
|
21
|
+
continue
|
|
22
|
+
if node.module:
|
|
23
|
+
yield node.module.split(".")[0]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_imported_files(file_path: Path) -> set[Path]:
|
|
27
|
+
"""
|
|
28
|
+
Get a list of path that are imported from the given python script
|
|
29
|
+
They are returned as relative paths to CWD
|
|
30
|
+
"""
|
|
31
|
+
paths_queue: list[Path] = [Path(file_path).resolve()]
|
|
32
|
+
paths_seen: set[Path] = set()
|
|
33
|
+
imports: set[Path] = set()
|
|
34
|
+
|
|
35
|
+
while paths_queue:
|
|
36
|
+
current = paths_queue.pop()
|
|
37
|
+
if current in paths_seen:
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
tree = ast.parse(current.read_text())
|
|
41
|
+
for mod in imported_modules(tree):
|
|
42
|
+
spec = importlib.util.find_spec(mod)
|
|
43
|
+
if spec and spec.origin:
|
|
44
|
+
p = Path(spec.origin).resolve()
|
|
45
|
+
if (
|
|
46
|
+
p != current
|
|
47
|
+
and p.is_relative_to(CWD)
|
|
48
|
+
and all(parent not in SITE_PACKAGES_PATHS for parent in p.parents)
|
|
49
|
+
and all(parent not in imports for parent in p.parents)
|
|
50
|
+
and not "site-packages" in str(p)
|
|
51
|
+
):
|
|
52
|
+
# add the whole package directory if __init__.py, else the file
|
|
53
|
+
if p.name == "__init__.py":
|
|
54
|
+
pkg_dir = p.parent
|
|
55
|
+
paths_queue.extend([p for p in pkg_dir.rglob("*.py") if p.is_file()])
|
|
56
|
+
imports.add(p.parent)
|
|
57
|
+
else:
|
|
58
|
+
paths_queue.append(p)
|
|
59
|
+
imports.add(p)
|
|
60
|
+
else:
|
|
61
|
+
logger.debug(f"Unable to find spec for module: {mod}")
|
|
62
|
+
|
|
63
|
+
return set([i.relative_to(CWD) for i in imports])
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import tempfile
|
|
3
|
+
import textwrap
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from locust_cloud.import_finder import CWD, get_imported_files
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@contextmanager
|
|
11
|
+
def temporary_file(content, dir=CWD, suffix=".py"):
|
|
12
|
+
with tempfile.NamedTemporaryFile(dir=dir, suffix=suffix) as f:
|
|
13
|
+
f.write(content.encode())
|
|
14
|
+
f.seek(0)
|
|
15
|
+
yield f.name
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def import_name(path):
|
|
19
|
+
return str(Path(path).relative_to(CWD)).removesuffix(".py")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_ignore_external_packages_imports():
|
|
23
|
+
with temporary_file("import requests") as f:
|
|
24
|
+
assert len(get_imported_files(Path(f))) == 0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_import_file():
|
|
28
|
+
with temporary_file(
|
|
29
|
+
textwrap.dedent(
|
|
30
|
+
"""
|
|
31
|
+
def foo():
|
|
32
|
+
return "bar"
|
|
33
|
+
"""
|
|
34
|
+
)
|
|
35
|
+
) as to_import:
|
|
36
|
+
with temporary_file(f"import {import_name(to_import)}\n") as f:
|
|
37
|
+
imports = get_imported_files(Path(f))
|
|
38
|
+
assert imports == {Path(to_import).relative_to(CWD)}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_from_import_file():
|
|
42
|
+
with temporary_file(
|
|
43
|
+
textwrap.dedent(
|
|
44
|
+
"""
|
|
45
|
+
def foo():
|
|
46
|
+
return "bar"
|
|
47
|
+
"""
|
|
48
|
+
)
|
|
49
|
+
) as to_import:
|
|
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(CWD)}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_all_imports():
|
|
55
|
+
with temporary_file(
|
|
56
|
+
textwrap.dedent(
|
|
57
|
+
"""
|
|
58
|
+
def foo():
|
|
59
|
+
return "bar"
|
|
60
|
+
"""
|
|
61
|
+
)
|
|
62
|
+
) as to_import:
|
|
63
|
+
with temporary_file(
|
|
64
|
+
textwrap.dedent(
|
|
65
|
+
f"""
|
|
66
|
+
import requests
|
|
67
|
+
import {import_name(to_import)}
|
|
68
|
+
"""
|
|
69
|
+
)
|
|
70
|
+
) as f:
|
|
71
|
+
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(CWD)}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_second_level_imports():
|
|
75
|
+
with temporary_file(
|
|
76
|
+
textwrap.dedent(
|
|
77
|
+
"""
|
|
78
|
+
def foo():
|
|
79
|
+
return "bar"
|
|
80
|
+
"""
|
|
81
|
+
)
|
|
82
|
+
) as to_import:
|
|
83
|
+
with temporary_file(
|
|
84
|
+
textwrap.dedent(
|
|
85
|
+
f"""
|
|
86
|
+
def bar():
|
|
87
|
+
import {import_name(to_import)}
|
|
88
|
+
"""
|
|
89
|
+
)
|
|
90
|
+
) as f:
|
|
91
|
+
assert get_imported_files(Path(f)) == {Path(to_import).relative_to(CWD)}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_recursive_imports():
|
|
95
|
+
with temporary_file(
|
|
96
|
+
textwrap.dedent(
|
|
97
|
+
"""
|
|
98
|
+
def foo():
|
|
99
|
+
return "bar"
|
|
100
|
+
"""
|
|
101
|
+
)
|
|
102
|
+
) as to_import_1:
|
|
103
|
+
with temporary_file(f"import {import_name(to_import_1)}") as to_import:
|
|
104
|
+
with temporary_file(f"import {import_name(to_import)}") as f:
|
|
105
|
+
assert get_imported_files(Path(f)) == {
|
|
106
|
+
Path(to_import).relative_to(CWD),
|
|
107
|
+
Path(to_import_1).relative_to(CWD),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_package_imports():
|
|
112
|
+
test_package = CWD / "test_package"
|
|
113
|
+
test_package.mkdir()
|
|
114
|
+
|
|
115
|
+
(test_package / "__init__.py").write_text(
|
|
116
|
+
textwrap.dedent(
|
|
117
|
+
"""
|
|
118
|
+
from test import bar
|
|
119
|
+
def foo():
|
|
120
|
+
return "bar"
|
|
121
|
+
"""
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
(test_package / "test.py").write_text(
|
|
125
|
+
textwrap.dedent(
|
|
126
|
+
"""
|
|
127
|
+
def bar():
|
|
128
|
+
return "baz"
|
|
129
|
+
"""
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
try:
|
|
133
|
+
with temporary_file("import test_package") as f:
|
|
134
|
+
assert get_imported_files(Path(f)) == {test_package.relative_to(CWD)}
|
|
135
|
+
|
|
136
|
+
with temporary_file("import test_package.test") as f:
|
|
137
|
+
assert get_imported_files(Path(f)) == {test_package.relative_to(CWD)}
|
|
138
|
+
|
|
139
|
+
with temporary_file("from test_package import bar") as f:
|
|
140
|
+
assert get_imported_files(Path(f)) == {test_package.relative_to(CWD)}
|
|
141
|
+
|
|
142
|
+
with temporary_file("from test_package.test import bar") as f:
|
|
143
|
+
assert get_imported_files(Path(f)) == {test_package.relative_to(CWD)}
|
|
144
|
+
finally:
|
|
145
|
+
shutil.rmtree(test_package, ignore_errors=True)
|
{locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{locust_cloud-1.25.2.dev3 → locust_cloud-1.25.3.dev5}/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
|
|
File without changes
|
|
File without changes
|