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.
- biolib/__init__.py +357 -11
- biolib/_data_record/data_record.py +380 -0
- biolib/_index/__init__.py +0 -0
- biolib/_index/index.py +55 -0
- biolib/_index/query_result.py +103 -0
- biolib/_internal/__init__.py +0 -0
- biolib/_internal/add_copilot_prompts.py +58 -0
- biolib/_internal/add_gui_files.py +81 -0
- biolib/_internal/data_record/__init__.py +1 -0
- biolib/_internal/data_record/data_record.py +85 -0
- biolib/_internal/data_record/push_data.py +116 -0
- biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
- biolib/_internal/errors.py +5 -0
- biolib/_internal/file_utils.py +125 -0
- biolib/_internal/fuse_mount/__init__.py +1 -0
- biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
- biolib/_internal/http_client.py +159 -0
- biolib/_internal/lfs/__init__.py +1 -0
- biolib/_internal/lfs/cache.py +51 -0
- biolib/_internal/libs/__init__.py +1 -0
- biolib/_internal/libs/fusepy/__init__.py +1257 -0
- biolib/_internal/push_application.py +488 -0
- biolib/_internal/runtime.py +22 -0
- biolib/_internal/string_utils.py +13 -0
- biolib/_internal/templates/__init__.py +1 -0
- biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
- biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
- biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
- biolib/_internal/templates/gitignore_template/.gitignore +10 -0
- biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
- biolib/_internal/templates/gui_template/App.tsx +53 -0
- biolib/_internal/templates/gui_template/Dockerfile +27 -0
- biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
- biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
- biolib/_internal/templates/gui_template/index.css +5 -0
- biolib/_internal/templates/gui_template/index.html +13 -0
- biolib/_internal/templates/gui_template/index.tsx +10 -0
- biolib/_internal/templates/gui_template/package.json +27 -0
- biolib/_internal/templates/gui_template/tsconfig.json +24 -0
- biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
- biolib/_internal/templates/gui_template/vite.config.mts +10 -0
- biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
- biolib/_internal/templates/init_template/Dockerfile +14 -0
- biolib/_internal/templates/init_template/requirements.txt +1 -0
- biolib/_internal/templates/init_template/run.py +12 -0
- biolib/_internal/templates/init_template/run.sh +4 -0
- biolib/_internal/templates/templates.py +25 -0
- biolib/_internal/tree_utils.py +106 -0
- biolib/_internal/utils/__init__.py +65 -0
- biolib/_internal/utils/auth.py +46 -0
- biolib/_internal/utils/job_url.py +33 -0
- biolib/_internal/utils/multinode.py +263 -0
- biolib/_runtime/runtime.py +157 -0
- biolib/_session/session.py +44 -0
- biolib/_shared/__init__.py +0 -0
- biolib/_shared/types/__init__.py +74 -0
- biolib/_shared/types/account.py +12 -0
- biolib/_shared/types/account_member.py +8 -0
- biolib/_shared/types/app.py +9 -0
- biolib/_shared/types/data_record.py +40 -0
- biolib/_shared/types/experiment.py +32 -0
- biolib/_shared/types/file_node.py +17 -0
- biolib/_shared/types/push.py +6 -0
- biolib/_shared/types/resource.py +37 -0
- biolib/_shared/types/resource_deploy_key.py +11 -0
- biolib/_shared/types/resource_permission.py +14 -0
- biolib/_shared/types/resource_version.py +19 -0
- biolib/_shared/types/result.py +14 -0
- biolib/_shared/types/typing.py +10 -0
- biolib/_shared/types/user.py +19 -0
- biolib/_shared/utils/__init__.py +7 -0
- biolib/_shared/utils/resource_uri.py +75 -0
- biolib/api/__init__.py +6 -0
- biolib/api/client.py +168 -0
- biolib/app/app.py +252 -49
- biolib/app/search_apps.py +45 -0
- biolib/biolib_api_client/api_client.py +126 -31
- biolib/biolib_api_client/app_types.py +24 -4
- biolib/biolib_api_client/auth.py +31 -8
- biolib/biolib_api_client/biolib_app_api.py +147 -52
- biolib/biolib_api_client/biolib_job_api.py +161 -141
- biolib/biolib_api_client/job_types.py +21 -5
- biolib/biolib_api_client/lfs_types.py +7 -23
- biolib/biolib_api_client/user_state.py +56 -0
- biolib/biolib_binary_format/__init__.py +1 -4
- biolib/biolib_binary_format/file_in_container.py +105 -0
- biolib/biolib_binary_format/module_input.py +24 -7
- biolib/biolib_binary_format/module_output_v2.py +149 -0
- biolib/biolib_binary_format/remote_endpoints.py +34 -0
- biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
- biolib/biolib_binary_format/saved_job.py +3 -2
- biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
- biolib/biolib_binary_format/system_status_update.py +3 -2
- biolib/biolib_binary_format/utils.py +175 -0
- biolib/biolib_docker_client/__init__.py +11 -2
- biolib/biolib_errors.py +36 -0
- biolib/biolib_logging.py +27 -10
- biolib/cli/__init__.py +38 -0
- biolib/cli/auth.py +46 -0
- biolib/cli/data_record.py +164 -0
- biolib/cli/index.py +32 -0
- biolib/cli/init.py +421 -0
- biolib/cli/lfs.py +101 -0
- biolib/cli/push.py +50 -0
- biolib/cli/run.py +63 -0
- biolib/cli/runtime.py +14 -0
- biolib/cli/sdk.py +16 -0
- biolib/cli/start.py +56 -0
- biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
- biolib/compute_node/job_worker/cache_state.py +66 -88
- biolib/compute_node/job_worker/cache_types.py +1 -6
- biolib/compute_node/job_worker/docker_image_cache.py +112 -37
- biolib/compute_node/job_worker/executors/__init__.py +0 -3
- biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
- biolib/compute_node/job_worker/executors/docker_types.py +9 -1
- biolib/compute_node/job_worker/executors/types.py +19 -9
- biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
- biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
- biolib/compute_node/job_worker/job_storage.py +108 -0
- biolib/compute_node/job_worker/job_worker.py +397 -212
- biolib/compute_node/job_worker/large_file_system.py +87 -38
- biolib/compute_node/job_worker/network_alloc.py +99 -0
- biolib/compute_node/job_worker/network_buffer.py +240 -0
- biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
- biolib/compute_node/job_worker/utils.py +9 -24
- biolib/compute_node/remote_host_proxy.py +400 -98
- biolib/compute_node/utils.py +31 -9
- biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
- biolib/compute_node/webserver/proxy_utils.py +28 -0
- biolib/compute_node/webserver/webserver.py +130 -44
- biolib/compute_node/webserver/webserver_types.py +2 -6
- biolib/compute_node/webserver/webserver_utils.py +77 -12
- biolib/compute_node/webserver/worker_thread.py +183 -42
- biolib/experiments/__init__.py +0 -0
- biolib/experiments/experiment.py +356 -0
- biolib/jobs/__init__.py +1 -0
- biolib/jobs/job.py +741 -0
- biolib/jobs/job_result.py +185 -0
- biolib/jobs/types.py +50 -0
- biolib/py.typed +0 -0
- biolib/runtime/__init__.py +14 -0
- biolib/sdk/__init__.py +91 -0
- biolib/tables.py +34 -0
- biolib/typing_utils.py +2 -7
- biolib/user/__init__.py +1 -0
- biolib/user/sign_in.py +54 -0
- biolib/utils/__init__.py +162 -0
- biolib/utils/cache_state.py +94 -0
- biolib/utils/multipart_uploader.py +194 -0
- biolib/utils/seq_util.py +150 -0
- biolib/utils/zip/remote_zip.py +640 -0
- pybiolib-1.2.1890.dist-info/METADATA +41 -0
- pybiolib-1.2.1890.dist-info/RECORD +177 -0
- {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
- pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
- README.md +0 -17
- biolib/app/app_result.py +0 -68
- biolib/app/utils.py +0 -62
- biolib/biolib-js/0-biolib.worker.js +0 -1
- biolib/biolib-js/1-biolib.worker.js +0 -1
- biolib/biolib-js/2-biolib.worker.js +0 -1
- biolib/biolib-js/3-biolib.worker.js +0 -1
- biolib/biolib-js/4-biolib.worker.js +0 -1
- biolib/biolib-js/5-biolib.worker.js +0 -1
- biolib/biolib-js/6-biolib.worker.js +0 -1
- biolib/biolib-js/index.html +0 -10
- biolib/biolib-js/main-biolib.js +0 -1
- biolib/biolib_api_client/biolib_account_api.py +0 -21
- biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
- biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
- biolib/biolib_binary_format/module_output.py +0 -58
- biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
- biolib/biolib_push.py +0 -114
- biolib/cli.py +0 -203
- biolib/cli_utils.py +0 -273
- biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
- biolib/compute_node/enclave/__init__.py +0 -2
- biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
- biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
- biolib/compute_node/job_worker/executors/base_executor.py +0 -18
- biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
- biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
- biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
- biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
- biolib/lfs.py +0 -196
- biolib/pyppeteer/.circleci/config.yml +0 -100
- biolib/pyppeteer/.coveragerc +0 -3
- biolib/pyppeteer/.gitignore +0 -89
- biolib/pyppeteer/.pre-commit-config.yaml +0 -28
- biolib/pyppeteer/CHANGES.md +0 -253
- biolib/pyppeteer/CONTRIBUTING.md +0 -26
- biolib/pyppeteer/LICENSE +0 -12
- biolib/pyppeteer/README.md +0 -137
- biolib/pyppeteer/docs/Makefile +0 -177
- biolib/pyppeteer/docs/_static/custom.css +0 -28
- biolib/pyppeteer/docs/_templates/layout.html +0 -10
- biolib/pyppeteer/docs/changes.md +0 -1
- biolib/pyppeteer/docs/conf.py +0 -299
- biolib/pyppeteer/docs/index.md +0 -21
- biolib/pyppeteer/docs/make.bat +0 -242
- biolib/pyppeteer/docs/reference.md +0 -211
- biolib/pyppeteer/docs/server.py +0 -60
- biolib/pyppeteer/poetry.lock +0 -1699
- biolib/pyppeteer/pyppeteer/__init__.py +0 -135
- biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
- biolib/pyppeteer/pyppeteer/browser.py +0 -401
- biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
- biolib/pyppeteer/pyppeteer/command.py +0 -22
- biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
- biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
- biolib/pyppeteer/pyppeteer/coverage.py +0 -346
- biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
- biolib/pyppeteer/pyppeteer/dialog.py +0 -79
- biolib/pyppeteer/pyppeteer/domworld.py +0 -597
- biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
- biolib/pyppeteer/pyppeteer/errors.py +0 -48
- biolib/pyppeteer/pyppeteer/events.py +0 -63
- biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
- biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
- biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
- biolib/pyppeteer/pyppeteer/helpers.py +0 -245
- biolib/pyppeteer/pyppeteer/input.py +0 -371
- biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
- biolib/pyppeteer/pyppeteer/launcher.py +0 -683
- biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
- biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
- biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
- biolib/pyppeteer/pyppeteer/multimap.py +0 -82
- biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
- biolib/pyppeteer/pyppeteer/options.py +0 -8
- biolib/pyppeteer/pyppeteer/page.py +0 -1728
- biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
- biolib/pyppeteer/pyppeteer/target.py +0 -147
- biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
- biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
- biolib/pyppeteer/pyppeteer/tracing.py +0 -93
- biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
- biolib/pyppeteer/pyppeteer/util.py +0 -18
- biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
- biolib/pyppeteer/pyppeteer/worker.py +0 -101
- biolib/pyppeteer/pyproject.toml +0 -97
- biolib/pyppeteer/spell.txt +0 -137
- biolib/pyppeteer/tox.ini +0 -72
- biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
- biolib/start_cli.py +0 -7
- biolib/utils.py +0 -47
- biolib/validators/validate_app_version.py +0 -183
- biolib/validators/validate_argument.py +0 -134
- biolib/validators/validate_module.py +0 -323
- biolib/validators/validate_zip_file.py +0 -40
- biolib/validators/validator_utils.py +0 -103
- pybiolib-0.2.951.dist-info/LICENSE +0 -21
- pybiolib-0.2.951.dist-info/METADATA +0 -61
- pybiolib-0.2.951.dist-info/RECORD +0 -153
- pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
- /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
|