locust 2.24.2.dev19__tar.gz → 2.25.0__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.
Files changed (351) hide show
  1. {locust-2.24.2.dev19 → locust-2.25.0}/PKG-INFO +1 -1
  2. {locust-2.24.2.dev19 → locust-2.25.0}/docs/changelog.rst +7 -0
  3. {locust-2.24.2.dev19 → locust-2.25.0}/docs/extending-locust.rst +14 -3
  4. locust-2.25.0/docs/images/extend_modern_web_ui_cache_stats.png +0 -0
  5. {locust-2.24.2.dev19 → locust-2.25.0}/docs/quickstart.rst +2 -2
  6. {locust-2.24.2.dev19 → locust-2.25.0}/docs/writing-a-locustfile.rst +11 -9
  7. {locust-2.24.2.dev19 → locust-2.25.0}/examples/extend_web_ui/extend.py +7 -0
  8. locust-2.25.0/examples/web_ui_cache_stats.py +210 -0
  9. {locust-2.24.2.dev19 → locust-2.25.0}/locust/_version.py +2 -2
  10. {locust-2.24.2.dev19 → locust-2.25.0}/locust/dispatch.py +44 -9
  11. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_dispatch.py +69 -0
  12. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/PKG-INFO +1 -1
  13. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/SOURCES.txt +2 -0
  14. {locust-2.24.2.dev19 → locust-2.25.0}/.coveragerc +0 -0
  15. {locust-2.24.2.dev19 → locust-2.25.0}/.dockerignore +0 -0
  16. {locust-2.24.2.dev19 → locust-2.25.0}/.git-blame-ignore-revs +0 -0
  17. {locust-2.24.2.dev19 → locust-2.25.0}/.gitattributes +0 -0
  18. {locust-2.24.2.dev19 → locust-2.25.0}/.github/CONTRIBUTING.md +0 -0
  19. {locust-2.24.2.dev19 → locust-2.25.0}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  20. {locust-2.24.2.dev19 → locust-2.25.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  21. {locust-2.24.2.dev19 → locust-2.25.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  22. {locust-2.24.2.dev19 → locust-2.25.0}/.github/workflows/stale.yml +0 -0
  23. {locust-2.24.2.dev19 → locust-2.25.0}/.github/workflows/tests.yml +0 -0
  24. {locust-2.24.2.dev19 → locust-2.25.0}/.gitignore +0 -0
  25. {locust-2.24.2.dev19 → locust-2.25.0}/.readthedocs.yaml +0 -0
  26. {locust-2.24.2.dev19 → locust-2.25.0}/.vscode/extensions.json +0 -0
  27. {locust-2.24.2.dev19 → locust-2.25.0}/.vscode/launch.json +0 -0
  28. {locust-2.24.2.dev19 → locust-2.25.0}/.vscode/launch_locust.json +0 -0
  29. {locust-2.24.2.dev19 → locust-2.25.0}/.vscode/settings.json +0 -0
  30. {locust-2.24.2.dev19 → locust-2.25.0}/CHANGELOG.md +0 -0
  31. {locust-2.24.2.dev19 → locust-2.25.0}/Dockerfile +0 -0
  32. {locust-2.24.2.dev19 → locust-2.25.0}/LICENSE +0 -0
  33. {locust-2.24.2.dev19 → locust-2.25.0}/MANIFEST.in +0 -0
  34. {locust-2.24.2.dev19 → locust-2.25.0}/Makefile +0 -0
  35. {locust-2.24.2.dev19 → locust-2.25.0}/README +0 -0
  36. {locust-2.24.2.dev19 → locust-2.25.0}/README.md +0 -0
  37. {locust-2.24.2.dev19 → locust-2.25.0}/SECURITY.md +0 -0
  38. {locust-2.24.2.dev19 → locust-2.25.0}/Vagrantfile +0 -0
  39. {locust-2.24.2.dev19 → locust-2.25.0}/benchmarks/dispatch.py +0 -0
  40. {locust-2.24.2.dev19 → locust-2.25.0}/docs/_static/theme-overrides.css +0 -0
  41. {locust-2.24.2.dev19 → locust-2.25.0}/docs/api.rst +0 -0
  42. {locust-2.24.2.dev19 → locust-2.25.0}/docs/conf.py +0 -0
  43. {locust-2.24.2.dev19 → locust-2.25.0}/docs/configuration.rst +0 -0
  44. {locust-2.24.2.dev19 → locust-2.25.0}/docs/custom-load-shape.rst +0 -0
  45. {locust-2.24.2.dev19 → locust-2.25.0}/docs/developing-locust.rst +0 -0
  46. {locust-2.24.2.dev19 → locust-2.25.0}/docs/further-reading.rst +0 -0
  47. {locust-2.24.2.dev19 → locust-2.25.0}/docs/history.rst +0 -0
  48. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/locust_workers.png +0 -0
  49. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/number_of_users.png +0 -0
  50. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/response_times.png +0 -0
  51. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/total_requests_per_second.png +0 -0
  52. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/userclass_picker_example.png +0 -0
  53. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/webui-running-statistics.png +0 -0
  54. {locust-2.24.2.dev19 → locust-2.25.0}/docs/images/webui-splash-screenshot.png +0 -0
  55. {locust-2.24.2.dev19 → locust-2.25.0}/docs/increase-performance.rst +0 -0
  56. {locust-2.24.2.dev19 → locust-2.25.0}/docs/index.rst +0 -0
  57. {locust-2.24.2.dev19 → locust-2.25.0}/docs/installation.rst +0 -0
  58. {locust-2.24.2.dev19 → locust-2.25.0}/docs/logging.rst +0 -0
  59. {locust-2.24.2.dev19 → locust-2.25.0}/docs/requirements.txt +0 -0
  60. {locust-2.24.2.dev19 → locust-2.25.0}/docs/retrieving-stats.rst +0 -0
  61. {locust-2.24.2.dev19 → locust-2.25.0}/docs/running-cloud-integration.rst +0 -0
  62. {locust-2.24.2.dev19 → locust-2.25.0}/docs/running-distributed.rst +0 -0
  63. {locust-2.24.2.dev19 → locust-2.25.0}/docs/running-in-debugger.rst +0 -0
  64. {locust-2.24.2.dev19 → locust-2.25.0}/docs/running-in-docker.rst +0 -0
  65. {locust-2.24.2.dev19 → locust-2.25.0}/docs/running-without-web-ui.rst +0 -0
  66. {locust-2.24.2.dev19 → locust-2.25.0}/docs/tasksets.rst +0 -0
  67. {locust-2.24.2.dev19 → locust-2.25.0}/docs/testing-other-systems.rst +0 -0
  68. {locust-2.24.2.dev19 → locust-2.25.0}/docs/use-as-lib.rst +0 -0
  69. {locust-2.24.2.dev19 → locust-2.25.0}/docs/what-is-locust.rst +0 -0
  70. {locust-2.24.2.dev19 → locust-2.25.0}/examples/add_command_line_argument.py +0 -0
  71. {locust-2.24.2.dev19 → locust-2.25.0}/examples/basic.py +0 -0
  72. {locust-2.24.2.dev19 → locust-2.25.0}/examples/browse_docs_sequence_test.py +0 -0
  73. {locust-2.24.2.dev19 → locust-2.25.0}/examples/browse_docs_test.py +0 -0
  74. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_messages.py +0 -0
  75. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_shape/double_wave.py +0 -0
  76. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_shape/stages.py +0 -0
  77. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_shape/staging_user_classes.py +0 -0
  78. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_shape/step_load.py +0 -0
  79. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_shape/wait_user_count.py +0 -0
  80. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_wait_function.py +0 -0
  81. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_xmlrpc_client/server.py +0 -0
  82. {locust-2.24.2.dev19 → locust-2.25.0}/examples/custom_xmlrpc_client/xmlrpc_locustfile.py +0 -0
  83. {locust-2.24.2.dev19 → locust-2.25.0}/examples/debugging.py +0 -0
  84. {locust-2.24.2.dev19 → locust-2.25.0}/examples/debugging_advanced.py +0 -0
  85. {locust-2.24.2.dev19 → locust-2.25.0}/examples/docker-compose/docker-compose.yml +0 -0
  86. {locust-2.24.2.dev19 → locust-2.25.0}/examples/dynamic_user_credentials.py +0 -0
  87. {locust-2.24.2.dev19 → locust-2.25.0}/examples/events.py +0 -0
  88. {locust-2.24.2.dev19 → locust-2.25.0}/examples/extend_modern_web_ui.py +0 -0
  89. {locust-2.24.2.dev19 → locust-2.25.0}/examples/extend_web_ui/static/custom-stats-table.css +0 -0
  90. {locust-2.24.2.dev19 → locust-2.25.0}/examples/extend_web_ui/static/extend.js +0 -0
  91. {locust-2.24.2.dev19 → locust-2.25.0}/examples/extend_web_ui/templates/extend.html +0 -0
  92. {locust-2.24.2.dev19 → locust-2.25.0}/examples/fast_http_locust.py +0 -0
  93. {locust-2.24.2.dev19 → locust-2.25.0}/examples/grpc/grpc_user.py +0 -0
  94. {locust-2.24.2.dev19 → locust-2.25.0}/examples/grpc/hello.proto +0 -0
  95. {locust-2.24.2.dev19 → locust-2.25.0}/examples/grpc/hello_pb2.py +0 -0
  96. {locust-2.24.2.dev19 → locust-2.25.0}/examples/grpc/hello_pb2_grpc.py +0 -0
  97. {locust-2.24.2.dev19 → locust-2.25.0}/examples/grpc/hello_server.py +0 -0
  98. {locust-2.24.2.dev19 → locust-2.25.0}/examples/grpc/locustfile.py +0 -0
  99. {locust-2.24.2.dev19 → locust-2.25.0}/examples/locustfile.py +0 -0
  100. {locust-2.24.2.dev19 → locust-2.25.0}/examples/manual_stats_reporting.py +0 -0
  101. {locust-2.24.2.dev19 → locust-2.25.0}/examples/multiple_hosts.py +0 -0
  102. {locust-2.24.2.dev19 → locust-2.25.0}/examples/nested_inline_tasksets.py +0 -0
  103. {locust-2.24.2.dev19 → locust-2.25.0}/examples/rest.py +0 -0
  104. {locust-2.24.2.dev19 → locust-2.25.0}/examples/sdk_session_patching/session_patch_locustfile.py +0 -0
  105. {locust-2.24.2.dev19 → locust-2.25.0}/examples/semaphore_wait.py +0 -0
  106. {locust-2.24.2.dev19 → locust-2.25.0}/examples/stop_on_threshold.py +0 -0
  107. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/README.md +0 -0
  108. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/data_subnet.tf +0 -0
  109. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/main.tf +0 -0
  110. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/output.tf +0 -0
  111. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/plan/basic.py +0 -0
  112. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/provisioner.tf +0 -0
  113. {locust-2.24.2.dev19 → locust-2.25.0}/examples/terraform/aws/variables.tf +0 -0
  114. {locust-2.24.2.dev19 → locust-2.25.0}/examples/test_data_management.py +0 -0
  115. {locust-2.24.2.dev19 → locust-2.25.0}/examples/use_as_lib.py +0 -0
  116. {locust-2.24.2.dev19 → locust-2.25.0}/examples/vagrant/README.md +0 -0
  117. {locust-2.24.2.dev19 → locust-2.25.0}/examples/vagrant/supervisord.conf +0 -0
  118. {locust-2.24.2.dev19 → locust-2.25.0}/examples/web_ui_auth.py +0 -0
  119. {locust-2.24.2.dev19 → locust-2.25.0}/examples/worker_index.py +0 -0
  120. {locust-2.24.2.dev19 → locust-2.25.0}/generate_changelog.py +0 -0
  121. {locust-2.24.2.dev19 → locust-2.25.0}/locust/__init__.py +0 -0
  122. {locust-2.24.2.dev19 → locust-2.25.0}/locust/__main__.py +0 -0
  123. {locust-2.24.2.dev19 → locust-2.25.0}/locust/argument_parser.py +0 -0
  124. {locust-2.24.2.dev19 → locust-2.25.0}/locust/clients.py +0 -0
  125. {locust-2.24.2.dev19 → locust-2.25.0}/locust/contrib/__init__.py +0 -0
  126. {locust-2.24.2.dev19 → locust-2.25.0}/locust/contrib/fasthttp.py +0 -0
  127. {locust-2.24.2.dev19 → locust-2.25.0}/locust/debug.py +0 -0
  128. {locust-2.24.2.dev19 → locust-2.25.0}/locust/env.py +0 -0
  129. {locust-2.24.2.dev19 → locust-2.25.0}/locust/event.py +0 -0
  130. {locust-2.24.2.dev19 → locust-2.25.0}/locust/exception.py +0 -0
  131. {locust-2.24.2.dev19 → locust-2.25.0}/locust/html.py +0 -0
  132. {locust-2.24.2.dev19 → locust-2.25.0}/locust/input_events.py +0 -0
  133. {locust-2.24.2.dev19 → locust-2.25.0}/locust/log.py +0 -0
  134. {locust-2.24.2.dev19 → locust-2.25.0}/locust/main.py +0 -0
  135. {locust-2.24.2.dev19 → locust-2.25.0}/locust/py.typed +0 -0
  136. {locust-2.24.2.dev19 → locust-2.25.0}/locust/rpc/__init__.py +0 -0
  137. {locust-2.24.2.dev19 → locust-2.25.0}/locust/rpc/protocol.py +0 -0
  138. {locust-2.24.2.dev19 → locust-2.25.0}/locust/rpc/zmqrpc.py +0 -0
  139. {locust-2.24.2.dev19 → locust-2.25.0}/locust/runners.py +0 -0
  140. {locust-2.24.2.dev19 → locust-2.25.0}/locust/shape.py +0 -0
  141. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/chart.js +0 -0
  142. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/css/application.css +0 -0
  143. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/css/application.css.map +0 -0
  144. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/css/tables.css +0 -0
  145. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/css/tables.css.map +0 -0
  146. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/echarts.common.min.js +0 -0
  147. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/img/favicon.ico +0 -0
  148. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/img/logo.png +0 -0
  149. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/img/ui-screenshot-charts.png +0 -0
  150. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/img/ui-screenshot-start-test.png +0 -0
  151. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/img/ui-screenshot-stats.png +0 -0
  152. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/img/ui-screenshot-workers.png +0 -0
  153. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/jquery-1.11.3.min.js +0 -0
  154. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/jquery.jqote2.min.js +0 -0
  155. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/jquery.tools.min.js +0 -0
  156. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/locust.js +0 -0
  157. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/sass/_base.sass +0 -0
  158. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/sass/_mixins.sass +0 -0
  159. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/sass/application.sass +0 -0
  160. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/sass/tables.sass +0 -0
  161. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/tasks.js +0 -0
  162. {locust-2.24.2.dev19 → locust-2.25.0}/locust/static/vintage.js +0 -0
  163. {locust-2.24.2.dev19 → locust-2.25.0}/locust/stats.py +0 -0
  164. {locust-2.24.2.dev19 → locust-2.25.0}/locust/templates/index.html +0 -0
  165. {locust-2.24.2.dev19 → locust-2.25.0}/locust/templates/report.html +0 -0
  166. {locust-2.24.2.dev19 → locust-2.25.0}/locust/templates/stats_data.html +0 -0
  167. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/__init__.py +0 -0
  168. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/fake_module1_for_env_test.py +0 -0
  169. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/fake_module2_for_env_test.py +0 -0
  170. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/mock_locustfile.py +0 -0
  171. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/mock_logging.py +0 -0
  172. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_debugging.py +0 -0
  173. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_env.py +0 -0
  174. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_fasthttp.py +0 -0
  175. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_http.py +0 -0
  176. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_interruptable_task.py +0 -0
  177. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_load_locustfile.py +0 -0
  178. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_locust_class.py +0 -0
  179. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_log.py +0 -0
  180. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_main.py +0 -0
  181. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_old_wait_api.py +0 -0
  182. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_parser.py +0 -0
  183. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_runners.py +0 -0
  184. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_sequential_taskset.py +0 -0
  185. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_stats.py +0 -0
  186. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_tags.py +0 -0
  187. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_taskratio.py +0 -0
  188. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_users.py +0 -0
  189. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_util.py +0 -0
  190. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_wait_time.py +0 -0
  191. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_web.py +0 -0
  192. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/test_zmqrpc.py +0 -0
  193. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/testcases.py +0 -0
  194. {locust-2.24.2.dev19 → locust-2.25.0}/locust/test/util.py +0 -0
  195. {locust-2.24.2.dev19 → locust-2.25.0}/locust/user/__init__.py +0 -0
  196. {locust-2.24.2.dev19 → locust-2.25.0}/locust/user/inspectuser.py +0 -0
  197. {locust-2.24.2.dev19 → locust-2.25.0}/locust/user/sequential_taskset.py +0 -0
  198. {locust-2.24.2.dev19 → locust-2.25.0}/locust/user/task.py +0 -0
  199. {locust-2.24.2.dev19 → locust-2.25.0}/locust/user/users.py +0 -0
  200. {locust-2.24.2.dev19 → locust-2.25.0}/locust/user/wait_time.py +0 -0
  201. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/__init__.py +0 -0
  202. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/cache.py +0 -0
  203. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/deprecation.py +0 -0
  204. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/exception_handler.py +0 -0
  205. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/load_locustfile.py +0 -0
  206. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/rounding.py +0 -0
  207. {locust-2.24.2.dev19 → locust-2.25.0}/locust/util/timespan.py +0 -0
  208. {locust-2.24.2.dev19 → locust-2.25.0}/locust/web.py +0 -0
  209. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/.eslintrc +0 -0
  210. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/.gitignore +0 -0
  211. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/.prettierrc +0 -0
  212. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/auth.html +0 -0
  213. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dev.html +0 -0
  214. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dist/assets/favicon.ico +0 -0
  215. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dist/assets/index-941b6e82.js +0 -0
  216. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dist/assets/logo.png +0 -0
  217. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dist/auth.html +0 -0
  218. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dist/index.html +0 -0
  219. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/dist/report.html +0 -0
  220. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/index.html +0 -0
  221. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/package.json +0 -0
  222. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/public/assets/favicon.ico +0 -0
  223. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/public/assets/logo.png +0 -0
  224. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/public/report.html +0 -0
  225. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/App.test.tsx +0 -0
  226. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/App.tsx +0 -0
  227. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/DataTable/DataTable.test.tsx +0 -0
  228. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/DataTable/DataTable.tsx +0 -0
  229. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/ExceptionsTable/ExceptionsTable.tsx +0 -0
  230. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/FailuresTable/FailuresTable.tsx +0 -0
  231. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/FallbackRender/FallbackRender.test.tsx +0 -0
  232. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/FallbackRender/FallbackRender.tsx +0 -0
  233. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Form/Form.tsx +0 -0
  234. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Form/Select.tsx +0 -0
  235. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Form/tests/Form.test.tsx +0 -0
  236. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Form/tests/Select.test.tsx +0 -0
  237. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Footer/About.tsx +0 -0
  238. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Footer/Footer.tsx +0 -0
  239. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Layout.tsx +0 -0
  240. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Navbar/DarkLightToggle.tsx +0 -0
  241. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Navbar/Navbar.tsx +0 -0
  242. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Navbar/SwarmMonitor.test.tsx +0 -0
  243. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Layout/Navbar/SwarmMonitor.tsx +0 -0
  244. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/LineChart/LineChart.test.tsx +0 -0
  245. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/LineChart/LineChart.tsx +0 -0
  246. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/LogViewer/LogViewer.tsx +0 -0
  247. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/LogViewer/tests/LogViewer.test.tsx +0 -0
  248. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/LogViewer/tests/useLogViewer.test.tsx +0 -0
  249. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/LogViewer/useLogViewer.ts +0 -0
  250. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Modal/Modal.tsx +0 -0
  251. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Reports/Reports.test.tsx +0 -0
  252. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Reports/Reports.tsx +0 -0
  253. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/ResponseTimeTable/ResponseTimeTable.test.tsx +0 -0
  254. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/ResponseTimeTable/ResponseTimeTable.tsx +0 -0
  255. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/EditButton.tsx +0 -0
  256. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/NewTestButton.tsx +0 -0
  257. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/ResetButton.tsx +0 -0
  258. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/StateButtons.tsx +0 -0
  259. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/StopButton.tsx +0 -0
  260. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/tests/ResetButton.test.tsx +0 -0
  261. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/tests/StateButtons.test.tsx +0 -0
  262. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StateButtons/tests/StopButton.test.tsx +0 -0
  263. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/StatsTable/StatsTable.tsx +0 -0
  264. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmCharts/SwarmCharts.tsx +0 -0
  265. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/SwarmCustomParameters.tsx +0 -0
  266. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/SwarmEditForm.tsx +0 -0
  267. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/SwarmForm.tsx +0 -0
  268. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/SwarmUserClassPicker.tsx +0 -0
  269. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/tests/SwarmCustomParameters.test.tsx +0 -0
  270. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/tests/SwarmEditForm.test.tsx +0 -0
  271. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/tests/SwarmForm.test.tsx +0 -0
  272. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmForm/tests/SwarmUserClassPicker.test.tsx +0 -0
  273. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmRatios/SwarmRatios.test.tsx +0 -0
  274. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/SwarmRatios/SwarmRatios.tsx +0 -0
  275. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Table/Table.test.tsx +0 -0
  276. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Table/Table.tsx +0 -0
  277. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Tabs/Tabs.constants.tsx +0 -0
  278. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Tabs/Tabs.test.tsx +0 -0
  279. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/Tabs/Tabs.tsx +0 -0
  280. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/ViewColumnSelector/ViewColumnSelector.test.tsx +0 -0
  281. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/ViewColumnSelector/ViewColumnSelector.tsx +0 -0
  282. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/components/WorkersTable/WorkersTable.tsx +0 -0
  283. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/constants/auth.ts +0 -0
  284. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/constants/swarm.ts +0 -0
  285. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/constants/theme.ts +0 -0
  286. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/global.d.ts +0 -0
  287. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/tests/useNotifications.test.tsx +0 -0
  288. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/tests/useSelecteViewColumns.test.tsx +0 -0
  289. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/tests/useSortByField.test.tsx +0 -0
  290. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/tests/useSwarmUi.test.tsx +0 -0
  291. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/useInterval.ts +0 -0
  292. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/useNotifications.ts +0 -0
  293. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/useSelectViewColumns.ts +0 -0
  294. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/useSortByField.ts +0 -0
  295. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/hooks/useSwarmUi.ts +0 -0
  296. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/index.tsx +0 -0
  297. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/pages/Auth.tsx +0 -0
  298. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/pages/Dashboard.tsx +0 -0
  299. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/pages/HtmlReport.tsx +0 -0
  300. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/pages/tests/Auth.test.tsx +0 -0
  301. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/pages/tests/Dashboard.test.tsx +0 -0
  302. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/pages/tests/HtmlReport.test.tsx +0 -0
  303. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/api/swarm.ts +0 -0
  304. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/hooks.ts +0 -0
  305. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/logViewer.slice.ts +0 -0
  306. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/notification.slice.ts +0 -0
  307. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/root.slice.ts +0 -0
  308. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/swarm.slice.ts +0 -0
  309. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/tests/ui.slice.test.ts +0 -0
  310. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/theme.slice.ts +0 -0
  311. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/ui.slice.ts +0 -0
  312. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/slice/url.slice.ts +0 -0
  313. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/store.ts +0 -0
  314. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/redux/utils.ts +0 -0
  315. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/styles/theme.ts +0 -0
  316. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/test/constants.ts +0 -0
  317. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/test/mocks/statsRequest.mock.ts +0 -0
  318. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/test/mocks/swarmState.mock.ts +0 -0
  319. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/test/setup.ts +0 -0
  320. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/test/testUtils.tsx +0 -0
  321. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/types/auth.types.ts +0 -0
  322. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/types/swarm.types.ts +0 -0
  323. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/types/tab.types.ts +0 -0
  324. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/types/table.types.ts +0 -0
  325. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/types/ui.types.ts +0 -0
  326. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/number.ts +0 -0
  327. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/object.ts +0 -0
  328. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/string.ts +0 -0
  329. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/tests/number.test.ts +0 -0
  330. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/tests/object.test.ts +0 -0
  331. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/tests/string.test.ts +0 -0
  332. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/tests/url.test.ts +0 -0
  333. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/src/utils/url.ts +0 -0
  334. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/tsconfig.json +0 -0
  335. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/vite.config.ts +0 -0
  336. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/vitest.config.ts +0 -0
  337. {locust-2.24.2.dev19 → locust-2.25.0}/locust/webui/yarn.lock +0 -0
  338. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/dependency_links.txt +0 -0
  339. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/entry_points.txt +0 -0
  340. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/not-zip-safe +0 -0
  341. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/requires.txt +0 -0
  342. {locust-2.24.2.dev19 → locust-2.25.0}/locust.egg-info/top_level.txt +0 -0
  343. {locust-2.24.2.dev19 → locust-2.25.0}/package.json +0 -0
  344. {locust-2.24.2.dev19 → locust-2.25.0}/pyproject.toml +0 -0
  345. {locust-2.24.2.dev19 → locust-2.25.0}/scripts/locustfile.py +0 -0
  346. {locust-2.24.2.dev19 → locust-2.25.0}/scripts/run-disributed-headless.sh +0 -0
  347. {locust-2.24.2.dev19 → locust-2.25.0}/scripts/run-disributed-web.sh +0 -0
  348. {locust-2.24.2.dev19 → locust-2.25.0}/scripts/run-local-headless.sh +0 -0
  349. {locust-2.24.2.dev19 → locust-2.25.0}/scripts/run-local-web.sh +0 -0
  350. {locust-2.24.2.dev19 → locust-2.25.0}/setup.cfg +0 -0
  351. {locust-2.24.2.dev19 → locust-2.25.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: locust
3
- Version: 2.24.2.dev19
3
+ Version: 2.25.0
4
4
  Summary: Developer friendly load testing framework
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/locustio/locust
@@ -4,6 +4,13 @@ Changelog Highlights
4
4
 
5
5
  For full details of the Locust changelog, please see https://github.com/locustio/locust/blob/master/CHANGELOG.md
6
6
 
7
+ 2.25.0
8
+ ======
9
+ * Add functionality to run listener functions for `custom_messages` concurrently (https://github.com/locustio/locust/pull/2650)
10
+ * Update User Classes in Distributed Mode (https://github.com/locustio/locust/pull/2666)
11
+ * Log deprecation warning for --legacy-ui (https://github.com/locustio/locust/pull/2670)
12
+ * Fix UserClasses weight distribution with gcd (https://github.com/locustio/locust/pull/2663)
13
+
7
14
  2.24.1
8
15
  ======
9
16
  * Some documentation updates & minor fixes to UI
@@ -53,6 +53,7 @@ To see a full list of available events see :ref:`events`.
53
53
 
54
54
  .. _request_context:
55
55
 
56
+
56
57
  Request context
57
58
  ===============
58
59
 
@@ -110,6 +111,10 @@ to the Flask app instance and use that to set up a new route::
110
111
 
111
112
  You should now be able to start locust and browse to http://127.0.0.1:8089/added_page
112
113
 
114
+ .. note::
115
+
116
+ Please note that at the moment Locust does not show the extended Web UI under the default root path `"/"`.
117
+ To view your extensions, navigate to the newly added web route. In this example, `"/added_page"`.
113
118
 
114
119
 
115
120
  Extending Web UI
@@ -123,9 +128,14 @@ as it involves also writing and including HTML and Javascript files to be served
123
128
  greatly enhance the utility and customizability of the web UI.
124
129
 
125
130
  A working example of extending the web UI, complete with HTML and Javascript example files, can be found
126
- in the `examples directory <https://github.com/locustio/locust/tree/master/examples>`_ of the Locust
131
+ in the `examples directory <https://github.com/locustio/locust/tree/master/examples/>`_ of the Locust
127
132
  source code.
128
133
 
134
+ * ``extend_modern_web_ui.py``: Display a table with content-length for each call.
135
+
136
+ * ``web_ui_cache_stats.py``: Display Varnish Hit/ Miss stats for each call. Could be easly extended to other CDN or cache proxies and gather other cache statistics such as cache age, control, ...
137
+
138
+ .. image:: images/extend_modern_web_ui_cache_stats.png
129
139
 
130
140
 
131
141
  Adding Authentication to the Web UI
@@ -149,8 +159,6 @@ authentication to the app should be granted.
149
159
  A full example can be seen `in the auth example <https://github.com/locustio/locust/tree/master/examples/web_ui_auth.py>`_.
150
160
 
151
161
 
152
-
153
-
154
162
  Run a background greenlet
155
163
  =========================
156
164
 
@@ -182,6 +190,7 @@ For example, you can monitor the fail ratio of your test and stop the run if it
182
190
 
183
191
  .. _parametrizing-locustfiles:
184
192
 
193
+
185
194
  Parametrizing locustfiles
186
195
  =========================
187
196
 
@@ -224,11 +233,13 @@ You can add your own command line arguments to Locust, using the :py:attr:`init_
224
233
 
225
234
  When running Locust :ref:`distributed <running-distributed>`, custom arguments are automatically forwarded to workers when the run is started (but not before then, so you cannot rely on forwarded arguments *before* the test has actually started).
226
235
 
236
+
227
237
  Test data management
228
238
  ====================
229
239
 
230
240
  There are a number of ways to get test data into your tests (after all, your test is just a Python program and it can do whatever Python can). Locust's events give you fine-grained control over *when* to fetch/release test data. You can find a `detailed example here <https://github.com/locustio/locust/tree/master/examples/test_data_management.py>`_.
231
241
 
242
+
232
243
  More examples
233
244
  =============
234
245
 
@@ -16,7 +16,7 @@ A Locust test is essentially just a Python program making requests to the system
16
16
  self.client.get("/hello")
17
17
  self.client.get("/world")
18
18
 
19
- This user will make HTTP requests to ``/hello``, and then ``/world``, again and again. For a full explanation and a more realistic example see :ref:`writing-a-locustfile`.
19
+ This user will make an HTTP request to ``/hello``, and after that to ``/world``, and then repeat. For a full explanation and a more realistic example see :ref:`writing-a-locustfile`.
20
20
 
21
21
  Change ``/hello`` and ``/world`` to some actual paths on the web site/service you want to test, put the code in a file named ``locustfile.py`` in your current directory and then run ``locust``:
22
22
 
@@ -24,7 +24,7 @@ Change ``/hello`` and ``/world`` to some actual paths on the web site/service yo
24
24
  :substitutions:
25
25
 
26
26
  $ locust
27
- [2021-07-24 09:58:46,215] .../INFO/locust.main: Starting web interface at http://*:8089
27
+ [2021-07-24 09:58:46,215] .../INFO/locust.main: Starting web interface at http://0.0.0.0:8089
28
28
  [2021-07-24 09:58:46,285] .../INFO/locust.main: Starting Locust |version|
29
29
 
30
30
  Locust's web interface
@@ -62,28 +62,30 @@ is executed. For more info see :ref:`wait-time`.
62
62
 
63
63
  @task
64
64
  def hello_world(self):
65
- ...
65
+ self.client.get("/hello")
66
+ self.client.get("/world")
66
67
 
67
- Methods decorated with ``@task`` are the core of your locust file. For every running user,
68
- Locust creates a greenlet (micro-thread), that will call those methods.
68
+ Methods decorated with ``@task`` are the core of your locust file. For every running User,
69
+ Locust creates a `greenlet <https://greenlet.readthedocs.io/en/stable/greenlet.html>`_ (a coroutine or "micro-thread"), that will call those methods.
70
+ Code within a task is executed sequentially (it is just regular Python code),
71
+ so ``/world`` wont be called until the response from ``/hello`` has been received.
69
72
 
70
73
  .. code-block:: python
71
74
 
72
75
  @task
73
76
  def hello_world(self):
74
- self.client.get("/hello")
75
- self.client.get("/world")
76
-
77
+ ...
78
+
77
79
  @task(3)
78
80
  def view_items(self):
79
- ...
81
+ ...
80
82
 
81
83
  We've declared two tasks by decorating two methods with ``@task``, one of which has been given a higher weight (3).
82
84
  When our ``QuickstartUser`` runs it'll pick one of the declared tasks - in this case either ``hello_world`` or
83
85
  ``view_items`` - and execute it. Tasks are picked at random, but you can give them different weighting. The above
84
86
  configuration will make Locust three times more likely to pick ``view_items`` than ``hello_world``. When a task has
85
- finished executing, the User will then sleep during its wait time (in this case between 1 and 5 seconds).
86
- After its wait time it'll pick a new task and keep repeating that.
87
+ finished executing, the User will then sleep for its specified wait time (in this case between 1 and 5 seconds).
88
+ Then it will pick a new task.
87
89
 
88
90
  Note that only methods decorated with ``@task`` will be picked, so you can define your own internal helper methods any way you like.
89
91
 
@@ -9,9 +9,16 @@ from locust import HttpUser, TaskSet, between, events, task, web
9
9
  import os
10
10
  from html import escape
11
11
  from time import time
12
+ from warnings import warn
12
13
 
13
14
  from flask import Blueprint, jsonify, make_response, render_template
14
15
 
16
+ warn(
17
+ "This UI example is deprecated, please downgrade to Locust 2.21.0 or use --legacy-ui flag ; version=2.22.0",
18
+ DeprecationWarning,
19
+ stacklevel=2,
20
+ )
21
+
15
22
 
16
23
  class MyTaskSet(TaskSet):
17
24
  @task(2)
@@ -0,0 +1,210 @@
1
+ """
2
+ This is an example of a locustfile that uses Locust's built in event and web
3
+ UI extension hooks to track the sum of Varnish cache hit/miss headers
4
+ and display them in the web UI.
5
+ """
6
+
7
+ from locust import HttpUser, TaskSet, between, events, task, web
8
+
9
+ import json
10
+ import os
11
+ from html import escape
12
+ from time import time
13
+
14
+ from flask import Blueprint, jsonify, make_response, render_template, request
15
+
16
+
17
+ class MyTaskSet(TaskSet):
18
+ @task(1)
19
+ def miss(l):
20
+ """MISS X-Cache header"""
21
+ l.client.get("/response-headers?X-Cache=MISS")
22
+
23
+ @task(2)
24
+ def hit(l):
25
+ """HIT X-Cache header"""
26
+ l.client.get("/response-headers?X-Cache=HIT")
27
+
28
+ @task(1)
29
+ def noinfo(l):
30
+ """No X-Cache header (noinfo counter)"""
31
+ l.client.get("/")
32
+
33
+
34
+ class WebsiteUser(HttpUser):
35
+ host = "http://httpbin.org"
36
+ wait_time = between(2, 5)
37
+ tasks = [MyTaskSet]
38
+
39
+
40
+ # This example is based on the Varnish hit/miss headers (https://docs.varnish-software.com/tutorials/hit-miss-logging/).
41
+ # It could easly be customised for matching other caching sytems, CDN or custom headers.
42
+ CACHE_HEADER = "X-Cache"
43
+
44
+ cache_stats = {}
45
+
46
+ page_stats = {"hit": 0, "miss": 0, "noinfo": 0}
47
+
48
+ path = os.path.dirname(os.path.abspath(__file__))
49
+ extend = Blueprint(
50
+ "extend",
51
+ "extend_web_ui",
52
+ static_folder=f"{path}/static/",
53
+ static_url_path="/extend/static/",
54
+ template_folder=f"{path}/templates/",
55
+ )
56
+
57
+
58
+ @events.init.add_listener
59
+ def locust_init(environment, **kwargs):
60
+ """
61
+ Load data on locust init.
62
+ :param environment:
63
+ :param kwargs:
64
+ :return:
65
+ """
66
+
67
+ if environment.web_ui:
68
+ # this code is only run on the master node (the web_ui instance doesn't exist on workers)
69
+
70
+ def get_cache_stats():
71
+ """
72
+ This is used by the Cache tab in the
73
+ extended web UI to show the stats.
74
+ """
75
+ if cache_stats:
76
+ stats_tmp = []
77
+
78
+ for name, inner_stats in cache_stats.items():
79
+ stats_tmp.append(
80
+ {
81
+ "name": name,
82
+ "safe_name": escape(name, quote=False),
83
+ "hit": inner_stats["hit"],
84
+ "miss": inner_stats["miss"],
85
+ "noinfo": inner_stats["noinfo"],
86
+ }
87
+ )
88
+
89
+ # Truncate the total number of stats and errors displayed since a large number
90
+ # of rows will cause the app to render extremely slowly.
91
+ return stats_tmp[:500]
92
+ return cache_stats
93
+
94
+ @environment.web_ui.app.after_request
95
+ def extend_stats_response(response):
96
+ if request.path != "/stats/requests":
97
+ return response
98
+
99
+ # extended_stats contains the data where extended_tables looks for its data: "cache-statistics"
100
+ response.set_data(
101
+ json.dumps(
102
+ {**response.json, "extended_stats": [{"key": "cache-statistics", "data": get_cache_stats()}]}
103
+ )
104
+ )
105
+
106
+ return response
107
+
108
+ @extend.route("/extend")
109
+ def extend_web_ui():
110
+ """
111
+ Add route to access the extended web UI with our new tab.
112
+ """
113
+ # ensure the template_args are up to date before using them
114
+ environment.web_ui.update_template_args()
115
+ # set the static paths to use the modern ui
116
+ environment.web_ui.set_static_modern_ui()
117
+
118
+ return render_template(
119
+ "index.html",
120
+ template_args={
121
+ **environment.web_ui.template_args,
122
+ # extended_tabs and extended_tables keys must match.
123
+ "extended_tabs": [{"title": "Cache statistics", "key": "cache-statistics"}],
124
+ "extended_tables": [
125
+ {
126
+ "key": "cache-statistics",
127
+ "structure": [
128
+ {"key": "name", "title": "Name"},
129
+ {"key": "hit", "title": "Hit"},
130
+ {"key": "miss", "title": "Miss"},
131
+ {"key": "noinfo", "title": "No Info"},
132
+ ],
133
+ }
134
+ ],
135
+ "extended_csv_files": [{"href": "/cache/csv", "title": "Download Cache statistics CSV"}],
136
+ },
137
+ )
138
+
139
+ @extend.route("/cache/csv")
140
+ def request_cache_csv():
141
+ """
142
+ Add route to enable downloading of cache stats as CSV
143
+ """
144
+ response = make_response(cache_csv())
145
+ file_name = f"cache-{time()}.csv"
146
+ disposition = f"attachment;filename={file_name}"
147
+ response.headers["Content-type"] = "text/csv"
148
+ response.headers["Content-disposition"] = disposition
149
+ return response
150
+
151
+ def cache_csv():
152
+ """Returns the cache stats as CSV."""
153
+ rows = [",".join(['"Name"', '"hit"', '"miss"', '"noinfo"'])]
154
+
155
+ if cache_stats:
156
+ for name, stats in cache_stats.items():
157
+ rows.append(f'"{name}",' + ",".join(str(v) for v in stats.values()))
158
+ return "\n".join(rows)
159
+
160
+ # register our new routes and extended UI with the Locust web UI
161
+ environment.web_ui.app.register_blueprint(extend)
162
+
163
+
164
+ @events.request.add_listener
165
+ def on_request(name, response, exception, **kwargs):
166
+ """
167
+ Event handler that get triggered on every request
168
+ """
169
+
170
+ cache_stats.setdefault(name, page_stats.copy())
171
+
172
+ if CACHE_HEADER not in response.headers:
173
+ cache_stats[name]["noinfo"] += 1
174
+ elif response.headers[CACHE_HEADER] == "HIT":
175
+ cache_stats[name]["hit"] += 1
176
+ elif response.headers[CACHE_HEADER] == "MISS":
177
+ cache_stats[name]["miss"] += 1
178
+
179
+
180
+ @events.report_to_master.add_listener
181
+ def on_report_to_master(client_id, data):
182
+ """
183
+ This event is triggered on the worker instances every time a stats report is
184
+ to be sent to the locust master. It will allow us to add our extra cache
185
+ data to the dict that is being sent, and then we clear the local stats in the worker.
186
+ """
187
+ global cache_stats
188
+ data["cache_stats"] = cache_stats
189
+ cache_stats = {}
190
+
191
+
192
+ @events.worker_report.add_listener
193
+ def on_worker_report(client_id, data):
194
+ """
195
+ This event is triggered on the master instance when a new stats report arrives
196
+ from a worker. Here we just add the cache to the master's aggregated stats dict.
197
+ """
198
+ for name in data["cache_stats"]:
199
+ cache_stats.setdefault(name, page_stats.copy())
200
+ for stat_name, value in data["cache_stats"][name].items():
201
+ cache_stats[name][stat_name] += value
202
+
203
+
204
+ @events.reset_stats.add_listener
205
+ def on_reset_stats():
206
+ """
207
+ Event handler that get triggered on click of web UI Reset Stats button
208
+ """
209
+ global cache_stats
210
+ cache_stats = {}
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.24.2.dev19'
16
- __version_tuple__ = version_tuple = (2, 24, 2, 'dev19')
15
+ __version__ = version = '2.25.0'
16
+ __version_tuple__ = version_tuple = (2, 25, 0)
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextlib
4
+ import functools
4
5
  import itertools
5
6
  import math
7
+ import sys
6
8
  import time
7
9
  from collections import defaultdict
8
10
  from operator import attrgetter
@@ -16,6 +18,20 @@ if TYPE_CHECKING:
16
18
  from locust.runners import WorkerNode
17
19
 
18
20
 
21
+ def compatible_math_gcd(*args: int) -> int:
22
+ """
23
+ This function is a workaround for the fact that `math.gcd` in:
24
+ - 3.5 <= Python < 3.9 doesn't accept more than two arguments.
25
+ - 3.9 <= Python can accept more than two arguments.
26
+ See more at https://docs.python.org/3.9/library/math.html#math.gcd
27
+ """
28
+ if (3, 5) <= sys.version_info < (3, 9):
29
+ return functools.reduce(math.gcd, args)
30
+ elif sys.version_info >= (3, 9):
31
+ return math.gcd(*args)
32
+ raise NotImplementedError("This function is only implemented for Python from 3.5")
33
+
34
+
19
35
  # To profile line-by-line, uncomment the code below (i.e. `import line_profiler ...`) and
20
36
  # place `@profile` on the functions/methods you wish to profile. Then, in the unit test you are
21
37
  # running, use `from locust.dispatch import profile; profile.print_stats()` at the end of the unit test.
@@ -366,18 +382,37 @@ class UsersDispatcher(Iterator):
366
382
  if not users:
367
383
  return itertools.cycle([None])
368
384
 
369
- # Normalize the weights so that the smallest weight will be equal to "target_min_weight".
370
- # The value "2" was experimentally determined because it gave a better distribution especially
371
- # when dealing with weights which are close to each others, e.g. 1.5, 2, 2.4, etc.
372
- target_min_weight = 2
373
-
374
- # 'Value' here means weight or fixed count
385
+ def _get_order_of_magnitude(n: float) -> int:
386
+ """Get how many times we need to multiply `n` to get an integer-like number.
387
+ For example:
388
+ 0.1 would return 10,
389
+ 0.04 would return 100,
390
+ 0.0007 would return 10000.
391
+ """
392
+ if n <= 0:
393
+ raise ValueError("To get the order of magnitude, the number must be greater than 0.")
394
+
395
+ counter = 0
396
+ while n < 1:
397
+ n *= 10
398
+ counter += 1
399
+ return 10**counter
400
+
401
+ # Get maximum order of magnitude to "normalize the weights".
402
+ # "Normalizing the weights" is to multiply all weights by the same number so that
403
+ # they become integers. Then we can find the largest common divisor of all the
404
+ # weights, divide them by it and get the smallest possible numbers with the same
405
+ # ratio as the numbers originally had.
406
+ max_order_of_magnitude = _get_order_of_magnitude(min(abs(u[1]) for u in users))
407
+ weights = tuple(int(u[1] * max_order_of_magnitude) for u in users)
408
+
409
+ greatest_common_divisor = compatible_math_gcd(*weights)
375
410
  normalized_values = [
376
411
  (
377
- user.__name__,
378
- round(target_min_weight * value / min(u[1] for u in users)),
412
+ user[0].__name__,
413
+ normalized_weight // greatest_common_divisor,
379
414
  )
380
- for user, value in users
415
+ for user, normalized_weight in zip(users, weights)
381
416
  ]
382
417
  generation_length_to_get_proper_distribution = sum(
383
418
  normalized_val[1] for normalized_val in normalized_values
@@ -847,6 +847,75 @@ class TestRampUpUsersFromZero(unittest.TestCase):
847
847
  delta = time.perf_counter() - ts
848
848
  self.assertTrue(0 <= delta <= _TOLERANCE, delta)
849
849
 
850
+ def test_implementation_of_dispatch_distribution_with_gcd(self):
851
+ class User1(User):
852
+ weight = 4
853
+
854
+ class User2(User):
855
+ weight = 5
856
+
857
+ user_classes = [User1, User2]
858
+ worker_node1 = WorkerNode("1")
859
+
860
+ sleep_time = 0.2 # Speed-up test
861
+
862
+ users_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=user_classes)
863
+ users_dispatcher.new_dispatch(target_user_count=9, spawn_rate=9)
864
+
865
+ users_dispatcher._wait_between_dispatch = sleep_time
866
+
867
+ ts = time.perf_counter()
868
+ self.assertDictEqual(
869
+ next(users_dispatcher),
870
+ {
871
+ "1": {"User1": 4, "User2": 5},
872
+ },
873
+ )
874
+ delta = time.perf_counter() - ts
875
+ self.assertTrue(0 <= delta <= _TOLERANCE, delta)
876
+
877
+ ts = time.perf_counter()
878
+ self.assertRaises(StopIteration, lambda: next(users_dispatcher))
879
+ delta = time.perf_counter() - ts
880
+ self.assertTrue(0 <= delta <= _TOLERANCE, delta)
881
+
882
+ def test_implementation_of_dispatch_distribution_with_gcd_float_weight(self):
883
+ class User1(User):
884
+ weight = 0.8
885
+
886
+ class User2(User):
887
+ weight = 1
888
+
889
+ normalized_weights_to_min_int = 5 # User1: 0.8 * 5 = 4; User2: 1 * 5 = 5
890
+
891
+ user_classes = [User1, User2]
892
+ worker_node1 = WorkerNode("1")
893
+
894
+ sleep_time = 0.2 # Speed-up test
895
+
896
+ users_dispatcher = UsersDispatcher(worker_nodes=[worker_node1], user_classes=user_classes)
897
+ users_dispatcher.new_dispatch(target_user_count=18, spawn_rate=18)
898
+
899
+ users_dispatcher._wait_between_dispatch = sleep_time
900
+
901
+ ts = time.perf_counter()
902
+ self.assertDictEqual(
903
+ next(users_dispatcher),
904
+ {
905
+ "1": {
906
+ "User1": int(normalized_weights_to_min_int * User1.weight * 2),
907
+ "User2": int(normalized_weights_to_min_int * User2.weight * 2),
908
+ },
909
+ },
910
+ )
911
+ delta = time.perf_counter() - ts
912
+ self.assertTrue(0 <= delta <= _TOLERANCE, delta)
913
+
914
+ ts = time.perf_counter()
915
+ self.assertRaises(StopIteration, lambda: next(users_dispatcher))
916
+ delta = time.perf_counter() - ts
917
+ self.assertTrue(0 <= delta <= _TOLERANCE, delta)
918
+
850
919
 
851
920
  class TestWaitBetweenDispatch(unittest.TestCase):
852
921
  def test_wait_between_dispatch(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: locust
3
- Version: 2.24.2.dev19
3
+ Version: 2.25.0
4
4
  Summary: Developer friendly load testing framework
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/locustio/locust
@@ -55,6 +55,7 @@ docs/use-as-lib.rst
55
55
  docs/what-is-locust.rst
56
56
  docs/writing-a-locustfile.rst
57
57
  docs/_static/theme-overrides.css
58
+ docs/images/extend_modern_web_ui_cache_stats.png
58
59
  docs/images/locust_workers.png
59
60
  docs/images/number_of_users.png
60
61
  docs/images/response_times.png
@@ -84,6 +85,7 @@ examples/stop_on_threshold.py
84
85
  examples/test_data_management.py
85
86
  examples/use_as_lib.py
86
87
  examples/web_ui_auth.py
88
+ examples/web_ui_cache_stats.py
87
89
  examples/worker_index.py
88
90
  examples/custom_shape/double_wave.py
89
91
  examples/custom_shape/stages.py
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