limits 4.7.2__tar.gz → 4.8.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 (103) hide show
  1. {limits-4.7.2 → limits-4.8.0}/HISTORY.rst +24 -0
  2. {limits-4.7.2 → limits-4.8.0}/PKG-INFO +1 -1
  3. {limits-4.7.2 → limits-4.8.0}/doc/source/conf.py +10 -1
  4. limits-4.8.0/doc/source/ext/_static/benchmark-chart.css +94 -0
  5. limits-4.8.0/doc/source/ext/_static/js/benchmark-chart.js +453 -0
  6. limits-4.8.0/doc/source/ext/_static/js/benchmark-details.js +117 -0
  7. limits-4.8.0/doc/source/ext/_static/js/benchmark-loader.js +43 -0
  8. limits-4.8.0/doc/source/ext/_templates/git_info.js +16 -0
  9. {limits-4.7.2 → limits-4.8.0}/doc/source/ext/bench_chart.py +12 -9
  10. limits-4.8.0/doc/source/performance.rst +126 -0
  11. {limits-4.7.2 → limits-4.8.0}/limits/_version.py +3 -3
  12. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/memcached.py +4 -1
  13. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/redis/__init__.py +22 -6
  14. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/redis/bridge.py +3 -2
  15. {limits-4.7.2 → limits-4.8.0}/limits/storage/redis.py +5 -2
  16. {limits-4.7.2 → limits-4.8.0}/limits/storage/redis_cluster.py +5 -2
  17. {limits-4.7.2 → limits-4.8.0}/limits/storage/redis_sentinel.py +3 -0
  18. {limits-4.7.2 → limits-4.8.0}/limits.egg-info/PKG-INFO +1 -1
  19. {limits-4.7.2 → limits-4.8.0}/requirements/docs.txt +3 -3
  20. limits-4.7.2/doc/source/ext/_static/benchmark-chart.css +0 -61
  21. limits-4.7.2/doc/source/ext/_static/js/benchmark-chart.js +0 -219
  22. limits-4.7.2/doc/source/ext/_static/js/benchmark-details.js +0 -103
  23. limits-4.7.2/doc/source/ext/_static/js/benchmark-loader.js +0 -31
  24. limits-4.7.2/doc/source/ext/_templates/git_info.js +0 -2
  25. limits-4.7.2/doc/source/performance.rst +0 -221
  26. {limits-4.7.2 → limits-4.8.0}/CLASSIFIERS +0 -0
  27. {limits-4.7.2 → limits-4.8.0}/CONTRIBUTIONS.rst +0 -0
  28. {limits-4.7.2 → limits-4.8.0}/LICENSE.txt +0 -0
  29. {limits-4.7.2 → limits-4.8.0}/MANIFEST.in +0 -0
  30. {limits-4.7.2 → limits-4.8.0}/README.rst +0 -0
  31. {limits-4.7.2 → limits-4.8.0}/doc/Makefile +0 -0
  32. {limits-4.7.2 → limits-4.8.0}/doc/source/_static/custom.css +0 -0
  33. {limits-4.7.2 → limits-4.8.0}/doc/source/api.rst +0 -0
  34. {limits-4.7.2 → limits-4.8.0}/doc/source/async.rst +0 -0
  35. {limits-4.7.2 → limits-4.8.0}/doc/source/changelog.rst +0 -0
  36. {limits-4.7.2 → limits-4.8.0}/doc/source/custom-storage.rst +0 -0
  37. {limits-4.7.2 → limits-4.8.0}/doc/source/index.rst +0 -0
  38. {limits-4.7.2 → limits-4.8.0}/doc/source/installation.rst +0 -0
  39. {limits-4.7.2 → limits-4.8.0}/doc/source/quickstart.rst +0 -0
  40. {limits-4.7.2 → limits-4.8.0}/doc/source/storage.rst +0 -0
  41. {limits-4.7.2 → limits-4.8.0}/doc/source/strategies.rst +0 -0
  42. {limits-4.7.2 → limits-4.8.0}/doc/source/theme_config.py +0 -0
  43. {limits-4.7.2 → limits-4.8.0}/limits/__init__.py +0 -0
  44. {limits-4.7.2 → limits-4.8.0}/limits/aio/__init__.py +0 -0
  45. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/__init__.py +0 -0
  46. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/base.py +0 -0
  47. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/etcd.py +0 -0
  48. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/memory.py +0 -0
  49. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/mongodb.py +0 -0
  50. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/redis/coredis.py +0 -0
  51. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/redis/redispy.py +0 -0
  52. {limits-4.7.2 → limits-4.8.0}/limits/aio/storage/redis/valkey.py +0 -0
  53. {limits-4.7.2 → limits-4.8.0}/limits/aio/strategies.py +0 -0
  54. {limits-4.7.2 → limits-4.8.0}/limits/errors.py +0 -0
  55. {limits-4.7.2 → limits-4.8.0}/limits/limits.py +0 -0
  56. {limits-4.7.2 → limits-4.8.0}/limits/py.typed +0 -0
  57. {limits-4.7.2 → limits-4.8.0}/limits/resources/redis/lua_scripts/acquire_moving_window.lua +0 -0
  58. {limits-4.7.2 → limits-4.8.0}/limits/resources/redis/lua_scripts/acquire_sliding_window.lua +0 -0
  59. {limits-4.7.2 → limits-4.8.0}/limits/resources/redis/lua_scripts/clear_keys.lua +0 -0
  60. {limits-4.7.2 → limits-4.8.0}/limits/resources/redis/lua_scripts/incr_expire.lua +0 -0
  61. {limits-4.7.2 → limits-4.8.0}/limits/resources/redis/lua_scripts/moving_window.lua +0 -0
  62. {limits-4.7.2 → limits-4.8.0}/limits/resources/redis/lua_scripts/sliding_window.lua +0 -0
  63. {limits-4.7.2 → limits-4.8.0}/limits/storage/__init__.py +0 -0
  64. {limits-4.7.2 → limits-4.8.0}/limits/storage/base.py +0 -0
  65. {limits-4.7.2 → limits-4.8.0}/limits/storage/etcd.py +0 -0
  66. {limits-4.7.2 → limits-4.8.0}/limits/storage/memcached.py +0 -0
  67. {limits-4.7.2 → limits-4.8.0}/limits/storage/memory.py +0 -0
  68. {limits-4.7.2 → limits-4.8.0}/limits/storage/mongodb.py +0 -0
  69. {limits-4.7.2 → limits-4.8.0}/limits/storage/registry.py +0 -0
  70. {limits-4.7.2 → limits-4.8.0}/limits/strategies.py +0 -0
  71. {limits-4.7.2 → limits-4.8.0}/limits/typing.py +0 -0
  72. {limits-4.7.2 → limits-4.8.0}/limits/util.py +0 -0
  73. {limits-4.7.2 → limits-4.8.0}/limits/version.py +0 -0
  74. {limits-4.7.2 → limits-4.8.0}/limits.egg-info/SOURCES.txt +0 -0
  75. {limits-4.7.2 → limits-4.8.0}/limits.egg-info/dependency_links.txt +0 -0
  76. {limits-4.7.2 → limits-4.8.0}/limits.egg-info/not-zip-safe +0 -0
  77. {limits-4.7.2 → limits-4.8.0}/limits.egg-info/requires.txt +0 -0
  78. {limits-4.7.2 → limits-4.8.0}/limits.egg-info/top_level.txt +0 -0
  79. {limits-4.7.2 → limits-4.8.0}/pyproject.toml +0 -0
  80. {limits-4.7.2 → limits-4.8.0}/requirements/ci.txt +0 -0
  81. {limits-4.7.2 → limits-4.8.0}/requirements/dev.txt +0 -0
  82. {limits-4.7.2 → limits-4.8.0}/requirements/main.txt +0 -0
  83. {limits-4.7.2 → limits-4.8.0}/requirements/storage/async-etcd.txt +0 -0
  84. {limits-4.7.2 → limits-4.8.0}/requirements/storage/async-memcached.txt +0 -0
  85. {limits-4.7.2 → limits-4.8.0}/requirements/storage/async-mongodb.txt +0 -0
  86. {limits-4.7.2 → limits-4.8.0}/requirements/storage/async-redis.txt +0 -0
  87. {limits-4.7.2 → limits-4.8.0}/requirements/storage/async-valkey.txt +0 -0
  88. {limits-4.7.2 → limits-4.8.0}/requirements/storage/etcd.txt +0 -0
  89. {limits-4.7.2 → limits-4.8.0}/requirements/storage/memcached.txt +0 -0
  90. {limits-4.7.2 → limits-4.8.0}/requirements/storage/mongodb.txt +0 -0
  91. {limits-4.7.2 → limits-4.8.0}/requirements/storage/redis.txt +0 -0
  92. {limits-4.7.2 → limits-4.8.0}/requirements/storage/rediscluster.txt +0 -0
  93. {limits-4.7.2 → limits-4.8.0}/requirements/storage/valkey.txt +0 -0
  94. {limits-4.7.2 → limits-4.8.0}/requirements/test.txt +0 -0
  95. {limits-4.7.2 → limits-4.8.0}/setup.cfg +0 -0
  96. {limits-4.7.2 → limits-4.8.0}/setup.py +0 -0
  97. {limits-4.7.2 → limits-4.8.0}/tests/test_limit_granularities.py +0 -0
  98. {limits-4.7.2 → limits-4.8.0}/tests/test_limits.py +0 -0
  99. {limits-4.7.2 → limits-4.8.0}/tests/test_ratelimit_parser.py +0 -0
  100. {limits-4.7.2 → limits-4.8.0}/tests/test_storage.py +0 -0
  101. {limits-4.7.2 → limits-4.8.0}/tests/test_strategy.py +0 -0
  102. {limits-4.7.2 → limits-4.8.0}/tests/test_utils.py +0 -0
  103. {limits-4.7.2 → limits-4.8.0}/versioneer.py +0 -0
@@ -3,6 +3,28 @@
3
3
  Changelog
4
4
  =========
5
5
 
6
+ v4.8.0
7
+ ------
8
+ Release Date: 2025-04-23
9
+
10
+ * Features
11
+
12
+ * Expose key_prefix constructor argument for all redis storage
13
+ implementations to simplify customizing the prefix used for all keys
14
+ created in redis (Backported from ``5.1.0``).
15
+
16
+ v4.7.3
17
+ ------
18
+ Release Date: 2025-04-12
19
+
20
+ * Documentation
21
+
22
+ * Expand benchmark results to included preseeded limits
23
+
24
+ * Bug Fix
25
+
26
+ * Handle clearing missing key with memcache + async
27
+
6
28
  v4.7.2
7
29
  ------
8
30
  Release Date: 2025-04-09
@@ -872,6 +894,8 @@ Release Date: 2015-01-08
872
894
 
873
895
 
874
896
 
897
+
898
+
875
899
 
876
900
 
877
901
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: limits
3
- Version: 4.7.2
3
+ Version: 4.8.0
4
4
  Summary: Rate limiting utilities
5
5
  Home-page: https://limits.readthedocs.org
6
6
  Author: Ali-Akber Saifee
@@ -39,7 +39,15 @@ else:
39
39
  "branch": git_info.get("branch", ""),
40
40
  "sha": git_info.get("long", None)
41
41
  }
42
-
42
+ benchmark_param_mapping = {
43
+ "percentage_full": {
44
+ "display": "Percentage Seeded",
45
+ "info": "Percentage of rate limit already filled before the benchmark is run",
46
+ },
47
+ "async": {"display": "Asyncio", "info": "Using asyncio storage implementation"},
48
+ "storage_type": {"display": "Storage", "info": "Storage Backend"},
49
+ "strategy": {"display": "Strategy", "info": "Rate Limiting Strategy"},
50
+ }
43
51
  html_static_path = ["_static"]
44
52
 
45
53
  html_css_files = [
@@ -73,6 +81,7 @@ extensions = [
73
81
  "sphinxext.opengraph",
74
82
  "sphinxcontrib.programoutput",
75
83
  "sphinx_copybutton",
84
+ "sphinx_design",
76
85
  "sphinx_inline_tabs",
77
86
  "sphinx_paramlinks",
78
87
  "bench_chart",
@@ -0,0 +1,94 @@
1
+ .benchmark-chart {
2
+ width: 100%;
3
+ }
4
+ .benchmark-details {
5
+ .benchmark-details-section {
6
+ thead > tr {
7
+ color: var(--color-purple);
8
+ }
9
+ }
10
+ }
11
+ .benchmark-chart-error,
12
+ .benchmark-details-error {
13
+ color: var(--color-red);
14
+ font-size: smaller;
15
+ padding: 2em;
16
+ text-align: center;
17
+ height: 5em;
18
+ }
19
+ .benchmark-chart-loading {
20
+ display: flex;
21
+ justify-content: center;
22
+ align-items: center;
23
+ font-size: 1.2rem;
24
+ font-weight: bold;
25
+ color: var(--color-purple);
26
+ letter-spacing: 1px;
27
+ height: 200px;
28
+ animation: benchmark-chart-loading-animation;
29
+ animation-iteration-count: 10;
30
+ animation-duration: 2s;
31
+ }
32
+
33
+ @keyframes benchmark-chart-loading-animation {
34
+ 0% {
35
+ opacity: 0;
36
+ }
37
+ 25% {
38
+ opacity: 0.25;
39
+ }
40
+ 50% {
41
+ opacity: 0.5;
42
+ }
43
+ 90% {
44
+ opacity: 0.9;
45
+ }
46
+ 100% {
47
+ opacity: 1;
48
+ }
49
+ }
50
+ .benchmark-filters {
51
+ padding-top: 1em;
52
+ padding-bottom: 1em;
53
+ display: flex;
54
+ justify-content: flex-end;
55
+ }
56
+ .benchmark-filter-dropdowns {
57
+ display: flex;
58
+ align-items: flex-end;
59
+ justify-content: flex-end;
60
+ margin-top: 2em;
61
+ flex-direction: row;
62
+ @media only screen and (max-width: 800px) {
63
+ flex-basis: min-content;
64
+ }
65
+ @media only screen and (max-width: 400px) {
66
+ flex-basis: max-content;
67
+ flex-direction: column;
68
+ }
69
+ }
70
+
71
+ .benchmark-filter {
72
+ display: flex;
73
+ flex-direction: row;
74
+ gap: 0.25em;
75
+ padding: 0.25em;
76
+ }
77
+
78
+ .benchmark-filter label {
79
+ font-size: small;
80
+ font-weight: bold;
81
+ }
82
+
83
+ .benchmark-filter select {
84
+ font-size: small;
85
+ border: 1px solid;
86
+ border-radius: 0.4em;
87
+ }
88
+ .benchmark-filter input[type="checkbox"] {
89
+ }
90
+
91
+ .compare-dropdowns {
92
+ display: flex;
93
+ justify-content: flex-end;
94
+ }
@@ -0,0 +1,453 @@
1
+ import { render, html } from "https://unpkg.com/uhtml@3.2.1?module";
2
+ import { BenchmarkLoader } from "./benchmark-loader.js";
3
+ const KNOWN_PARAMS = [
4
+ "storage_type",
5
+ "limit",
6
+ "strategy",
7
+ "async",
8
+ "percentage_full",
9
+ ];
10
+ let dispatched = new Set();
11
+ window.Benchmarks = {};
12
+ let currentLoader = new BenchmarkLoader();
13
+ let compareLoaders = {};
14
+
15
+ class BenchmarkChartUtils {
16
+ static getBenchmarkData(result, query) {
17
+ let benchmarks = result.benchmarks;
18
+ return benchmarks.filter(function (benchmark) {
19
+ let okay = true;
20
+ if (query) {
21
+ Object.entries(query).forEach((entry) => {
22
+ let key = entry[0];
23
+ let value = entry[1];
24
+ if (
25
+ key != "group" &&
26
+ !(value === "") && // i.e. any.
27
+ benchmark.params[key] != null &&
28
+ benchmark.params[key] != value
29
+ ) {
30
+ okay = false;
31
+ } else if (key == "group" && value != benchmark.group) {
32
+ okay = false;
33
+ }
34
+ });
35
+ }
36
+ return okay;
37
+ });
38
+ }
39
+
40
+ static formatParam(key, str) {
41
+ if (key === "limit") {
42
+ var m = str.match(/(\d+(?:\.\d+)?)\s+per\s+1\s+(\w+)/i);
43
+ if (!m) return str;
44
+ var n = parseFloat(m[1]),
45
+ u = m[2].toLowerCase(),
46
+ num =
47
+ n >= 1000
48
+ ? (n / 1000) % 1 === 0
49
+ ? n / 1000 + "K"
50
+ : (n / 1000).toFixed(1) + "K"
51
+ : n.toString(),
52
+ umap = {
53
+ second: "s",
54
+ seconds: "s",
55
+ minute: "min",
56
+ minutes: "mins",
57
+ hour: "hr",
58
+ hours: "hr",
59
+ day: "day",
60
+ days: "day",
61
+ };
62
+ return num + "/" + (umap[u] || u);
63
+ } else if (key === "percentage_full") {
64
+ return `${str}% Seeded`;
65
+ }
66
+ return str;
67
+ }
68
+
69
+ static nameTransform(benchmark, stripParams, query) {
70
+ let name = benchmark.name;
71
+ let params = benchmark.params;
72
+ name = name
73
+ .replace(/\[.*?\]/, "")
74
+ .replace("_async", "")
75
+ .replaceAll("_", "-");
76
+ name = name.replace(benchmark.group, "");
77
+ let queryParam = Object.entries(query).map((entry) => entry[0]);
78
+ let additional = BenchmarkChartUtils.getRemainingGroups(benchmark, query);
79
+ Object.entries(additional).forEach((param) => {
80
+ let value = BenchmarkChartUtils.formatParam(param[0], param[1]);
81
+ if (name) {
82
+ name += ` - ${value}`;
83
+ } else {
84
+ name = `${value}`;
85
+ }
86
+ });
87
+ return name;
88
+ }
89
+
90
+ static getRemainingGroups(benchmark, query) {
91
+ let queryParam = Object.entries(query).map((entry) => entry[0]);
92
+ let additional = {};
93
+ Object.entries(benchmark.params).forEach((param) => {
94
+ const key = param[0];
95
+ const value = param[1];
96
+ if (
97
+ (!queryParam.includes(key) || query?.[key] === "") &&
98
+ KNOWN_PARAMS.includes(key)
99
+ ) {
100
+ additional[key] = value;
101
+ }
102
+ });
103
+ return additional;
104
+ }
105
+
106
+ static getColorForStorage(storageType) {
107
+ const storageColorMap = {
108
+ memory: window
109
+ .getComputedStyle(document.body)
110
+ .getPropertyValue("--color-purple"),
111
+ mongodb: window
112
+ .getComputedStyle(document.body)
113
+ .getPropertyValue("--color-yellow"),
114
+ memcached: window
115
+ .getComputedStyle(document.body)
116
+ .getPropertyValue("--color-blue"),
117
+ redis: window
118
+ .getComputedStyle(document.body)
119
+ .getPropertyValue("--color-red"),
120
+ };
121
+
122
+ // Fallback color if an unknown storageType appears
123
+ return storageColorMap[storageType] || "#7f7f7f"; // gray
124
+ }
125
+
126
+ static sortBenchmarksByParams(benchmarks, sortKeys) {
127
+ return benchmarks.sort(function (a, b) {
128
+ for (const key of sortKeys) {
129
+ let valA = (a.params?.[key] || "").toLowerCase();
130
+ let valB = (b.params?.[key] || "").toLowerCase();
131
+ if (key === "limit") {
132
+ valA = parseInt(valA.split(" ")[0], 10);
133
+ valB = parseInt(valB.split(" ")[0], 10);
134
+ }
135
+ if (valA < valB) return -1;
136
+ if (valA > valB) return 1;
137
+ }
138
+ return a.name.localeCompare(b.name);
139
+ });
140
+ }
141
+
142
+ static fetchComparisonData(source, comparisonSource) {
143
+ if (!compareLoaders[comparisonSource]) {
144
+ compareLoaders[comparisonSource] = new BenchmarkLoader([
145
+ `https://${comparisonSource.replaceAll(".", "-")}--py-limits.netlify.app/`,
146
+ ]);
147
+ }
148
+ return compareLoaders[comparisonSource].fetchBenchmarkData(source);
149
+ }
150
+ }
151
+
152
+ class BenchmarkChart {
153
+ constructor(node) {
154
+ this.chart = node;
155
+ this.source = this.chart.dataset.source;
156
+ this.filters = JSON.parse(this.chart.dataset.filters);
157
+ this.query = JSON.parse(this.chart.dataset.query);
158
+ this.paramMapping = JSON.parse(this.chart.dataset.paramMapping);
159
+ this.chartId = this.chart.dataset.chartId;
160
+ this.currentCompare = null;
161
+ this.sortBy = JSON.parse(
162
+ this.chart.dataset.sortBy || '["storage_type", "limit"]',
163
+ );
164
+ let self = this;
165
+ render(
166
+ this.chart,
167
+ html`
168
+ <div class="benchmark-chart-loading">
169
+ <span>Loading</span>
170
+ </div>
171
+ `,
172
+ );
173
+ if (!dispatched.has(this.source)) {
174
+ currentLoader
175
+ .fetchBenchmarkData(this.source)
176
+ .then((result) => {
177
+ let event = new Event(`${self.source}-loaded`);
178
+ window.dispatchEvent(event);
179
+ })
180
+ .catch((error) => {
181
+ let event = new Event(`${self.source}-failed`);
182
+ window.dispatchEvent(event);
183
+ });
184
+ }
185
+ dispatched.add(this.source);
186
+ window.addEventListener(`${this.source}-failed`, function () {
187
+ self.handleError();
188
+ });
189
+ window.addEventListener(`${this.source}-loaded`, function () {
190
+ self.render();
191
+ });
192
+ }
193
+ handleError() {
194
+ this.chart.querySelector(".benchmark-chart-loading")?.remove();
195
+ render(
196
+ this.chart,
197
+ html`
198
+ <div class="benchmark-chart-error">Benchmark data not available.</div>
199
+ `,
200
+ );
201
+ }
202
+ renderCompare() {
203
+ const compareTargets = ["master", "stable", "4.x"]
204
+ .concat(window.LATEST_RELEASES || [])
205
+ .filter((element) => element != window.GITBRANCH);
206
+ let self = this;
207
+ const compareDropdown = html`
208
+ <div class="compare" title="Compare against another release or branch">
209
+ <label for="compare-select">
210
+ Compare with
211
+ <select
212
+ onchange=${(e) => {
213
+ const value = e.target.value;
214
+ self.currentCompare = value;
215
+ if (value !== "") {
216
+ BenchmarkChartUtils.fetchComparisonData(
217
+ self.source,
218
+ value,
219
+ ).then(function (comparisonData) {
220
+ self.currentComparisonData = comparisonData;
221
+ self.renderChartWithFilters();
222
+ });
223
+ } else {
224
+ self.currentComparisonData = null;
225
+ self.renderChartWithFilters();
226
+ }
227
+ }}
228
+ >
229
+ <option value=""></option>
230
+ ${compareTargets.map(
231
+ (val) => html`
232
+ <option
233
+ value=${val}
234
+ ?selected=${this.currentCompare == val.toString()}
235
+ >
236
+ ${val}
237
+ </option>
238
+ `,
239
+ )}
240
+ </select>
241
+ </label>
242
+ </div>
243
+ `;
244
+ render(
245
+ this.compareTarget,
246
+ html`<div class="compare-dropdowns">${compareDropdown}</div>`,
247
+ );
248
+ }
249
+ renderDropdowns() {
250
+ const dropdowns = Object.entries(this.filters).map(([key]) => {
251
+ const fullName = `${this.chartId}-${key}`;
252
+ const uniqueValues = [
253
+ ...new Set(this.allBenchmarks.map((b) => b.params?.[key])),
254
+ ].sort();
255
+ const isBoolean =
256
+ uniqueValues.length === 2 &&
257
+ uniqueValues.includes(true) &&
258
+ uniqueValues.includes(false);
259
+ let self = this;
260
+ if (isBoolean) {
261
+ return html`
262
+ <div class="benchmark-filter" title=${this.paramMapping[key]?.info}>
263
+ <input
264
+ type="checkbox"
265
+ id=${fullName}
266
+ ?checked=${self.currentFilters[key] === true}
267
+ onchange=${(e) => {
268
+ self.currentFilters[key] = e.target.checked;
269
+ self.renderChartWithFilters();
270
+ }}
271
+ />
272
+ <label for=${fullName}>
273
+ ${self.paramMapping[key]?.display || key}
274
+ </label>
275
+ </div>
276
+ `;
277
+ } else {
278
+ return html`
279
+ <div class="benchmark-filter" title=${self.paramMapping[key]?.info}>
280
+ <label for=${fullName}>
281
+ ${self.paramMapping[key]?.display || key}
282
+ <select
283
+ id=${fullName}
284
+ onchange=${(e) => {
285
+ const value = e.target.value;
286
+ if (value) {
287
+ self.currentFilters[key] =
288
+ value === "false"
289
+ ? false
290
+ : value === "true"
291
+ ? true
292
+ : value;
293
+ } else {
294
+ self.currentFilters[key] = "";
295
+ }
296
+ self.renderChartWithFilters();
297
+ }}
298
+ >
299
+ <option value="" ?selected=${self.currentFilters[key] == ""}>
300
+ All
301
+ </option>
302
+ ${uniqueValues.map(
303
+ (val) => html`
304
+ <option
305
+ value=${val}
306
+ ?selected=${self.currentFilters[key] == val.toString()}
307
+ >
308
+ ${val}
309
+ </option>
310
+ `,
311
+ )}
312
+ </select>
313
+ </label>
314
+ </div>
315
+ `;
316
+ }
317
+ });
318
+ render(
319
+ this.dropdownTarget,
320
+ html`<div class="benchmark-filter-dropdowns">${dropdowns}</div>`,
321
+ );
322
+ }
323
+ renderChartWithFilters() {
324
+ const queryFilter = { ...this.query, ...this.currentFilters };
325
+ let data = BenchmarkChartUtils.sortBenchmarksByParams(
326
+ BenchmarkChartUtils.getBenchmarkData(this.results, queryFilter),
327
+ this.sortBy,
328
+ );
329
+ let comparisonData = [];
330
+ let comparing = false;
331
+ if (this.currentComparisonData?.benchmarks) {
332
+ comparisonData = BenchmarkChartUtils.sortBenchmarksByParams(
333
+ BenchmarkChartUtils.getBenchmarkData(
334
+ this.currentComparisonData,
335
+ queryFilter,
336
+ ),
337
+ this.sortBy,
338
+ );
339
+ comparisonData.forEach((benchmark) => {
340
+ benchmark.forComparison = true;
341
+ });
342
+ comparing = true;
343
+ data = data.concat(comparisonData);
344
+ }
345
+ let legendGroupKey =
346
+ queryFilter?.storage_type == ""
347
+ ? "storage_type"
348
+ : Object.entries(queryFilter).find((entry) => entry[1] === "")?.[0];
349
+ function legendKeyFunc(benchmark, key) {
350
+ return key === "group" ? benchmark.group : benchmark.params[key];
351
+ }
352
+
353
+ Plotly.newPlot(
354
+ this.chartTarget,
355
+ data.map((benchmark) => ({
356
+ type: "box",
357
+ name: BenchmarkChartUtils.nameTransform(benchmark, true, queryFilter),
358
+ opacity: benchmark.forComparison ? 0.75 : comparing ? 0.5 : 1,
359
+ y: benchmark.stats.data || [
360
+ benchmark.stats.min * 1e3,
361
+ benchmark.stats.q1 * 1e3,
362
+ benchmark.stats.median * 1e3,
363
+ benchmark.stats.q3 * 1e3,
364
+ benchmark.stats.max * 1e3,
365
+ ],
366
+ boxmean: true,
367
+ boxpoints: false,
368
+ line: { width: 1 },
369
+ marker: {
370
+ color: benchmark.forComparison
371
+ ? "#39FF14"
372
+ : BenchmarkChartUtils.getColorForStorage(
373
+ benchmark.params.storage_type,
374
+ ),
375
+ },
376
+ showlegend: !benchmark.forComparison,
377
+ legendgroup: legendKeyFunc(benchmark, legendGroupKey),
378
+ legendgrouptitle: {
379
+ text: BenchmarkChartUtils.formatParam(
380
+ legendGroupKey,
381
+ legendKeyFunc(benchmark, legendGroupKey),
382
+ ),
383
+ },
384
+ })),
385
+ {
386
+ yaxis: {
387
+ title: { text: "Time (ms)" },
388
+ exponentformat: "none",
389
+ ticksuffix: " ms",
390
+ tickformat: ",.2f",
391
+ },
392
+ title: comparing
393
+ ? {
394
+ text: `Comparing against: ${this.currentCompare}`,
395
+ font: { color: "39FF14" },
396
+ }
397
+ : {},
398
+ },
399
+ {
400
+ responsive: true,
401
+ displaylogo: false,
402
+ },
403
+ );
404
+ }
405
+ render() {
406
+ this.chart.innerHTML = "";
407
+ this.chart.querySelector(".benchmark-chart-loading")?.remove();
408
+ this.results = currentLoader.sources[this.source];
409
+ this.allBenchmarks = BenchmarkChartUtils.getBenchmarkData(
410
+ this.results,
411
+ this.query,
412
+ );
413
+ this.currentFilters = Object.fromEntries(
414
+ Object.entries(this.filters).map(([key, value]) => {
415
+ return typeof value.default === "boolean"
416
+ ? [key, value.default]
417
+ : [key, value.default != null ? value.default.toString() : ""];
418
+ }),
419
+ );
420
+ this.currentCompare = "";
421
+ this.currentComparisonData = null;
422
+ this.dropdownTarget = document.createElement("div");
423
+ this.compareTarget = document.createElement("div");
424
+ this.dropdownTarget.classList.add("benchmark-filters");
425
+ this.chartTarget = document.createElement("div");
426
+ this.chart.append(this.chartTarget);
427
+ this.chart.append(this.dropdownTarget);
428
+ this.chart.append(this.compareTarget);
429
+ this.renderDropdowns();
430
+ this.renderCompare();
431
+ this.renderChartWithFilters();
432
+ let initial = true;
433
+ this.chartTarget.on("plotly_afterplot", function () {
434
+ const { hash } = window.location;
435
+ if (hash && initial) {
436
+ initial = false;
437
+ const target = document.querySelector(hash);
438
+ if (target) {
439
+ setTimeout(function () {
440
+ target.scrollIntoView({ behavior: "instant" });
441
+ }, 10);
442
+ }
443
+ }
444
+ });
445
+ }
446
+ }
447
+
448
+ document.addEventListener("DOMContentLoaded", function () {
449
+ const charts = document.querySelectorAll(".benchmark-chart");
450
+ charts.forEach((chart) => {
451
+ const chartObject = new BenchmarkChart(chart);
452
+ });
453
+ });