pybiolib 0.2.951__py3-none-any.whl → 1.2.1890__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. biolib/__init__.py +357 -11
  2. biolib/_data_record/data_record.py +380 -0
  3. biolib/_index/__init__.py +0 -0
  4. biolib/_index/index.py +55 -0
  5. biolib/_index/query_result.py +103 -0
  6. biolib/_internal/__init__.py +0 -0
  7. biolib/_internal/add_copilot_prompts.py +58 -0
  8. biolib/_internal/add_gui_files.py +81 -0
  9. biolib/_internal/data_record/__init__.py +1 -0
  10. biolib/_internal/data_record/data_record.py +85 -0
  11. biolib/_internal/data_record/push_data.py +116 -0
  12. biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
  13. biolib/_internal/errors.py +5 -0
  14. biolib/_internal/file_utils.py +125 -0
  15. biolib/_internal/fuse_mount/__init__.py +1 -0
  16. biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
  17. biolib/_internal/http_client.py +159 -0
  18. biolib/_internal/lfs/__init__.py +1 -0
  19. biolib/_internal/lfs/cache.py +51 -0
  20. biolib/_internal/libs/__init__.py +1 -0
  21. biolib/_internal/libs/fusepy/__init__.py +1257 -0
  22. biolib/_internal/push_application.py +488 -0
  23. biolib/_internal/runtime.py +22 -0
  24. biolib/_internal/string_utils.py +13 -0
  25. biolib/_internal/templates/__init__.py +1 -0
  26. biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
  27. biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
  28. biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
  29. biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
  30. biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
  31. biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
  32. biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
  33. biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
  34. biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
  35. biolib/_internal/templates/gitignore_template/.gitignore +10 -0
  36. biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
  37. biolib/_internal/templates/gui_template/App.tsx +53 -0
  38. biolib/_internal/templates/gui_template/Dockerfile +27 -0
  39. biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
  40. biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
  41. biolib/_internal/templates/gui_template/index.css +5 -0
  42. biolib/_internal/templates/gui_template/index.html +13 -0
  43. biolib/_internal/templates/gui_template/index.tsx +10 -0
  44. biolib/_internal/templates/gui_template/package.json +27 -0
  45. biolib/_internal/templates/gui_template/tsconfig.json +24 -0
  46. biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
  47. biolib/_internal/templates/gui_template/vite.config.mts +10 -0
  48. biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
  49. biolib/_internal/templates/init_template/Dockerfile +14 -0
  50. biolib/_internal/templates/init_template/requirements.txt +1 -0
  51. biolib/_internal/templates/init_template/run.py +12 -0
  52. biolib/_internal/templates/init_template/run.sh +4 -0
  53. biolib/_internal/templates/templates.py +25 -0
  54. biolib/_internal/tree_utils.py +106 -0
  55. biolib/_internal/utils/__init__.py +65 -0
  56. biolib/_internal/utils/auth.py +46 -0
  57. biolib/_internal/utils/job_url.py +33 -0
  58. biolib/_internal/utils/multinode.py +263 -0
  59. biolib/_runtime/runtime.py +157 -0
  60. biolib/_session/session.py +44 -0
  61. biolib/_shared/__init__.py +0 -0
  62. biolib/_shared/types/__init__.py +74 -0
  63. biolib/_shared/types/account.py +12 -0
  64. biolib/_shared/types/account_member.py +8 -0
  65. biolib/_shared/types/app.py +9 -0
  66. biolib/_shared/types/data_record.py +40 -0
  67. biolib/_shared/types/experiment.py +32 -0
  68. biolib/_shared/types/file_node.py +17 -0
  69. biolib/_shared/types/push.py +6 -0
  70. biolib/_shared/types/resource.py +37 -0
  71. biolib/_shared/types/resource_deploy_key.py +11 -0
  72. biolib/_shared/types/resource_permission.py +14 -0
  73. biolib/_shared/types/resource_version.py +19 -0
  74. biolib/_shared/types/result.py +14 -0
  75. biolib/_shared/types/typing.py +10 -0
  76. biolib/_shared/types/user.py +19 -0
  77. biolib/_shared/utils/__init__.py +7 -0
  78. biolib/_shared/utils/resource_uri.py +75 -0
  79. biolib/api/__init__.py +6 -0
  80. biolib/api/client.py +168 -0
  81. biolib/app/app.py +252 -49
  82. biolib/app/search_apps.py +45 -0
  83. biolib/biolib_api_client/api_client.py +126 -31
  84. biolib/biolib_api_client/app_types.py +24 -4
  85. biolib/biolib_api_client/auth.py +31 -8
  86. biolib/biolib_api_client/biolib_app_api.py +147 -52
  87. biolib/biolib_api_client/biolib_job_api.py +161 -141
  88. biolib/biolib_api_client/job_types.py +21 -5
  89. biolib/biolib_api_client/lfs_types.py +7 -23
  90. biolib/biolib_api_client/user_state.py +56 -0
  91. biolib/biolib_binary_format/__init__.py +1 -4
  92. biolib/biolib_binary_format/file_in_container.py +105 -0
  93. biolib/biolib_binary_format/module_input.py +24 -7
  94. biolib/biolib_binary_format/module_output_v2.py +149 -0
  95. biolib/biolib_binary_format/remote_endpoints.py +34 -0
  96. biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
  97. biolib/biolib_binary_format/saved_job.py +3 -2
  98. biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
  99. biolib/biolib_binary_format/system_status_update.py +3 -2
  100. biolib/biolib_binary_format/utils.py +175 -0
  101. biolib/biolib_docker_client/__init__.py +11 -2
  102. biolib/biolib_errors.py +36 -0
  103. biolib/biolib_logging.py +27 -10
  104. biolib/cli/__init__.py +38 -0
  105. biolib/cli/auth.py +46 -0
  106. biolib/cli/data_record.py +164 -0
  107. biolib/cli/index.py +32 -0
  108. biolib/cli/init.py +421 -0
  109. biolib/cli/lfs.py +101 -0
  110. biolib/cli/push.py +50 -0
  111. biolib/cli/run.py +63 -0
  112. biolib/cli/runtime.py +14 -0
  113. biolib/cli/sdk.py +16 -0
  114. biolib/cli/start.py +56 -0
  115. biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
  116. biolib/compute_node/job_worker/cache_state.py +66 -88
  117. biolib/compute_node/job_worker/cache_types.py +1 -6
  118. biolib/compute_node/job_worker/docker_image_cache.py +112 -37
  119. biolib/compute_node/job_worker/executors/__init__.py +0 -3
  120. biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
  121. biolib/compute_node/job_worker/executors/docker_types.py +9 -1
  122. biolib/compute_node/job_worker/executors/types.py +19 -9
  123. biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
  124. biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
  125. biolib/compute_node/job_worker/job_storage.py +108 -0
  126. biolib/compute_node/job_worker/job_worker.py +397 -212
  127. biolib/compute_node/job_worker/large_file_system.py +87 -38
  128. biolib/compute_node/job_worker/network_alloc.py +99 -0
  129. biolib/compute_node/job_worker/network_buffer.py +240 -0
  130. biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
  131. biolib/compute_node/job_worker/utils.py +9 -24
  132. biolib/compute_node/remote_host_proxy.py +400 -98
  133. biolib/compute_node/utils.py +31 -9
  134. biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
  135. biolib/compute_node/webserver/proxy_utils.py +28 -0
  136. biolib/compute_node/webserver/webserver.py +130 -44
  137. biolib/compute_node/webserver/webserver_types.py +2 -6
  138. biolib/compute_node/webserver/webserver_utils.py +77 -12
  139. biolib/compute_node/webserver/worker_thread.py +183 -42
  140. biolib/experiments/__init__.py +0 -0
  141. biolib/experiments/experiment.py +356 -0
  142. biolib/jobs/__init__.py +1 -0
  143. biolib/jobs/job.py +741 -0
  144. biolib/jobs/job_result.py +185 -0
  145. biolib/jobs/types.py +50 -0
  146. biolib/py.typed +0 -0
  147. biolib/runtime/__init__.py +14 -0
  148. biolib/sdk/__init__.py +91 -0
  149. biolib/tables.py +34 -0
  150. biolib/typing_utils.py +2 -7
  151. biolib/user/__init__.py +1 -0
  152. biolib/user/sign_in.py +54 -0
  153. biolib/utils/__init__.py +162 -0
  154. biolib/utils/cache_state.py +94 -0
  155. biolib/utils/multipart_uploader.py +194 -0
  156. biolib/utils/seq_util.py +150 -0
  157. biolib/utils/zip/remote_zip.py +640 -0
  158. pybiolib-1.2.1890.dist-info/METADATA +41 -0
  159. pybiolib-1.2.1890.dist-info/RECORD +177 -0
  160. {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
  161. pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
  162. README.md +0 -17
  163. biolib/app/app_result.py +0 -68
  164. biolib/app/utils.py +0 -62
  165. biolib/biolib-js/0-biolib.worker.js +0 -1
  166. biolib/biolib-js/1-biolib.worker.js +0 -1
  167. biolib/biolib-js/2-biolib.worker.js +0 -1
  168. biolib/biolib-js/3-biolib.worker.js +0 -1
  169. biolib/biolib-js/4-biolib.worker.js +0 -1
  170. biolib/biolib-js/5-biolib.worker.js +0 -1
  171. biolib/biolib-js/6-biolib.worker.js +0 -1
  172. biolib/biolib-js/index.html +0 -10
  173. biolib/biolib-js/main-biolib.js +0 -1
  174. biolib/biolib_api_client/biolib_account_api.py +0 -21
  175. biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
  176. biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
  177. biolib/biolib_binary_format/module_output.py +0 -58
  178. biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
  179. biolib/biolib_push.py +0 -114
  180. biolib/cli.py +0 -203
  181. biolib/cli_utils.py +0 -273
  182. biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
  183. biolib/compute_node/enclave/__init__.py +0 -2
  184. biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
  185. biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
  186. biolib/compute_node/job_worker/executors/base_executor.py +0 -18
  187. biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
  188. biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
  189. biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
  190. biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
  191. biolib/lfs.py +0 -196
  192. biolib/pyppeteer/.circleci/config.yml +0 -100
  193. biolib/pyppeteer/.coveragerc +0 -3
  194. biolib/pyppeteer/.gitignore +0 -89
  195. biolib/pyppeteer/.pre-commit-config.yaml +0 -28
  196. biolib/pyppeteer/CHANGES.md +0 -253
  197. biolib/pyppeteer/CONTRIBUTING.md +0 -26
  198. biolib/pyppeteer/LICENSE +0 -12
  199. biolib/pyppeteer/README.md +0 -137
  200. biolib/pyppeteer/docs/Makefile +0 -177
  201. biolib/pyppeteer/docs/_static/custom.css +0 -28
  202. biolib/pyppeteer/docs/_templates/layout.html +0 -10
  203. biolib/pyppeteer/docs/changes.md +0 -1
  204. biolib/pyppeteer/docs/conf.py +0 -299
  205. biolib/pyppeteer/docs/index.md +0 -21
  206. biolib/pyppeteer/docs/make.bat +0 -242
  207. biolib/pyppeteer/docs/reference.md +0 -211
  208. biolib/pyppeteer/docs/server.py +0 -60
  209. biolib/pyppeteer/poetry.lock +0 -1699
  210. biolib/pyppeteer/pyppeteer/__init__.py +0 -135
  211. biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
  212. biolib/pyppeteer/pyppeteer/browser.py +0 -401
  213. biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
  214. biolib/pyppeteer/pyppeteer/command.py +0 -22
  215. biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
  216. biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
  217. biolib/pyppeteer/pyppeteer/coverage.py +0 -346
  218. biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
  219. biolib/pyppeteer/pyppeteer/dialog.py +0 -79
  220. biolib/pyppeteer/pyppeteer/domworld.py +0 -597
  221. biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
  222. biolib/pyppeteer/pyppeteer/errors.py +0 -48
  223. biolib/pyppeteer/pyppeteer/events.py +0 -63
  224. biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
  225. biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
  226. biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
  227. biolib/pyppeteer/pyppeteer/helpers.py +0 -245
  228. biolib/pyppeteer/pyppeteer/input.py +0 -371
  229. biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
  230. biolib/pyppeteer/pyppeteer/launcher.py +0 -683
  231. biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
  232. biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
  233. biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
  234. biolib/pyppeteer/pyppeteer/multimap.py +0 -82
  235. biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
  236. biolib/pyppeteer/pyppeteer/options.py +0 -8
  237. biolib/pyppeteer/pyppeteer/page.py +0 -1728
  238. biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
  239. biolib/pyppeteer/pyppeteer/target.py +0 -147
  240. biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
  241. biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
  242. biolib/pyppeteer/pyppeteer/tracing.py +0 -93
  243. biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
  244. biolib/pyppeteer/pyppeteer/util.py +0 -18
  245. biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
  246. biolib/pyppeteer/pyppeteer/worker.py +0 -101
  247. biolib/pyppeteer/pyproject.toml +0 -97
  248. biolib/pyppeteer/spell.txt +0 -137
  249. biolib/pyppeteer/tox.ini +0 -72
  250. biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
  251. biolib/start_cli.py +0 -7
  252. biolib/utils.py +0 -47
  253. biolib/validators/validate_app_version.py +0 -183
  254. biolib/validators/validate_argument.py +0 -134
  255. biolib/validators/validate_module.py +0 -323
  256. biolib/validators/validate_zip_file.py +0 -40
  257. biolib/validators/validator_utils.py +0 -103
  258. pybiolib-0.2.951.dist-info/LICENSE +0 -21
  259. pybiolib-0.2.951.dist-info/METADATA +0 -61
  260. pybiolib-0.2.951.dist-info/RECORD +0 -153
  261. pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
  262. /LICENSE → /pybiolib-1.2.1890.dist-info/licenses/LICENSE +0 -0
@@ -1,135 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- import logging
4
- import os
5
- from pathlib import Path
6
- from typing import Any, List, Sequence, Union
7
-
8
- from appdirs import AppDirs
9
-
10
- __author__ = 'Hiroyuki Takagi, Bernardas Ališauskas, Matt Marcus'
11
- __email__ = 'pyppeteer@protonmail.com'
12
- __version__ = '0.2.2'
13
- __chromium_revision__ = '722234'
14
- __base_puppeteer_version__ = 'v2.1.1'
15
- __pyppeteer_home__ = os.environ.get('PYPPETEER_HOME', AppDirs('pyppeteer').user_data_dir) # type: str
16
-
17
-
18
- from biolib.pyppeteer.pyppeteer.browser import Browser # isort:skip
19
- from biolib.pyppeteer.pyppeteer.browser_fetcher import BrowserFetcher, Platform # isort:skip
20
- from biolib.pyppeteer.pyppeteer.device_descriptors import devices # isort:skip
21
- from biolib.pyppeteer.pyppeteer.launcher import ChromeLauncher, FirefoxLauncher, launcher # isort:skip
22
- from biolib.pyppeteer.pyppeteer.models import BrowserOptions, ChromeArgOptions, Devices, LaunchOptions, Protocol # isort:skip
23
- from biolib.pyppeteer.pyppeteer.websocket_transport import WebsocketTransport # isort:skip
24
-
25
- # Setup root logger
26
- _logger = logging.getLogger(__name__)
27
- _logger.setLevel(logging.DEBUG)
28
-
29
-
30
- class Pyppeteer:
31
- def __init__(self, projectRoot: str = None, preferredRevision: str = None):
32
- self._projectRoot = projectRoot
33
- self._preferredRevision = preferredRevision
34
- self._lazyLauncher = None
35
- self.productName = None
36
-
37
- @property
38
- def executablePath(self) -> Union[str, Path]:
39
- return self._launcher.executablePath
40
-
41
- @property
42
- def product(self) -> str:
43
- return self._launcher.product
44
-
45
- @property
46
- def devices(self) -> Devices:
47
- return devices
48
-
49
- async def launch(self, **kwargs: Union[LaunchOptions, ChromeArgOptions, BrowserOptions]) -> Browser:
50
- if not self.productName and kwargs:
51
- self.productName = kwargs.get('product')
52
- return await self._launcher.launch(**kwargs)
53
-
54
- async def connect(
55
- self,
56
- browserWSEndpoint: str = None,
57
- browserURL: str = None,
58
- transport: WebsocketTransport = None,
59
- ignoreHTTPSErrors: bool = False,
60
- slowMo: float = 0,
61
- defaultViewport: Protocol.Page.Viewport = None,
62
- ) -> Browser:
63
- return await self._launcher.connect(
64
- browserWSEndpoint=browserWSEndpoint,
65
- browserURL=browserURL,
66
- ignoreHTTPSErrors=ignoreHTTPSErrors,
67
- transport=transport,
68
- slowMo=slowMo,
69
- defaultViewport=defaultViewport,
70
- )
71
-
72
- @property
73
- def _launcher(self) -> Union[FirefoxLauncher, ChromeLauncher]:
74
- if not self._lazyLauncher:
75
- self._lazyLauncher = launcher(
76
- projectRoot=self._projectRoot, preferredRevision=self._preferredRevision, product=self.productName
77
- )
78
- return self._lazyLauncher
79
-
80
- async def defaultArgs(
81
- self,
82
- args: Sequence[str] = None,
83
- devtools: bool = False,
84
- headless: bool = None,
85
- userDataDir: str = None,
86
- **_: Any,
87
- ) -> List[str]:
88
- return self._launcher.default_args(args=args, devtools=devtools, headless=headless, userDataDir=userDataDir)
89
-
90
- def createBrowserFetcher(self, platform: Platform = None, host: str = None,) -> BrowserFetcher:
91
- return BrowserFetcher(projectRoot=self._projectRoot, platform=platform, host=host)
92
-
93
-
94
- # shortcut methods
95
- async def launch(
96
- projectRoot: Union[Path, str] = None,
97
- preferredRevision: str = None,
98
- **kwargs: Union[LaunchOptions, ChromeArgOptions, BrowserOptions],
99
- ) -> Browser:
100
- return await Pyppeteer(projectRoot, preferredRevision).launch(**kwargs)
101
-
102
-
103
- async def connect(
104
- projectRoot: Union[Path, str] = None,
105
- preferredRevision: str = None,
106
- browserWSEndpoint: str = None,
107
- browserURL: str = None,
108
- transport: WebsocketTransport = None,
109
- ignoreHTTPSErrors: bool = False,
110
- slowMo: float = 0,
111
- defaultViewport: Protocol.Page.Viewport = None,
112
- ) -> Browser:
113
- return await Pyppeteer(projectRoot, preferredRevision).connect(
114
- browserWSEndpoint=browserWSEndpoint,
115
- browserURL=browserURL,
116
- transport=transport,
117
- ignoreHTTPSErrors=ignoreHTTPSErrors,
118
- slowMo=slowMo,
119
- defaultViewport=defaultViewport,
120
- )
121
-
122
-
123
- version = __version__
124
- version_info = tuple(int(i) for i in version.split('.'))
125
-
126
- __all__ = [
127
- '__chromium_revision__',
128
- '__pyppeteer_home__',
129
- 'version',
130
- 'version_info',
131
- 'devices',
132
- 'launch',
133
- 'connect',
134
- 'Pyppeteer',
135
- ]
@@ -1,286 +0,0 @@
1
- from typing import TYPE_CHECKING, Dict, List, Set, Union
2
-
3
- if TYPE_CHECKING:
4
- from pyppeteer.connection.cdpsession import CDPSession
5
- from pyppeteer.jshandle import ElementHandle
6
-
7
-
8
- class Accessibility:
9
- def __init__(self, client: 'CDPSession'):
10
- self._client = client
11
-
12
- async def snapshot(self, interestingOnly: bool = True, root: 'ElementHandle' = None):
13
- nodes = (await self._client.send('Accessibility.getFullAXTree'))['nodes']
14
- backendNodeId = None
15
- if root:
16
- node = (await self._client.send('DOM.describeNode', {'objectId': root._remoteObject['objectId']}))['node']
17
- backendNodeId = node['backendNodeId']
18
- defaultRoot = AXNode.createTree(nodes)
19
- needle = defaultRoot
20
- if backendNodeId:
21
- needle = defaultRoot.find(lambda _node: _node._payload['backendDOMNodeId'] == backendNodeId)
22
- if not needle:
23
- return
24
- if not interestingOnly:
25
- return serializeTree(needle)[0]
26
-
27
- interestingNodes = set()
28
- collectInterestingNodes(interestingNodes, defaultRoot, False)
29
- if needle not in interestingNodes:
30
- return None
31
- return serializeTree(needle, interestingNodes)[0]
32
-
33
-
34
- class AXNode:
35
- def __init__(self, payload: Dict):
36
- """
37
- :param payload: dict with keys nodeId, name, role, properties
38
- """
39
- self._payload = payload
40
- self._children: List[AXNode] = []
41
- self._richlyEditable = False
42
- self._editable = False
43
- self._focusable = False
44
- self._expanded = False
45
- self._hidden = False
46
- self._name = payload.get('name', {}).get('value', '')
47
- self._role = payload.get('role', {}).get('value', 'Unknown')
48
- self._cacheHasFocusableChild = None
49
-
50
- for property in payload.get('properties', []):
51
- _name = property['name']
52
- _value = property.get('value', {}).get('value', None)
53
- if _name == 'editable':
54
- self._richlyEditable = _value == 'richtext'
55
- self._editable = True
56
- if _name == 'focusable':
57
- self._focusable = _value
58
- if _name == 'expanded':
59
- self._expanded = _value
60
- if _name == 'hidden':
61
- self._hidden = _value
62
-
63
- @property
64
- def _isPlainTextField(self):
65
- if self._richlyEditable:
66
- return False
67
- if self._editable:
68
- return True
69
- return self._role in ['textbox', 'ComboBox', 'searchbox']
70
-
71
- @property
72
- def _isTextOnlyObject(self):
73
- return self._role in ['LineBreak', 'text', 'InlineTextBox']
74
-
75
- @property
76
- def _hasFocusableChild(self):
77
- if self._cacheHasFocusableChild is None:
78
- self._cacheHasFocusableChild = False
79
- for child in self._children:
80
- if child._focusable or child._hasFocusableChild:
81
- self._cacheHasFocusableChild = True
82
- break
83
- return self._cacheHasFocusableChild
84
-
85
- def find(self, predicate):
86
- if predicate(self):
87
- return self
88
- for child in self._children:
89
- result = child.find(predicate)
90
- if result:
91
- return result
92
-
93
- def isLeafNode(self):
94
- if not self._children:
95
- return True
96
-
97
- # These types of objects may have children that we use as internal
98
- # implementation details, but we want to expose them as leaves to platform
99
- # accessibility APIs because screen readers might be confused if they find
100
- # any children.
101
- if self._isPlainTextField or self._isTextOnlyObject:
102
- return True
103
- # Roles whose children are only presentational according to the ARIA and
104
- # HTML5 Specs should be hidden from screen readers.
105
- # (Note that whilst ARIA buttons can have only presentational children, HTML5
106
- # buttons are allowed to have content.)
107
- if self._role in [
108
- 'doc-cover',
109
- 'graphics-symbol',
110
- 'img',
111
- 'Meter',
112
- 'scrollbar',
113
- 'slider',
114
- 'separator',
115
- 'progressbar',
116
- ]:
117
- return True
118
-
119
- # here and below: android heuristics
120
- if self._hasFocusableChild:
121
- return False
122
- if self._focusable and self._name:
123
- return True
124
- if self._role == 'heading' and self._name:
125
- return True
126
- return False
127
-
128
- @property
129
- def isControl(self):
130
- return self._role in [
131
- 'button',
132
- 'checkbox',
133
- 'ColorWell',
134
- 'combobox',
135
- 'DisclosureTriangle',
136
- 'listbox',
137
- 'menu',
138
- 'menubar',
139
- 'menuitem',
140
- 'menuitemcheckbox',
141
- 'menuitemradio',
142
- 'radio',
143
- 'scrollbar',
144
- 'searchbox',
145
- 'slider',
146
- 'spinbutton',
147
- 'switch',
148
- 'tab',
149
- 'textbox',
150
- 'tree',
151
- ]
152
-
153
- def isInteresting(self, insideControl: bool) -> bool:
154
- role = self._role
155
- if role == 'Ignored' or self._hidden:
156
- return False
157
- if self._focusable or self._richlyEditable:
158
- return True
159
-
160
- # If it's not focusable but has a control role, then it's interesting.
161
- if self.isControl:
162
- return True
163
-
164
- # A non focusable child of a control is not interesting
165
- if insideControl:
166
- return False
167
- return self.isLeafNode() and bool(self._name)
168
-
169
- def serialize(self): # noqa C901
170
- properties: Dict[str, Union[str, float, bool]] = {}
171
- for property_ in self._payload.get('properties', []):
172
- properties[property_['name'].lower()] = property_['value'].get('value')
173
- if self._payload.get('name'):
174
- properties['name'] = self._payload['name']['value']
175
- if self._payload.get('value'):
176
- properties['value'] = self._payload['value']['value']
177
- if self._payload.get('description'):
178
- properties['description'] = self._payload['description']['value']
179
-
180
- node = {'role': self._role}
181
- userStringProperties = [
182
- 'name',
183
- 'value',
184
- 'description',
185
- 'keyshortcuts',
186
- 'roledescription',
187
- 'valuetext',
188
- ]
189
- for prop in userStringProperties:
190
- if prop not in properties:
191
- continue
192
- node[prop] = properties[prop]
193
- booleanProperties = [
194
- 'disabled',
195
- 'expanded',
196
- 'focused',
197
- 'modal',
198
- 'multiline',
199
- 'multiselectable',
200
- 'readonly',
201
- 'required',
202
- 'selected',
203
- ]
204
- for prop in booleanProperties:
205
- # WebArea's treat focus differently than other nodes. They report whether their frame has focus,
206
- # not whether focus is specifically on the root node.
207
- if prop == 'focused' and self._role == 'WebArea':
208
- continue
209
- value = properties.get(prop)
210
- if value:
211
- node[prop] = value
212
-
213
- tristateProperties = [
214
- 'checked',
215
- 'pressed',
216
- ]
217
- for prop in tristateProperties:
218
- if prop not in properties:
219
- continue
220
- value = properties.get(prop)
221
- if value != 'mixed':
222
- if value == 'true':
223
- value = True
224
- else:
225
- value = False
226
- node[prop] = value
227
-
228
- numericProperties = [
229
- 'level',
230
- 'valuemax',
231
- 'valuemin',
232
- ]
233
- for prop in numericProperties:
234
- if prop not in properties:
235
- continue
236
- node[prop] = properties.get(prop)
237
-
238
- tokenProperties = [
239
- 'autocomplete',
240
- 'haspopup',
241
- 'invalid',
242
- 'orientation',
243
- ]
244
- for prop in tokenProperties:
245
- value = properties.get(prop)
246
- if not value or value == 'false':
247
- continue
248
- node[prop] = value
249
- return node
250
-
251
- @staticmethod
252
- def createTree(payloads: List[dict]) -> 'AXNode':
253
- """
254
-
255
- :param payloads: List of dictionaries of AXNode kwargs
256
- :return:
257
- """
258
- nodeById = {}
259
- for payload in payloads:
260
- nodeById[payload['nodeId']] = AXNode(payload)
261
- for node in nodeById.values():
262
- for childId in node._payload.get('childIds', []):
263
- node._children.append(nodeById[childId])
264
- return [*nodeById.values()][0]
265
-
266
-
267
- def collectInterestingNodes(collection: Set[AXNode], node: AXNode, insideControl: bool):
268
- if node.isInteresting(insideControl):
269
- collection.add(node)
270
- if node.isLeafNode():
271
- return
272
- insideControl = insideControl or node.isControl
273
- for child in node._children:
274
- collectInterestingNodes(collection, child, insideControl)
275
-
276
-
277
- def serializeTree(node: 'AXNode', whitelistedNodes: Set[AXNode] = None):
278
- children = []
279
- for child in node._children:
280
- children.extend(serializeTree(child, whitelistedNodes))
281
- if whitelistedNodes and node not in whitelistedNodes:
282
- return children
283
- serializedNode = node.serialize()
284
- if children:
285
- serializedNode['children'] = children
286
- return [serializedNode]