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,306 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- import asyncio
5
- import logging
6
- import re
7
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set
8
-
9
- from pyee import AsyncIOEventEmitter
10
- from biolib.pyppeteer.pyppeteer import helpers
11
- from biolib.pyppeteer.pyppeteer.connection import CDPSession
12
- from biolib.pyppeteer.pyppeteer.errors import BrowserError, ElementHandleError, PageError
13
- from biolib.pyppeteer.pyppeteer.events import Events
14
- from biolib.pyppeteer.pyppeteer.execution_context import ExecutionContext
15
- from biolib.pyppeteer.pyppeteer.frame import Frame
16
- from biolib.pyppeteer.pyppeteer.lifecycle_watcher import LifecycleWatcher
17
- from biolib.pyppeteer.pyppeteer.models import WaitTargets
18
- from biolib.pyppeteer.pyppeteer.network_manager import NetworkManager, Response
19
- from biolib.pyppeteer.pyppeteer.timeout_settings import TimeoutSettings
20
-
21
- if TYPE_CHECKING:
22
- from pyppeteer.page import Page
23
-
24
- logger = logging.getLogger(__name__)
25
-
26
- UTILITY_WORLD_NAME = '__pyppeteer_utility_world__'
27
- EVALUATION_SCRIPT_URL = '__pyppeteer_evaluation_script__'
28
- SOURCE_URL_REGEX = re.compile(r'^[ \t]*//[@#] sourceURL=\s*(\S*?)\s*$', re.MULTILINE,)
29
-
30
-
31
- class FrameManager(AsyncIOEventEmitter):
32
- """FrameManager class."""
33
-
34
- def __init__(
35
- self, client: CDPSession, page: 'Page', ignoreHTTPSErrors: bool, timeoutSettings: TimeoutSettings
36
- ) -> None:
37
- """Make new frame manager."""
38
- super().__init__()
39
- self._client = client
40
- self._page = page
41
- self._networkManager = NetworkManager(client, ignoreHTTPSErrors, self)
42
- self._timeoutSettings = timeoutSettings
43
- self._mainFrame: Optional[Frame] = None
44
- self._frames: Dict[Any, Frame] = {}
45
- self._contextIdToContext: Dict[int, ExecutionContext] = {}
46
- self._isolatedWorlds: Set[str] = set()
47
-
48
- client.on(
49
- 'Page.frameAttached',
50
- lambda event: self._onFrameAttached(event.get('frameId', ''), event.get('parentFrameId', '')),
51
- )
52
- client.on('Page.frameNavigated', lambda event: self._onFrameNavigated(event.get('frame')))
53
- client.on(
54
- 'Page.navigatedWithinDocument',
55
- lambda event: self._onFrameNavigatedWithinDocument(event.get('frameId'), event.get('url')),
56
- )
57
- client.on('Page.frameDetached', lambda event: self._onFrameDetached(event.get('frameId')))
58
- client.on('Page.frameStoppedLoading', lambda event: self._onFrameStoppedLoading(event.get('frameId')))
59
- client.on(
60
- 'Runtime.executionContextCreated', lambda event: self._onExecutionContextCreated(event.get('context'))
61
- )
62
- client.on(
63
- 'Runtime.executionContextDestroyed',
64
- lambda event: self._onExecutionContextDestroyed(event.get('executionContextId')),
65
- )
66
- client.on('Runtime.executionContextsCleared', lambda event: self._onExecutionContextsCleared())
67
- client.on('Page.lifecycleEvent', lambda event: self._onLifecycleEvent(event))
68
-
69
- async def initialize(self) -> None:
70
- await self._client.send('Page.enable')
71
- frameTree = (await self._client.send('Page.getFrameTree'))['frameTree']
72
- self._handleFrameTree(frameTree)
73
-
74
- async def runtime_enabled() -> None:
75
- await self._client.send('Runtime.enable', {})
76
- await self._ensureIsolatedWorld(UTILITY_WORLD_NAME)
77
-
78
- await asyncio.gather(
79
- self._client.send('Page.setLifecycleEventsEnabled', {'enabled': True}),
80
- runtime_enabled(),
81
- self._networkManager.initialize(),
82
- )
83
-
84
- @property
85
- def networkManager(self) -> NetworkManager:
86
- return self._networkManager
87
-
88
- async def navigateFrame(
89
- self, frame: 'Frame', url: str, referer: str = None, timeout: float = None, waitUntil: WaitTargets = None,
90
- ) -> Optional[Response]:
91
- ensureNewDocumentNavigation = False
92
-
93
- async def navigate(url_: str, referer_: Optional[str], frameId: str) -> Optional[Exception]:
94
- try:
95
- # careful, this is the correct spelling of referrer (even though everywhere else the key is correct
96
- response = await self._client.send(
97
- 'Page.navigate', {'url': url_, 'referrer': referer_, 'frameId': frameId}
98
- )
99
- nonlocal ensureNewDocumentNavigation
100
- ensureNewDocumentNavigation = bool(response.get('loaderId'))
101
- if response.get('errorText'):
102
- raise BrowserError(f'{response["errorText"]} at {url}')
103
- except Exception as e:
104
- return e
105
-
106
- if referer is None:
107
- referer = self._networkManager.extraHTTPHeaders().get('referer', '')
108
- if waitUntil is None:
109
- waitUntil = ['load']
110
- if timeout is None:
111
- timeout = self._timeoutSettings.navigationTimeout
112
-
113
- watcher = LifecycleWatcher(self, frame=frame, timeout=timeout, waitUntil=waitUntil)
114
- error = await helpers.future_race(navigate(url, referer, frame._id), watcher.timeoutOrTerminationFuture)
115
- if not error:
116
- if ensureNewDocumentNavigation:
117
- nav_fut = watcher.newDocumentNavigationFuture
118
- else:
119
- nav_fut = watcher.sameDocumentNavigationFuture
120
- error = await helpers.future_race(watcher.timeoutOrTerminationFuture, nav_fut)
121
- watcher.dispose()
122
- if error:
123
- raise error
124
- return watcher.navigationResponse()
125
-
126
- async def waitForFrameNavigation(
127
- self, frame: 'Frame', waitUntil: WaitTargets = None, timeout: float = None
128
- ) -> Optional[Response]:
129
- if not waitUntil:
130
- waitUntil = ['load']
131
- if not timeout:
132
- timeout = self._timeoutSettings.navigationTimeout
133
- watcher = LifecycleWatcher(self, frame=frame, timeout=timeout, waitUntil=waitUntil)
134
- error = await helpers.future_race(
135
- watcher.timeoutOrTerminationFuture,
136
- watcher.sameDocumentNavigationFuture,
137
- watcher.newDocumentNavigationFuture,
138
- )
139
- watcher.dispose()
140
- if error:
141
- raise error
142
- return watcher.navigationResponse()
143
-
144
- def _onLifecycleEvent(self, event: Dict) -> None:
145
- frame = self._frames.get(event['frameId'])
146
- if not frame:
147
- return
148
- frame._onLifecycleEvent(event['loaderId'], event['name'])
149
- self.emit(Events.FrameManager.LifecycleEvent, frame)
150
-
151
- def _onFrameStoppedLoading(self, frameId: str) -> None:
152
- frame = self._frames.get(frameId)
153
- if not frame:
154
- return
155
- frame._onLoadingStopped()
156
- self.emit(Events.FrameManager.LifecycleEvent, frame)
157
-
158
- def _handleFrameTree(self, frameTree: Dict) -> None:
159
- frame = frameTree['frame']
160
- if 'parentId' in frame:
161
- self._onFrameAttached(
162
- frame['id'], frame['parentId'],
163
- )
164
- self._onFrameNavigated(frame)
165
- for child in frameTree.get('childFrames', []):
166
- self._handleFrameTree(child)
167
-
168
- @property
169
- def page(self) -> 'Page':
170
- return self._page
171
-
172
- @property
173
- def mainFrame(self) -> Optional['Frame']:
174
- """Return main frame."""
175
- return self._mainFrame
176
-
177
- @property
178
- def frames(self) -> List['Frame']:
179
- """Return all frames."""
180
- return list(self._frames.values())
181
-
182
- def frame(self, frameId: Optional[str]) -> Optional['Frame']:
183
- """Return :class:`Frame` of ``frameId``."""
184
- return self._frames.get(frameId)
185
-
186
- def _onFrameAttached(self, frameId: str, parentFrameId: str) -> None:
187
- if frameId in self._frames:
188
- return
189
- parentFrame = self._frames.get(parentFrameId)
190
- frame = Frame(frameManager=self, client=self._client, parentFrame=parentFrame, frameId=frameId)
191
- self._frames[frameId] = frame
192
- self.emit(Events.FrameManager.FrameAttached, frame)
193
-
194
- def _onFrameNavigated(self, framePayload: dict) -> None:
195
- isMainFrame = not framePayload.get('parentId')
196
- if isMainFrame:
197
- frame = self._mainFrame
198
- else:
199
- frame = self._frames.get(framePayload.get('id', ''))
200
- if not (isMainFrame or frame):
201
- raise PageError('We either navigate top level or have old version of the navigated frame')
202
-
203
- # Detach all child frames first.
204
- if frame:
205
- for child in frame.childFrames:
206
- self._removeFramesRecursively(child)
207
-
208
- # Update or create main frame.
209
- _id = framePayload.get('id', '')
210
- if isMainFrame:
211
- if frame:
212
- # Update frame id to retain frame identity on cross-process navigation.
213
- self._frames.pop(frame._id, None)
214
- frame._id = _id
215
- else:
216
- # Initial main frame navigation.
217
- frame = Frame(frameManager=self, client=self._client, parentFrame=None, frameId=_id)
218
- self._frames[_id] = frame
219
- self._mainFrame = frame
220
-
221
- # Update frame payload.
222
- frame._navigated(framePayload) # type: ignore
223
- self.emit(Events.FrameManager.FrameNavigated, frame)
224
-
225
- async def _ensureIsolatedWorld(self, name: str) -> None:
226
- if name in self._isolatedWorlds:
227
- return
228
- self._isolatedWorlds.add(name)
229
- await self._client.send(
230
- 'Page.addScriptToEvaluateOnNewDocument',
231
- {'source': f'//# sourceURL={EVALUATION_SCRIPT_URL}', 'worldName': name,},
232
- )
233
- results = await asyncio.gather(
234
- *[
235
- self._client.send(
236
- 'Page.createIsolatedWorld', {'frameId': frame._id, 'grantUniversalAccess': True, 'worldName': name,}
237
- )
238
- for frame in self.frames
239
- ],
240
- return_exceptions=True,
241
- )
242
- for result in results:
243
- if isinstance(result, Exception):
244
- logger.exception(f'An exception occurred: {result}')
245
-
246
- def _onFrameNavigatedWithinDocument(self, frameId: str, url: str) -> None:
247
- frame = self._frames.get(frameId)
248
- if not frame:
249
- return
250
- frame._navigatedWithinDocument(url)
251
- self.emit(Events.FrameManager.FrameNavigatedWithinDocument, frame)
252
- self.emit(Events.FrameManager.FrameNavigated, frame)
253
-
254
- def _onFrameDetached(self, frameId: str) -> None:
255
- frame = self._frames.get(frameId)
256
- if frame:
257
- self._removeFramesRecursively(frame)
258
-
259
- def _onExecutionContextCreated(self, contextPayload: Dict) -> None:
260
- auxData = contextPayload.get('auxData')
261
- frameId = auxData.get('frameId')
262
- frame = self._frames.get(frameId)
263
- world = None
264
- if frame:
265
- if auxData and auxData['isDefault']:
266
- world = frame._mainWorld
267
- elif contextPayload.get('name') == UTILITY_WORLD_NAME and not frame._secondaryWorld._hasContext():
268
- # In case of multiple sessions to the same target, there's a race between
269
- # connections so we might end up creating multiple isolated worlds.
270
- # We can use either.
271
- world = frame._secondaryWorld
272
- if auxData and auxData.get('type') == 'isolated':
273
- self._isolatedWorlds.add(contextPayload['name'])
274
-
275
- context = ExecutionContext(self._client, contextPayload, world)
276
- if world:
277
- world._setContext(context)
278
- self._contextIdToContext[contextPayload['id']] = context
279
-
280
- def _onExecutionContextDestroyed(self, executionContextId: int) -> None:
281
- context = self._contextIdToContext.get(executionContextId)
282
- if not context:
283
- return
284
- del self._contextIdToContext[executionContextId]
285
- if context._world:
286
- context._world._setContext(None)
287
-
288
- def _onExecutionContextsCleared(self) -> None:
289
- for context in self._contextIdToContext.values():
290
- if context._world:
291
- context._world._setContext(None)
292
- self._contextIdToContext.clear()
293
-
294
- def executionContextById(self, contextId: int) -> 'ExecutionContext':
295
- """Get stored ``ExecutionContext`` by ``id``."""
296
- context = self._contextIdToContext.get(contextId)
297
- if not context:
298
- raise ElementHandleError(f'INTERNAL ERROR: missing context with id = {contextId}')
299
- return context
300
-
301
- def _removeFramesRecursively(self, frame: 'Frame') -> None:
302
- for child in frame.childFrames:
303
- self._removeFramesRecursively(child)
304
- frame._detach()
305
- self._frames.pop(frame._id, None)
306
- self.emit(Events.FrameManager.FrameDetached, frame)
@@ -1,245 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """Helper functions."""
5
-
6
- import asyncio
7
- import json
8
- import logging
9
- import math
10
- import re
11
- from asyncio.futures import Future
12
- from pathlib import Path
13
- from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
14
-
15
- from pyee import AsyncIOEventEmitter
16
- from biolib.pyppeteer.pyppeteer.connection import CDPSession
17
- from biolib.pyppeteer.pyppeteer.errors import ElementHandleError, TimeoutError
18
-
19
- if TYPE_CHECKING:
20
- from pyppeteer.models import Protocol
21
-
22
- logger = logging.getLogger(__name__)
23
-
24
-
25
- async def future_race(*fs) -> Union[None, Exception]:
26
- """
27
- Analogous to JS's Promise.race(). Returns the results of the first completed future
28
- :param fs: Future to be waited upon
29
- :return: result of first completed Future, whether that be an exception or result
30
-
31
- :raises Exception: any exception raised by asyncio.wait call. Any exception raised from the first future is returned
32
- """
33
- done, _ = await asyncio.wait(fs, return_when=asyncio.FIRST_COMPLETED,)
34
- try:
35
- return done.pop().result()
36
- except Exception as e:
37
- return e
38
-
39
-
40
- def evaluationString(fun: str, *args: Any) -> str:
41
- """Convert function and arguments to str."""
42
- _args = ', '.join([json.dumps('undefined' if arg is None else arg) for arg in args])
43
- expr = f'({fun})({_args})'
44
- return expr
45
-
46
-
47
- def getExceptionMessage(exceptionDetails: dict) -> str:
48
- """Get exception message from `exceptionDetails` object."""
49
- exception = exceptionDetails.get('exception')
50
- if exception:
51
- return exception.get('description') or exception.get('value')
52
- message = exceptionDetails.get('text', '')
53
- stackTrace = exceptionDetails.get('stackTrace', {})
54
- if stackTrace:
55
- for callframe in stackTrace.get('callFrames'):
56
- location = (
57
- str(callframe.get('url', ''))
58
- + ':'
59
- + str(callframe.get('lineNumber', ''))
60
- + ':'
61
- + str(callframe.get('columnNumber'))
62
- )
63
- functionName = callframe.get('functionName', '<anonymous>')
64
- message = message + f'\n at {functionName} ({location})'
65
- return message
66
-
67
-
68
- def addEventListener(emitter: AsyncIOEventEmitter, eventName: str, handler: Callable) -> Dict[str, Any]:
69
- """Add handler to the emitter and return emitter/handler."""
70
- emitter.on(eventName, handler)
71
- return {'emitter': emitter, 'eventName': eventName, 'handler': handler}
72
-
73
-
74
- def removeEventListeners(listeners: List[dict]) -> None:
75
- """Remove listeners from emitter."""
76
- for listener in listeners:
77
- emitter = listener['emitter']
78
- eventName = listener['eventName']
79
- handler = listener['handler']
80
- emitter.remove_listener(eventName, handler)
81
- listeners.clear()
82
-
83
-
84
- unserializableValueMap = {
85
- '-0': -0,
86
- 'NaN': None,
87
- None: None,
88
- 'Infinity': math.inf,
89
- '-Infinity': -math.inf,
90
- }
91
-
92
-
93
- def valueFromRemoteObject(remoteObject: 'Protocol.Runtime.RemoteObject') -> Any:
94
- """Serialize value of remote object."""
95
- if remoteObject.get('objectId'):
96
- raise ElementHandleError('Cannot extract value when objectId is given')
97
- value = remoteObject.get('unserializableValue')
98
- if value:
99
- try:
100
- value = unserializableValueMap[value]
101
- except KeyError:
102
- raise ElementHandleError(f'Unsupported unserializable value: {value}')
103
- return remoteObject.get('value')
104
-
105
-
106
- async def releaseObject(client: CDPSession, remoteObject: dict) -> Awaitable:
107
- """Release remote object."""
108
- objectId = remoteObject.get('objectId')
109
- if objectId:
110
- try:
111
- await client.send('Runtime.releaseObject', {'objectId': objectId})
112
- except Exception as e:
113
- # Exceptions might happen in case of a page been navigated or closed.
114
- # Swallow these since they are harmless and we don't leak anything in this case. # noqa
115
- logger.error(f'An exception occurred: {e}')
116
-
117
-
118
- def waitForEvent(
119
- emitter: AsyncIOEventEmitter,
120
- eventName: str, # noqa: C901
121
- predicate: Callable[[Any], bool],
122
- timeout: Optional[float],
123
- loop: asyncio.AbstractEventLoop,
124
- ) -> Awaitable:
125
- """Wait for an event emitted from the emitter."""
126
- future = loop.create_future()
127
-
128
- def resolveCallback(target: Any) -> None:
129
- future.set_result(target)
130
-
131
- def rejectCallback(exception: Exception) -> None:
132
- future.set_exception(exception)
133
-
134
- async def timeoutTimer() -> None:
135
- if timeout:
136
- await asyncio.sleep(timeout / 1000)
137
- else:
138
- await asyncio.get_event_loop().create_future()
139
- rejectCallback(TimeoutError('Timeout exceeded while waiting for event'))
140
-
141
- def _listener(target: Any) -> None:
142
- if not predicate(target):
143
- return
144
- cleanup()
145
- resolveCallback(target)
146
-
147
- listener = addEventListener(emitter, eventName, _listener)
148
- if timeout:
149
- eventTimeout = loop.create_task(timeoutTimer())
150
-
151
- def cleanup() -> None:
152
- removeEventListeners([listener])
153
- if timeout:
154
- eventTimeout.cancel()
155
-
156
- return future
157
-
158
-
159
- def get_positive_int(obj: dict, name: str) -> int:
160
- """Get and check the value of name in obj is positive integer."""
161
- value = obj[name]
162
- if not isinstance(value, int):
163
- raise TypeError(f'{name} must be integer: {type(value)}')
164
- elif value < 0:
165
- raise ValueError(f'{name} must be positive integer: {value}')
166
- return value
167
-
168
-
169
- _js_identifier_re = r'(?:[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC])(?:[\$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC])*'
170
-
171
-
172
- def is_js_func(func: str) -> bool:
173
- """
174
- Heuristically check if a str (when interpreted as js) is a function.
175
- A two step process is used.
176
-
177
- First, the str is stripped of whitespace with the builtin str.strip() method. Then the str is checked for whether
178
- or not it starts with or async, (either of which would indicate a function).
179
-
180
- If the str fails this check, all whitespace ( \t\r\n) is removed with str.translate. str.translate is almost an
181
- order of magnitude faster for large strs (tested with a 50kb str) than chained str.replace.
182
- Then, the larger regex is used. The regex uses a regex found here: https://stackoverflow.com/a/9337047
183
- and here: http://mothereff.in/js-variables to identify valid JS identifiers. (Unicode surrogates have been removed
184
- from the regex as they aren't supported in strs in Python)
185
- """
186
- if not isinstance(func, str):
187
- return False
188
- func = func.strip()
189
- if func.startswith('function ') or func.startswith('async '):
190
- return True
191
- # ~85% faster than using chained .replace()
192
- func = func.translate(str.maketrans(dict.fromkeys(' \t\r\n')))
193
- return bool(
194
- re.match(
195
- r"""
196
- # js functions can start with async, function, <name>, or any permutation of all three (in that order)
197
- # the only way a function may be named is if the function is declared with the function keyword
198
- (?:async)?(?P<named>function)?(?(named)(?:%(js)s)?)
199
- # js func args
200
- # matches opening ( if it exists
201
- (?P<argopen>\()?
202
- # matches one argument optionally. if function args are opening with (, we optionally match the rest argument
203
- (?P<args>(?(argopen)(?:\.\.\.)?)%(js)s,?)?
204
- # if the function opens with ( and the first arg was matched, we match more arguments,
205
- # otherwise, don't match any thing. if function args are opening with (, we optionally match the rest argument
206
- (?(argopen) (?(args)(?:(?(argopen)(?:\.\.\.)?)%(js)s,?)*) )
207
- # if we matched the opening (, match the closing )
208
- (?(argopen)\))
209
- # function bodies open with =>, {, both, or none. If the function is declared with a name, it must have a { to
210
- # open the body. The opposite is also true. If the function has a name, it will not open with =>.
211
- (?(named)(?P<fnopen>{)|(?:=>))
212
- # asserts that there are at least 3 characters behind us (the minimum to be able create a function declaration
213
- # and begin declaring the function body, ie f=>).
214
- (?<=.{3})
215
- # matches the function body
216
- .+
217
- # matches the closing } if the opening one was found
218
- (?(fnopen)})
219
- """
220
- % {'js': _js_identifier_re},
221
- func,
222
- re.VERBOSE,
223
- )
224
- )
225
-
226
-
227
- def safe_future_set_result(fut: Future, res: Any):
228
- if not fut.done():
229
- fut.set_result(res)
230
-
231
-
232
- async def readProtocolStream(client: CDPSession, handle: str, path: Union[Path, str]) -> str:
233
- # might be better to return as bytes
234
- eof = False
235
- buffs = []
236
- while not eof:
237
- response = await client.send('IO.read', {'handle': handle})
238
- buffs.append(response.get('data', ''))
239
- eof = response.get('eof', False)
240
- await client.send('IO.close', {'handle': handle})
241
-
242
- result = ''.join(bufs)
243
- if path:
244
- Path(path).write_text(result)
245
- return result