plesty-bench 0.3.1__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 (114) hide show
  1. plesty_bench-0.3.1/.gitignore +86 -0
  2. plesty_bench-0.3.1/.gitlab-ci.yml +238 -0
  3. plesty_bench-0.3.1/CHANGELOG.md +805 -0
  4. plesty_bench-0.3.1/LICENSE +184 -0
  5. plesty_bench-0.3.1/LICENSES/LGPL-3.0-or-later.txt +165 -0
  6. plesty_bench-0.3.1/PKG-INFO +75 -0
  7. plesty_bench-0.3.1/README.md +51 -0
  8. plesty_bench-0.3.1/REUSE.toml +11 -0
  9. plesty_bench-0.3.1/docs/api_reference.md +19 -0
  10. plesty_bench-0.3.1/docs/bench_setup.md +154 -0
  11. plesty_bench-0.3.1/docs/cli.md +136 -0
  12. plesty_bench-0.3.1/docs/console.md +180 -0
  13. plesty_bench-0.3.1/docs/design/agent.md +107 -0
  14. plesty_bench-0.3.1/docs/design/architecture.md +154 -0
  15. plesty_bench-0.3.1/docs/design/console.md +136 -0
  16. plesty_bench-0.3.1/docs/design/cross_platform.md +77 -0
  17. plesty_bench-0.3.1/docs/design/index.md +43 -0
  18. plesty_bench-0.3.1/docs/design/installer.md +141 -0
  19. plesty_bench-0.3.1/docs/design/packaging.md +168 -0
  20. plesty_bench-0.3.1/docs/design/ports.md +85 -0
  21. plesty_bench-0.3.1/docs/design/processes.md +154 -0
  22. plesty_bench-0.3.1/docs/field_test.md +85 -0
  23. plesty_bench-0.3.1/docs/index.md +137 -0
  24. plesty_bench-0.3.1/docs/maintenance.md +106 -0
  25. plesty_bench-0.3.1/docs/modules.md +118 -0
  26. plesty_bench-0.3.1/docs/remote.md +148 -0
  27. plesty_bench-0.3.1/docs/running.md +130 -0
  28. plesty_bench-0.3.1/docs/toc.yaml +29 -0
  29. plesty_bench-0.3.1/gl-secret-detection-report.json +50 -0
  30. plesty_bench-0.3.1/packaging/install.sh +191 -0
  31. plesty_bench-0.3.1/packaging/plesty-mark.png +0 -0
  32. plesty_bench-0.3.1/packaging/server_dist.py +237 -0
  33. plesty_bench-0.3.1/packaging/windows/build-installer.ps1 +209 -0
  34. plesty_bench-0.3.1/packaging/windows/install.ps1 +254 -0
  35. plesty_bench-0.3.1/packaging/windows/plesty-server.iss +453 -0
  36. plesty_bench-0.3.1/packaging/windows/probes/README.md +37 -0
  37. plesty_bench-0.3.1/packaging/windows/probes/probe.iss +27 -0
  38. plesty_bench-0.3.1/packaging/windows/probes/probe.ps1 +60 -0
  39. plesty_bench-0.3.1/packaging/windows/probes/probe2.iss +27 -0
  40. plesty_bench-0.3.1/packaging/windows/probes/probe2.ps1 +55 -0
  41. plesty_bench-0.3.1/packaging/windows/uninstall.ps1 +63 -0
  42. plesty_bench-0.3.1/plesty/bench/__init__.py +45 -0
  43. plesty_bench-0.3.1/plesty/bench/cli.py +152 -0
  44. plesty_bench-0.3.1/plesty/bench/client.py +323 -0
  45. plesty_bench-0.3.1/plesty/server/__init__.py +22 -0
  46. plesty_bench-0.3.1/plesty/server/__main__.py +9 -0
  47. plesty_bench-0.3.1/plesty/server/assets/console.qss +357 -0
  48. plesty_bench-0.3.1/plesty/server/assets/env.example +12 -0
  49. plesty_bench-0.3.1/plesty/server/assets/fleet.example.yaml +44 -0
  50. plesty_bench-0.3.1/plesty/server/assets/plesty-mark.svg +54 -0
  51. plesty_bench-0.3.1/plesty/server/cli/__init__.py +14 -0
  52. plesty_bench-0.3.1/plesty/server/cli/main.py +1000 -0
  53. plesty_bench-0.3.1/plesty/server/config.yaml +35 -0
  54. plesty_bench-0.3.1/plesty/server/host.py +565 -0
  55. plesty_bench-0.3.1/plesty/server/model/__init__.py +164 -0
  56. plesty_bench-0.3.1/plesty/server/model/agent.py +139 -0
  57. plesty_bench-0.3.1/plesty/server/model/catalogue.py +130 -0
  58. plesty_bench-0.3.1/plesty/server/model/device.py +221 -0
  59. plesty_bench-0.3.1/plesty/server/model/device_manager.py +386 -0
  60. plesty_bench-0.3.1/plesty/server/model/envfile.py +65 -0
  61. plesty_bench-0.3.1/plesty/server/model/errors.py +8 -0
  62. plesty_bench-0.3.1/plesty/server/model/field_test.py +356 -0
  63. plesty_bench-0.3.1/plesty/server/model/fleet.py +232 -0
  64. plesty_bench-0.3.1/plesty/server/model/home.py +164 -0
  65. plesty_bench-0.3.1/plesty/server/model/job.py +275 -0
  66. plesty_bench-0.3.1/plesty/server/model/ports.py +151 -0
  67. plesty_bench-0.3.1/plesty/server/model/process.py +124 -0
  68. plesty_bench-0.3.1/plesty/server/model/purge.py +215 -0
  69. plesty_bench-0.3.1/plesty/server/model/traffic.py +114 -0
  70. plesty_bench-0.3.1/plesty/server/presenter/__init__.py +33 -0
  71. plesty_bench-0.3.1/plesty/server/presenter/bench.py +1636 -0
  72. plesty_bench-0.3.1/plesty/server/reexec.py +89 -0
  73. plesty_bench-0.3.1/plesty/server/services/__init__.py +77 -0
  74. plesty_bench-0.3.1/plesty/server/services/agent.py +378 -0
  75. plesty_bench-0.3.1/plesty/server/services/catalogue.py +136 -0
  76. plesty_bench-0.3.1/plesty/server/services/control.py +95 -0
  77. plesty_bench-0.3.1/plesty/server/services/gitbin.py +178 -0
  78. plesty_bench-0.3.1/plesty/server/services/installer.py +619 -0
  79. plesty_bench-0.3.1/plesty/server/services/jobs.py +118 -0
  80. plesty_bench-0.3.1/plesty/server/services/launch.py +241 -0
  81. plesty_bench-0.3.1/plesty/server/services/supervisor.py +487 -0
  82. plesty_bench-0.3.1/plesty/server/view/__init__.py +21 -0
  83. plesty_bench-0.3.1/plesty/server/view/app.py +97 -0
  84. plesty_bench-0.3.1/plesty/server/view/control.py +228 -0
  85. plesty_bench-0.3.1/plesty/server/view/dialogs.py +1228 -0
  86. plesty_bench-0.3.1/plesty/server/view/window.py +1257 -0
  87. plesty_bench-0.3.1/plesty/server/view/workers.py +92 -0
  88. plesty_bench-0.3.1/pyproject.toml +175 -0
  89. plesty_bench-0.3.1/reports/check.json +169 -0
  90. plesty_bench-0.3.1/tests/__init__.py +4 -0
  91. plesty_bench-0.3.1/tests/conftest.py +185 -0
  92. plesty_bench-0.3.1/tests/fake_server.py +195 -0
  93. plesty_bench-0.3.1/tests/test_agent.py +305 -0
  94. plesty_bench-0.3.1/tests/test_agent_settings.py +66 -0
  95. plesty_bench-0.3.1/tests/test_bench.py +773 -0
  96. plesty_bench-0.3.1/tests/test_catalogue.py +124 -0
  97. plesty_bench-0.3.1/tests/test_cli.py +216 -0
  98. plesty_bench-0.3.1/tests/test_control.py +475 -0
  99. plesty_bench-0.3.1/tests/test_device_manager.py +135 -0
  100. plesty_bench-0.3.1/tests/test_field_test.py +147 -0
  101. plesty_bench-0.3.1/tests/test_fleet.py +245 -0
  102. plesty_bench-0.3.1/tests/test_gitbin.py +132 -0
  103. plesty_bench-0.3.1/tests/test_home.py +45 -0
  104. plesty_bench-0.3.1/tests/test_host.py +297 -0
  105. plesty_bench-0.3.1/tests/test_installer.py +530 -0
  106. plesty_bench-0.3.1/tests/test_job.py +91 -0
  107. plesty_bench-0.3.1/tests/test_jobs.py +173 -0
  108. plesty_bench-0.3.1/tests/test_packaging.py +156 -0
  109. plesty_bench-0.3.1/tests/test_purge.py +245 -0
  110. plesty_bench-0.3.1/tests/test_server.py +20 -0
  111. plesty_bench-0.3.1/tests/test_supervisor.py +300 -0
  112. plesty_bench-0.3.1/tests/test_traffic.py +101 -0
  113. plesty_bench-0.3.1/tests/test_view.py +1040 -0
  114. plesty_bench-0.3.1/uv.lock +2297 -0
@@ -0,0 +1,86 @@
1
+ # Custom
2
+ .deprecated/
3
+ logs/
4
+ runs/
5
+
6
+ # Field test and check reports. The JSON documents are the published
7
+ # artifacts — hub.plesty.net and manager.plesty.net read them, so they are
8
+ # committed. The markdown and JSONL forms are for whoever ran the test: they
9
+ # carry the host name, the instrument address, its serial and the lab's file
10
+ # paths, and a hub repository is world-readable.
11
+ # An interrupted `plesty init field-test` leaves its answers here.
12
+ .plesty-field-test.partial.json
13
+
14
+ reports/**/*.md
15
+ reports/**/*.jsonl
16
+ reports/*.md
17
+ reports/*.jsonl
18
+
19
+ # Auto-generated device reference pages (regenerated by `plesty docs`)
20
+ docs/_generated/
21
+
22
+ # Python bytecode and caches
23
+ __pycache__/
24
+ *.py[cod]
25
+ *$py.class
26
+
27
+ # C extensions and shared libraries
28
+ *.so
29
+ *.pyd
30
+ *.dll
31
+
32
+ # Packaging and build artifacts. The dist-* directories are the ones the
33
+ # packaging scripts write: dist-server (server_dist.py), dist-test and dist-exp
34
+ # (the installers' test modes). They must be listed HERE and not only be
35
+ # self-ignored by the .gitignore uv drops inside its own output directories:
36
+ # the build backend honours this file and does not descend into nested ones, so
37
+ # a directory git hides but this file does not name still lands in the sdist.
38
+ build/
39
+ dist/
40
+ dist-*/
41
+ *.egg-info/
42
+ .eggs/
43
+
44
+ # uv's cache, which `[tool.uv] cache-dir` puts inside the project. Same reason:
45
+ # uv hides it from git with a file of its own, which the build backend does not
46
+ # read — and a source distribution built from a synced tree carried 485 MB of
47
+ # it before this line existed.
48
+ .uv-cache/
49
+ .uv_cache/
50
+
51
+ # Local credentials — keep `.env.example` tracked, never the real `.env`
52
+ .env
53
+
54
+ # Virtual environments
55
+ .venv/
56
+ venv/
57
+ env/
58
+ ENV/
59
+
60
+ # Test and coverage outputs
61
+ .pytest_cache/
62
+ .coverage
63
+ .coverage.*
64
+ htmlcov/
65
+ .mypy_cache/
66
+ .ruff_cache/
67
+
68
+ # Jupyter
69
+ .ipynb_checkpoints/
70
+
71
+ # Logs and runtime files
72
+ *.log
73
+ *.pid
74
+
75
+ # IDE/editor
76
+ .vscode/
77
+ .idea/
78
+ *.swp
79
+ *.swo
80
+
81
+ # OS files
82
+ .DS_Store
83
+ Thumbs.db
84
+
85
+ # Generated from packaging/plesty-mark.png at build time (installer:windows).
86
+ packaging/plesty-mark.ico
@@ -0,0 +1,238 @@
1
+ stages:
2
+ - check
3
+ - security
4
+ - deploy
5
+ - release
6
+
7
+ variables:
8
+ # Required for versioningit: the runner must fetch full Git history and tags.
9
+ GIT_DEPTH: "0"
10
+
11
+ include:
12
+ - component: $CI_SERVER_FQDN/plesty/plesty-ci/plesty-standard-ci@main
13
+ inputs:
14
+ standard: quantum
15
+ extra_branch: exp
16
+ access_token: $CI_BOT_TOKEN
17
+
18
+ # The template's hub-release publishes plesty-bench. Both PyPI publishers use
19
+ # the same namespace/project/pipeline file, so they are told apart by the
20
+ # GitLab environment the publishing job runs in (PyPI refuses two pending
21
+ # publishers that differ only in the project name): pypi-bench here,
22
+ # pypi-server below.
23
+ hub-release:
24
+ environment:
25
+ name: pypi-bench
26
+ url: https://pypi.org/project/plesty-bench/
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # The suite, on Windows.
30
+ #
31
+ # Every gate the template runs, runs on Linux — and nearly every bug this
32
+ # component has had is Windows-only: process groups and console control
33
+ # events, path handling, whether a tool exists at all. The
34
+ # `# pragma: no cover — exercised on the Windows CI job` markers in
35
+ # plesty/server/host.py mean this job; without it they claim a coverage that
36
+ # never happened, and a green pipeline says nothing about the platform the
37
+ # benches run on.
38
+ #
39
+ # Runs where hub-check runs (merge requests, the default branch, exp) and on
40
+ # tags. Coverage is measured on Linux, where the gate is; here the outcome is
41
+ # the point, so pytest runs plain.
42
+ # ---------------------------------------------------------------------------
43
+ tests:windows:
44
+ stage: check
45
+ tags:
46
+ - saas-windows-medium-amd64
47
+ rules:
48
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
49
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
50
+ - if: $CI_COMMIT_BRANCH == "exp"
51
+ - if: $CI_COMMIT_TAG
52
+ timeout: 30 minutes
53
+ variables:
54
+ UV_LINK_MODE: copy
55
+ # No cache, deliberately. Measured on this runner: restoring it took 4m03s
56
+ # and saving it several minutes more, to speed a `uv sync` that takes 1m35s
57
+ # even when the cache is warm. It cost about ten minutes of a twenty-two
58
+ # minute job to save one and a half. Downloading the wheels afresh is the
59
+ # cheaper half of that trade.
60
+ UV_CACHE_DIR: .uv_cache
61
+ # tests/test_view.py drives the Qt console and the runner has no desktop
62
+ QT_QPA_PLATFORM: offscreen
63
+ before_script:
64
+ - powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
65
+ - $env:Path = "$env:USERPROFILE\.local\bin;$env:Path"
66
+ - uv --version
67
+ script:
68
+ # --all-extras: the Qt console is part of what a bench runs on Windows
69
+ - uv sync --frozen --all-extras
70
+ # -v so the log names the test that is running when something wedges, and a
71
+ # per-test timeout so a wedge is a traceback rather than the job's 30
72
+ # minutes: `-q` reports failures only in a summary a hang never reaches,
73
+ # which is exactly how the first run of this job came back unreadable.
74
+ - uv run pytest -v -rf --timeout=120 --timeout-method=thread
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # The second distribution and the light installers — on tags.
78
+ #
79
+ # The standard template's hub-release publishes the wheel of this project
80
+ # (plesty-bench, the client). The bench application is a second distribution,
81
+ # plesty-server, assembled from the same tree by packaging/server_dist.py and
82
+ # published here through the same trusted publisher (OIDC, no stored token).
83
+ # The installers carry no application code: they install uv and run
84
+ # `uv tool install "plesty-server[gui]==<version>"` on the bench.
85
+ # ---------------------------------------------------------------------------
86
+ .release-rules:
87
+ rules:
88
+ - if: $CI_COMMIT_TAG
89
+
90
+ release:server-wheel:
91
+ stage: release
92
+ extends: .release-rules
93
+ # the PLESTY runner image (git + uv), as the template's hub-release uses
94
+ image: registry.gitlab.com/plesty/core/plesty-ci/runner:quantum
95
+ environment:
96
+ name: pypi-server
97
+ url: https://pypi.org/project/plesty-server/
98
+ id_tokens:
99
+ PYPI_ID_TOKEN:
100
+ aud: pypi
101
+ variables:
102
+ UV_LINK_MODE: copy
103
+ UV_CACHE_DIR: /tmp/uv-cache
104
+ before_script:
105
+ - git fetch --tags --force
106
+ script:
107
+ - uv sync --frozen
108
+ - VERSION="$(uv run python -c 'from importlib.metadata import version; print(version("plesty-bench"))')"
109
+ # This is the version the installers pin, so it has to be the tag itself --
110
+ # a .dev/+local version here means the tag or the fetch went wrong.
111
+ - |
112
+ test "$VERSION" = "${CI_COMMIT_TAG#v}" || {
113
+ echo "wheel version $VERSION does not match tag ${CI_COMMIT_TAG#v}"; exit 1; }
114
+ - echo "plesty-server $VERSION"
115
+ - uv run python packaging/server_dist.py --out dist-server --version "$VERSION"
116
+ - ls -la dist-server
117
+ - |
118
+ # Trusted publishing: mint an upload token from the job's ID token, as the
119
+ # standard template does for plesty-bench (a PYPI_TOKEN variable wins if set).
120
+ if [ -n "${PYPI_TOKEN:-}" ]; then
121
+ uv publish --token "$PYPI_TOKEN" dist-server/*
122
+ else
123
+ test -n "$PYPI_ID_TOKEN" || { echo "no OIDC ID token"; exit 1; }
124
+ API_TOKEN="$(python3 - <<'PY'
125
+ import json, os, urllib.request
126
+ req = urllib.request.Request("https://pypi.org/_/oidc/mint-token",
127
+ data=json.dumps({"token": os.environ["PYPI_ID_TOKEN"]}).encode(),
128
+ headers={"Content-Type": "application/json"})
129
+ with urllib.request.urlopen(req, timeout=30) as r:
130
+ print(json.load(r)["token"])
131
+ PY
132
+ )"
133
+ uv publish --token "$API_TOKEN" dist-server/*
134
+ fi
135
+ artifacts:
136
+ paths:
137
+ - dist-server/*.whl
138
+ expire_in: 1 year
139
+
140
+ installer:windows:
141
+ stage: release
142
+ # on tags, and on demand from an installer/<name> branch to prove a change
143
+ # to the Inno Setup script before it is tagged
144
+ rules:
145
+ - if: $CI_COMMIT_TAG
146
+ - if: $CI_COMMIT_BRANCH =~ /^installer\//
147
+ tags:
148
+ - saas-windows-medium-amd64
149
+ before_script:
150
+ - powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
151
+ - $env:Path = "$env:USERPROFILE\.local\bin;$env:Path"
152
+ - choco install innosetup -y --no-progress | Out-Null
153
+ - git fetch --tags --force
154
+ script:
155
+ # The version baked in is the PyPI pin the wizard installs, so it must be a
156
+ # released version -- never what versioningit derives from the working tree
157
+ # (0.2.3.dev7+abc1234 and 0.2.2+modified are not on PyPI, and an installer
158
+ # built with one fails on every machine). On a tag it is the tag; an
159
+ # installer/<name> branch proves a script change against the last release,
160
+ # or against INSTALLER_VERSION when set.
161
+ - |
162
+ if ($env:CI_COMMIT_TAG) { $VERSION = $env:CI_COMMIT_TAG -replace '^v', '' }
163
+ elseif ($env:INSTALLER_VERSION) { $VERSION = $env:INSTALLER_VERSION -replace '^v', '' }
164
+ else { $VERSION = (git describe --tags --abbrev=0) -replace '^v', '' }
165
+ if ($VERSION -notmatch '^[0-9]+\.[0-9]+\.[0-9]+$') {
166
+ throw "not a released version: '$VERSION' -- set INSTALLER_VERSION to the release to pin"
167
+ }
168
+ "installer pins plesty-server[gui]==$VERSION"
169
+ - uv sync --frozen --group build
170
+ - uv run --group build python -c "from PIL import Image; Image.open('packaging/plesty-mark.png').save('packaging/plesty-mark.ico', sizes=[(256,256),(128,128),(64,64),(48,48),(32,32),(16,16)])"
171
+ # WINDOWS_SIGN_COMMAND (a protected CI variable) is the signtool command
172
+ # line with Inno's $f placeholder for the file. Without it the build is
173
+ # unsigned and SmartScreen warns on download -- see docs/design/packaging.md.
174
+ - |
175
+ $isccArgs = @("/DAppVersion=$VERSION")
176
+ if ($env:WINDOWS_SIGN_COMMAND) {
177
+ $isccArgs += @("/DSignTool=plesty", "/Splesty=$env:WINDOWS_SIGN_COMMAND")
178
+ } else {
179
+ Write-Warning "WINDOWS_SIGN_COMMAND is not set -- building an unsigned installer"
180
+ }
181
+ & "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" @isccArgs packaging\windows\plesty-server.iss
182
+ if ($LASTEXITCODE -ne 0) { throw "ISCC failed with exit code $LASTEXITCODE" }
183
+ - Get-ChildItem dist
184
+ artifacts:
185
+ paths:
186
+ - dist/plesty-server-*-windows-setup.exe
187
+ expire_in: 1 year
188
+
189
+ # The GitLab release for a tag: the Windows installer and the install script
190
+ # for macOS/Linux from the package registry (permanent), plus the two PyPI
191
+ # distributions — the Releases page is the download list.
192
+ release:assets:
193
+ stage: release
194
+ extends: .release-rules
195
+ image: registry.gitlab.com/gitlab-org/release-cli:latest
196
+ needs:
197
+ - job: installer:windows
198
+ artifacts: true
199
+ - job: release:server-wheel
200
+ artifacts: false
201
+ variables:
202
+ REGISTRY: "$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/generic/plesty-server/$CI_COMMIT_TAG"
203
+ script:
204
+ - apk add --no-cache curl >/dev/null
205
+ - ls -la dist
206
+ - |
207
+ setup="$(ls dist/plesty-server-*-windows-setup.exe | head -1)"
208
+ [ -n "$setup" ] || { echo "no Windows installer"; exit 1; }
209
+ curl --fail --silent --show-error --header "JOB-TOKEN: $CI_JOB_TOKEN" \
210
+ --upload-file "$setup" "$REGISTRY/plesty-server-$CI_COMMIT_TAG-windows-setup.exe"
211
+ # Stamp the release into the script so it pins, like the Windows installer.
212
+ version="${CI_COMMIT_TAG#v}"
213
+ sed "s/@PLESTY_VERSION@/$version/" packaging/install.sh > install.sh
214
+ grep -q "release_version=\"$version\"" install.sh || { echo "install.sh not stamped"; exit 1; }
215
+ curl --fail --silent --show-error --header "JOB-TOKEN: $CI_JOB_TOKEN" \
216
+ --upload-file install.sh "$REGISTRY/install.sh"
217
+ release:
218
+ tag_name: $CI_COMMIT_TAG
219
+ name: "plesty-server $CI_COMMIT_TAG"
220
+ description: >-
221
+ Windows: run the installer (it installs uv and the application from PyPI;
222
+ Start-menu entry, optional desktop shortcut). macOS and Linux: download
223
+ install.sh and run it with sh. Developer machines: uv tool install plesty-bench.
224
+ Update a bench with uv tool upgrade plesty-server. See CHANGELOG.md.
225
+ assets:
226
+ links:
227
+ - name: "plesty-server-$CI_COMMIT_TAG-windows-setup.exe (Windows installer)"
228
+ url: "$REGISTRY/plesty-server-$CI_COMMIT_TAG-windows-setup.exe"
229
+ link_type: package
230
+ - name: "install.sh (macOS and Linux: run with sh)"
231
+ url: "$REGISTRY/install.sh"
232
+ link_type: other
233
+ - name: "plesty-server on PyPI (the bench application)"
234
+ url: "https://pypi.org/project/plesty-server/"
235
+ link_type: other
236
+ - name: "plesty-bench on PyPI (the client)"
237
+ url: "https://pypi.org/project/plesty-bench/"
238
+ link_type: other