Skip to content

Commit

Permalink
[py] Add new APIs to control the browser via a bidi connection
Browse files Browse the repository at this point in the history
  • Loading branch information
AutomatedTester committed Oct 2, 2020
1 parent e2987e2 commit 587106b
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 246 deletions.
258 changes: 116 additions & 142 deletions py/BUILD.bazel

Large diffs are not rendered by default.

33 changes: 30 additions & 3 deletions py/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,35 @@
#
# CDP domain: {{}}{{}}
from __future__ import annotations
from ..util import event_class, T_JSON_DICT
from .util import event_class, T_JSON_DICT
from dataclasses import dataclass
import enum
import typing
'''.format(SHARED_HEADER)

current_version = ''

UTIL_PY = """
import typing
T_JSON_DICT = typing.Dict[str, typing.Any]
_event_parsers = dict()
def event_class(method):
''' A decorator that registers a class as an event class. '''
def decorate(cls):
_event_parsers[method] = cls
return cls
return decorate
def parse_json_event(json: T_JSON_DICT) -> typing.Any:
''' Parse a JSON dictionary into a CDP event. '''
return _event_parsers[json['method']].from_json(json['params'])
"""


def indent(s, n):
''' A shortcut for ``textwrap.indent`` that always uses spaces. '''
Expand Down Expand Up @@ -275,6 +296,7 @@ def generate_from_json(self, dict_):
if self.items.ref:
py_ref = ref_to_python(self.items.ref)
expr = f"[{py_ref}.from_json(i) for i in {dict_}['{self.name}']]"
expr
else:
cons = CdpPrimitiveType.get_constructor(self.items.type, 'i')
expr = f"[{cons} for i in {dict_}['{self.name}']]"
Expand Down Expand Up @@ -830,7 +852,7 @@ def generate_imports(self):
continue
if domain != self.domain:
dependencies.add(snake_case(domain))
code = '\n'.join(f'from .. import {d}' for d in sorted(dependencies))
code = '\n'.join(f'from . import {d}' for d in sorted(dependencies))

return code

Expand Down Expand Up @@ -928,7 +950,7 @@ def generate_init(init_path, domains):
init_file.write(INIT_HEADER)
for domain in domains:
init_file.write('from . import {}\n'.format(domain.module))
init_file.write('from .. import util\n\n')
init_file.write('from . import util\n\n')


def generate_docs(docs_path, domains):
Expand Down Expand Up @@ -957,6 +979,11 @@ def main(browser_protocol_path, js_protocol_path, output_path):
]
output_path.mkdir(parents=True)

# Generate util.py
util_path = output_path / "util.py"
with util_path.open('w') as util_file:
util_file.write(UTIL_PY)

# Remove generated code
for subpath in output_path.iterdir():
if subpath.is_file() and subpath.name not in ('py.typed', 'util.py'):
Expand Down
3 changes: 3 additions & 0 deletions py/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
[pytest]
console_output_style = progress
faulthandler_timeout = 60
log_cli = True
addopts = --capture=tee-sys
trio_mode = true
markers =
xfail_chrome: Tests expected to fail in Chrome
Expand Down
16 changes: 16 additions & 0 deletions py/selenium/webdriver/common/bidi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# The MIT License(MIT)
#
# http://www.apache.org/licenses/LICENSE-2.0
# Copyright(c) 2018 Hyperion Gray
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# This code comes from https://github.com/HyperionGray/trio-chrome-devtools-protocol/tree/master/trio_cdp

from trio_websocket import (
ConnectionClosed as WsConnectionClosed,
connect_websocket_url,
Expand All @@ -36,10 +44,14 @@
T = typing.TypeVar('T')
MAX_WS_MESSAGE_SIZE = 2**24

global devtools
devtools = None
version = None


def import_devtools(version):
def import_devtools(ver):
global devtools
global version
version = ver
devtools = importlib.import_module("selenium.webdriver.common.devtools.v{}".format(version))


Expand Down Expand Up @@ -259,6 +271,7 @@ def _handle_event(self, data):
Handle an event.
:param dict data: event as a JSON dictionary
'''
global devtools
event = devtools.util.parse_json_event(data)
logger.debug('Received event: %s', event)
to_remove = set()
Expand Down Expand Up @@ -303,6 +316,7 @@ async def dom_enable(self):
This keeps track of concurrent callers and only disables DOM events when
all callers have exited.
'''
global devtools
async with self._dom_enable_lock:
self._dom_enable_count += 1
if self._dom_enable_count == 1:
Expand All @@ -323,6 +337,7 @@ async def page_enable(self):
This keeps track of concurrent callers and only disables page events
when all callers have exited.
'''
global devtools
async with self._page_enable_lock:
self._page_enable_count += 1
if self._page_enable_count == 1:
Expand Down Expand Up @@ -384,6 +399,7 @@ async def connect_session(self, target_id) -> 'CdpSession':
'''
Returns a new :class:`CdpSession` connected to the specified target.
'''
global devtools
session_id = await self.execute(devtools.target.attach_to_target(
target_id, True))
session = CdpSession(self.ws, session_id, target_id)
Expand All @@ -395,6 +411,7 @@ async def _reader_task(self):
Runs in the background and handles incoming messages: dispatching
responses to commands and events to listeners.
'''
global devtools
while True:
try:
message = await self.ws.get_message()
Expand Down
25 changes: 25 additions & 0 deletions py/selenium/webdriver/common/bidi/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from enum import Enum


class Console(Enum):

ALL = "all"
LOG = "log"
ERROR = "error"
18 changes: 0 additions & 18 deletions py/selenium/webdriver/common/devtools/util.py

This file was deleted.

18 changes: 0 additions & 18 deletions py/selenium/webdriver/common/devtools/v84/util.py

This file was deleted.

18 changes: 0 additions & 18 deletions py/selenium/webdriver/common/devtools/v85/util.py

This file was deleted.

18 changes: 0 additions & 18 deletions py/selenium/webdriver/common/devtools/v86/util.py

This file was deleted.

Loading

0 comments on commit 587106b

Please sign in to comment.