locust 2.26.1.dev72__py3-none-any.whl → 2.27.1.dev7__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.
Files changed (41) hide show
  1. locust/_version.py +2 -2
  2. locust/argument_parser.py +1 -1
  3. locust/env.py +0 -2
  4. locust/html.py +37 -83
  5. locust/main.py +10 -14
  6. locust/stats.py +3 -10
  7. locust/test/test_fasthttp.py +6 -4
  8. locust/test/test_main.py +6 -15
  9. locust/test/test_web.py +53 -199
  10. locust/web.py +18 -56
  11. {locust-2.26.1.dev72.dist-info → locust-2.27.1.dev7.dist-info}/METADATA +1 -1
  12. {locust-2.26.1.dev72.dist-info → locust-2.27.1.dev7.dist-info}/RECORD +16 -41
  13. locust/static/chart.js +0 -133
  14. locust/static/css/application.css +0 -491
  15. locust/static/css/application.css.map +0 -1
  16. locust/static/css/tables.css +0 -74
  17. locust/static/css/tables.css.map +0 -1
  18. locust/static/echarts.common.min.js +0 -22
  19. locust/static/img/favicon.ico +0 -0
  20. locust/static/img/logo.png +0 -0
  21. locust/static/img/ui-screenshot-charts.png +0 -0
  22. locust/static/img/ui-screenshot-start-test.png +0 -0
  23. locust/static/img/ui-screenshot-stats.png +0 -0
  24. locust/static/img/ui-screenshot-workers.png +0 -0
  25. locust/static/jquery-1.11.3.min.js +0 -5
  26. locust/static/jquery.jqote2.min.js +0 -14
  27. locust/static/jquery.tools.min.js +0 -17
  28. locust/static/locust.js +0 -324
  29. locust/static/sass/_base.sass +0 -27
  30. locust/static/sass/_mixins.sass +0 -3
  31. locust/static/sass/application.sass +0 -320
  32. locust/static/sass/tables.sass +0 -61
  33. locust/static/tasks.js +0 -48
  34. locust/static/vintage.js +0 -35
  35. locust/templates/index.html +0 -370
  36. locust/templates/report.html +0 -265
  37. locust/templates/stats_data.html +0 -10
  38. {locust-2.26.1.dev72.dist-info → locust-2.27.1.dev7.dist-info}/LICENSE +0 -0
  39. {locust-2.26.1.dev72.dist-info → locust-2.27.1.dev7.dist-info}/WHEEL +0 -0
  40. {locust-2.26.1.dev72.dist-info → locust-2.27.1.dev7.dist-info}/entry_points.txt +0 -0
  41. {locust-2.26.1.dev72.dist-info → locust-2.27.1.dev7.dist-info}/top_level.txt +0 -0
locust/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.26.1.dev72'
16
- __version_tuple__ = version_tuple = (2, 26, 1, 'dev72')
15
+ __version__ = version = '2.27.1.dev7'
16
+ __version_tuple__ = version_tuple = (2, 27, 1, 'dev7')
locust/argument_parser.py CHANGED
@@ -500,7 +500,7 @@ def setup_parser_arguments(parser):
500
500
  "--legacy-ui",
501
501
  default=False,
502
502
  action="store_true",
503
- help="Use the legacy frontend for the web UI (deprecated, support will be removed soon)",
503
+ help=configargparse.SUPPRESS,
504
504
  env_var="LOCUST_LEGACY_UI",
505
505
  )
506
506
 
locust/env.py CHANGED
@@ -166,7 +166,6 @@ class Environment:
166
166
  stats_csv_writer: StatsCSV | None = None,
167
167
  delayed_start=False,
168
168
  userclass_picker_is_active=False,
169
- modern_ui=False,
170
169
  ) -> WebUI:
171
170
  """
172
171
  Creates a :class:`WebUI <locust.web.WebUI>` instance for this Environment and start running the web server
@@ -193,7 +192,6 @@ class Environment:
193
192
  stats_csv_writer=stats_csv_writer,
194
193
  delayed_start=delayed_start,
195
194
  userclass_picker_is_active=userclass_picker_is_active,
196
- modern_ui=modern_ui,
197
195
  )
198
196
  return self.web_ui
199
197
 
locust/html.py CHANGED
@@ -14,10 +14,13 @@ from .stats import sort_stats
14
14
  from .user.inspectuser import get_ratio
15
15
 
16
16
  PERCENTILES_FOR_HTML_REPORT = [0.50, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 1.0]
17
+ ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
18
+ BUILD_PATH = os.path.join(ROOT_PATH, "webui", "dist")
19
+ STATIC_PATH = os.path.join(BUILD_PATH, "assets")
17
20
 
18
21
 
19
- def render_template(file, template_path, **kwargs):
20
- env = Environment(loader=FileSystemLoader(template_path), extensions=["jinja2.ext.do"])
22
+ def render_template(file, **kwargs):
23
+ env = Environment(loader=FileSystemLoader(BUILD_PATH), extensions=["jinja2.ext.do"])
21
24
  template = env.get_template(file)
22
25
  return template.render(**kwargs)
23
26
 
@@ -25,17 +28,8 @@ def render_template(file, template_path, **kwargs):
25
28
  def get_html_report(
26
29
  environment,
27
30
  show_download_link=True,
28
- use_modern_ui=False,
29
31
  theme="",
30
32
  ):
31
- root_path = os.path.dirname(os.path.abspath(__file__))
32
- if use_modern_ui:
33
- static_path = os.path.join(root_path, "webui", "dist", "assets")
34
- template_path = os.path.join(root_path, "webui", "dist")
35
- else:
36
- static_path = os.path.join(root_path, "static")
37
- template_path = os.path.join(root_path, "templates")
38
-
39
33
  stats = environment.runner.stats
40
34
 
41
35
  start_ts = stats.start_time
@@ -63,28 +57,15 @@ def get_html_report(
63
57
  history = stats.history
64
58
 
65
59
  static_js = []
66
- if use_modern_ui:
67
- js_files = [os.path.basename(filepath) for filepath in glob.glob(os.path.join(static_path, "*.js"))]
68
- else:
69
- js_files = ["jquery-1.11.3.min.js", "echarts.common.min.js", "vintage.js", "chart.js", "tasks.js"]
60
+ js_files = [os.path.basename(filepath) for filepath in glob.glob(os.path.join(STATIC_PATH, "*.js"))]
70
61
 
71
62
  for js_file in js_files:
72
- path = os.path.join(static_path, js_file)
63
+ path = os.path.join(STATIC_PATH, js_file)
73
64
  static_js.append("// " + js_file + "\n")
74
65
  with open(path, encoding="utf8") as f:
75
66
  static_js.append(f.read())
76
67
  static_js.extend(["", ""])
77
68
 
78
- if not use_modern_ui:
79
- static_css = []
80
- css_files = ["tables.css"]
81
- for css_file in css_files:
82
- path = os.path.join(static_path, "css", css_file)
83
- static_css.append("/* " + css_file + " */")
84
- with open(path, encoding="utf8") as f:
85
- static_css.append(f.read())
86
- static_css.extend(["", ""])
87
-
88
69
  is_distributed = isinstance(environment.runner, MasterRunner)
89
70
  user_spawned = (
90
71
  environment.runner.reported_user_classes_count if is_distributed else environment.runner.user_classes_count
@@ -98,60 +79,33 @@ def get_html_report(
98
79
  "total": get_ratio(environment.user_classes, user_spawned, True),
99
80
  }
100
81
 
101
- if use_modern_ui:
102
- res = render_template(
103
- "report.html",
104
- template_path,
105
- template_args={
106
- "is_report": True,
107
- "requests_statistics": [stat.to_dict(escape_string_values=True) for stat in requests_statistics],
108
- "failures_statistics": [stat.to_dict() for stat in failures_statistics],
109
- "exceptions_statistics": [stat for stat in exceptions_statistics],
110
- "response_time_statistics": [
111
- {
112
- "name": escape(stat.name),
113
- "method": escape(stat.method or ""),
114
- **{
115
- str(percentile): stat.get_response_time_percentile(percentile)
116
- for percentile in PERCENTILES_FOR_HTML_REPORT
117
- },
118
- }
119
- for stat in requests_statistics
120
- ],
121
- "start_time": start_time,
122
- "end_time": end_time,
123
- "host": escape(str(host)),
124
- "history": history,
125
- "show_download_link": show_download_link,
126
- "locustfile": escape(str(environment.locustfile)),
127
- "tasks": task_data,
128
- "percentiles_to_chart": stats_module.MODERN_UI_PERCENTILES_TO_CHART,
129
- },
130
- theme=theme,
131
- static_js="\n".join(static_js),
132
- )
133
- else:
134
- res = render_template(
135
- "report.html",
136
- template_path,
137
- int=int,
138
- round=round,
139
- escape=escape,
140
- str=str,
141
- requests_statistics=requests_statistics,
142
- failures_statistics=failures_statistics,
143
- exceptions_statistics=exceptions_statistics,
144
- start_time=start_time,
145
- end_time=end_time,
146
- host=host,
147
- history=history,
148
- static_js="\n".join(static_js),
149
- static_css="\n".join(static_css),
150
- show_download_link=show_download_link,
151
- locustfile=environment.locustfile,
152
- tasks=dumps(task_data),
153
- percentile1=stats_module.PERCENTILES_TO_CHART[0],
154
- percentile2=stats_module.PERCENTILES_TO_CHART[1],
155
- )
156
-
157
- return res
82
+ return render_template(
83
+ "report.html",
84
+ template_args={
85
+ "is_report": True,
86
+ "requests_statistics": [stat.to_dict(escape_string_values=True) for stat in requests_statistics],
87
+ "failures_statistics": [stat.to_dict() for stat in failures_statistics],
88
+ "exceptions_statistics": [stat for stat in exceptions_statistics],
89
+ "response_time_statistics": [
90
+ {
91
+ "name": escape(stat.name),
92
+ "method": escape(stat.method or ""),
93
+ **{
94
+ str(percentile): stat.get_response_time_percentile(percentile)
95
+ for percentile in PERCENTILES_FOR_HTML_REPORT
96
+ },
97
+ }
98
+ for stat in requests_statistics
99
+ ],
100
+ "start_time": start_time,
101
+ "end_time": end_time,
102
+ "host": escape(str(host)),
103
+ "history": history,
104
+ "show_download_link": show_download_link,
105
+ "locustfile": escape(str(environment.locustfile)),
106
+ "tasks": task_data,
107
+ "percentiles_to_chart": stats_module.PERCENTILES_TO_CHART,
108
+ },
109
+ theme=theme,
110
+ static_js="\n".join(static_js),
111
+ )
locust/main.py CHANGED
@@ -128,12 +128,8 @@ def main():
128
128
  available_user_classes[key] = value
129
129
  available_user_tasks[key] = value.tasks or {}
130
130
 
131
- if len(stats.PERCENTILES_TO_CHART) != 2:
132
- logging.error("stats.PERCENTILES_TO_CHART parameter should be 2 parameters \n")
133
- sys.exit(1)
134
-
135
- if len(stats.MODERN_UI_PERCENTILES_TO_CHART) > 6:
136
- logging.error("stats.MODERN_UI_PERCENTILES_TO_CHART parameter should be a maximum of 6 parameters \n")
131
+ if len(stats.PERCENTILES_TO_CHART) > 6:
132
+ logging.error("stats.PERCENTILES_TO_CHART parameter should be a maximum of 6 parameters \n")
137
133
  sys.exit(1)
138
134
 
139
135
  def is_valid_percentile(parameter):
@@ -183,7 +179,8 @@ def main():
183
179
  options.spawn_rate = options.hatch_rate
184
180
 
185
181
  if options.legacy_ui:
186
- sys.stderr.write("[DEPRECATED] The legacy UI is deprecated and will be removed soon\n")
182
+ sys.stderr.write("[REMOVED] The legacy UI has been removed. Remove this flag to use the new UI")
183
+ sys.exit(1)
187
184
 
188
185
  # setup logging
189
186
  if not options.skip_log_setup:
@@ -411,8 +408,8 @@ See https://github.com/locustio/locust/wiki/Installation#increasing-maximum-numb
411
408
  if options.worker:
412
409
  logger.error("The --master argument cannot be combined with --worker")
413
410
  sys.exit(-1)
414
- if options.expect_workers_max_wait and not options.expect_workers:
415
- logger.error("The --expect-workers-max-wait argument only makes sense when combined with --expect-workers")
411
+ if options.expect_workers < 1:
412
+ logger.error(f"Invalid --expect-workers argument ({options.expect_workers}), must be a positive number")
416
413
  sys.exit(-1)
417
414
  runner = environment.create_master_runner(
418
415
  master_bind_host=options.master_bind_host,
@@ -480,7 +477,6 @@ See https://github.com/locustio/locust/wiki/Installation#increasing-maximum-numb
480
477
  stats_csv_writer=stats_csv_writer,
481
478
  delayed_start=True,
482
479
  userclass_picker_is_active=options.class_picker,
483
- modern_ui=not options.legacy_ui,
484
480
  )
485
481
  else:
486
482
  web_ui = None
@@ -654,8 +650,8 @@ See https://github.com/locustio/locust/wiki/Installation#increasing-maximum-numb
654
650
  logger.info("Got SIGTERM signal")
655
651
  shutdown()
656
652
 
657
- def save_html_report(use_modern_ui=False):
658
- html_report = get_html_report(environment, show_download_link=False, use_modern_ui=use_modern_ui)
653
+ def save_html_report():
654
+ html_report = get_html_report(environment, show_download_link=False)
659
655
  logger.info("writing html report to file: %s", options.html_file)
660
656
  with open(options.html_file, "w", encoding="utf-8") as file:
661
657
  file.write(html_report)
@@ -671,10 +667,10 @@ See https://github.com/locustio/locust/wiki/Installation#increasing-maximum-numb
671
667
 
672
668
  main_greenlet.join()
673
669
  if options.html_file:
674
- save_html_report(not options.legacy_ui)
670
+ save_html_report()
675
671
  except KeyboardInterrupt:
676
672
  if options.html_file:
677
- save_html_report(not options.legacy_ui)
673
+ save_html_report()
678
674
  except Exception:
679
675
  raise
680
676
  shutdown()
locust/stats.py CHANGED
@@ -126,8 +126,7 @@ CachedResponseTimes = namedtuple("CachedResponseTimes", ["response_times", "num_
126
126
  PERCENTILES_TO_REPORT = [0.50, 0.66, 0.75, 0.80, 0.90, 0.95, 0.98, 0.99, 0.999, 0.9999, 1.0]
127
127
 
128
128
  PERCENTILES_TO_STATISTICS = [0.95, 0.99]
129
- PERCENTILES_TO_CHART = [0.50, 0.95]
130
- MODERN_UI_PERCENTILES_TO_CHART = [0.95]
129
+ PERCENTILES_TO_CHART = [0.95]
131
130
 
132
131
 
133
132
  class RequestStatsAdditionError(Exception):
@@ -695,9 +694,7 @@ class StatsEntry:
695
694
  "current_rps": self.current_rps,
696
695
  "current_fail_per_sec": self.current_fail_per_sec,
697
696
  "median_response_time": self.median_response_time,
698
- "ninetieth_response_time": self.get_response_time_percentile(0.9), # for legacy ui
699
- "ninety_ninth_response_time": self.get_response_time_percentile(0.99), # for legacy ui
700
- **response_time_percentiles, # for modern ui
697
+ **response_time_percentiles,
701
698
  "avg_content_length": self.avg_content_length,
702
699
  }
703
700
 
@@ -920,7 +917,7 @@ def stats_history(runner: Runner) -> None:
920
917
  current_response_time_percentiles = {
921
918
  f"response_time_percentile_{percentile}": stats.total.get_current_response_time_percentile(percentile)
922
919
  or 0
923
- for percentile in MODERN_UI_PERCENTILES_TO_CHART
920
+ for percentile in PERCENTILES_TO_CHART
924
921
  }
925
922
 
926
923
  r = {
@@ -928,10 +925,6 @@ def stats_history(runner: Runner) -> None:
928
925
  "time": datetime.datetime.now(tz=datetime.timezone.utc).strftime("%H:%M:%S"),
929
926
  "current_rps": stats.total.current_rps or 0,
930
927
  "current_fail_per_sec": stats.total.current_fail_per_sec or 0,
931
- "response_time_percentile_1": stats.total.get_current_response_time_percentile(PERCENTILES_TO_CHART[0])
932
- or 0,
933
- "response_time_percentile_2": stats.total.get_current_response_time_percentile(PERCENTILES_TO_CHART[1])
934
- or 0,
935
928
  "total_avg_response_time": stats.total.avg_response_time,
936
929
  "user_count": runner.user_count or 0,
937
930
  }
@@ -11,6 +11,7 @@ from tempfile import NamedTemporaryFile
11
11
 
12
12
  import gevent
13
13
  from geventhttpclient.client import HTTPClientPool
14
+ from pyquery import PyQuery as pq
14
15
 
15
16
  from .testcases import LocustTestCase, WebserverTestCase
16
17
  from .util import create_tls_cert
@@ -777,7 +778,8 @@ class TestFastHttpSsl(LocustTestCase):
777
778
 
778
779
  def test_ssl_request_insecure(self):
779
780
  s = FastHttpSession(self.environment, "https://127.0.0.1:%i" % self.web_port, insecure=True, user=None)
780
- r = s.get("/")
781
- self.assertEqual(200, r.status_code)
782
- self.assertIn("<title>Locust for None</title>", r.content.decode("utf-8"))
783
- self.assertIn("<p>Script: <span>None</span></p>", r.text)
781
+ response = s.get("/")
782
+ d = pq(response.content.decode("utf-8"))
783
+
784
+ self.assertEqual(200, response.status_code)
785
+ self.assertIn('"users": null', str(d))
locust/test/test_main.py CHANGED
@@ -219,9 +219,8 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
219
219
  content=textwrap.dedent(
220
220
  """
221
221
  from locust import User, task, constant, events
222
- from locust.stats import PERCENTILES_TO_CHART
223
- PERCENTILES_TO_CHART[0] = 0.9
224
- PERCENTILES_TO_CHART[1] = 0.4
222
+ from locust import stats
223
+ stats.PERCENTILES_TO_CHART = [0.9, 0.4]
225
224
  class TestUser(User):
226
225
  wait_time = constant(3)
227
226
  @task
@@ -274,8 +273,8 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
274
273
  content=textwrap.dedent(
275
274
  """
276
275
  from locust import User, task, constant, events
277
- from locust.stats import PERCENTILES_TO_CHART
278
- PERCENTILES_TO_CHART[0] = 1.2
276
+ from locust import stats
277
+ stats.PERCENTILES_TO_CHART = [1.2]
279
278
  class TestUser(User):
280
279
  wait_time = constant(3)
281
280
  @task
@@ -728,7 +727,6 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
728
727
  "locust",
729
728
  "-f",
730
729
  mocked.file_path,
731
- "--legacy-ui",
732
730
  "--web-port",
733
731
  str(port),
734
732
  "--autostart",
@@ -795,7 +793,6 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
795
793
  "locust",
796
794
  "-f",
797
795
  f"{mocked1.file_path},{mocked2}",
798
- "--legacy-ui",
799
796
  "--web-port",
800
797
  str(port),
801
798
  "--autostart",
@@ -1115,7 +1112,6 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
1115
1112
  "locust",
1116
1113
  "-f",
1117
1114
  mocked.file_path,
1118
- "--legacy-ui",
1119
1115
  "--host",
1120
1116
  "https://test.com/",
1121
1117
  "--run-time",
@@ -1138,16 +1134,11 @@ class StandaloneIntegrationTests(ProcessIntegrationTest):
1138
1134
 
1139
1135
  # make sure title appears in the report
1140
1136
  _, locustfile = os.path.split(mocked.file_path)
1141
- self.assertIn(f"<title>Test Report for {locustfile}</title>", html_report_content)
1142
- self.assertIn(f"<p>Script: <span>{locustfile}</span></p>", html_report_content)
1137
+ self.assertIn(locustfile, html_report_content)
1143
1138
 
1144
1139
  # make sure host appears in the report
1145
1140
  self.assertIn("https://test.com/", html_report_content)
1146
-
1147
- # make sure the charts container appears in the report
1148
- self.assertIn("charts-container", html_report_content)
1149
-
1150
- self.assertNotIn("Download the Report", html_report_content, "Download report link found in HTML content")
1141
+ self.assertIn('"show_download_link": false', html_report_content)
1151
1142
 
1152
1143
  def test_run_with_userclass_picker(self):
1153
1144
  with temporary_file(content=MOCK_LOCUSTFILE_CONTENT_A) as file1: