From 309534592ae7a9fe38653149955fc35a9cdbc530 Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Mon, 28 Oct 2019 13:27:06 -0400 Subject: [PATCH 01/81] Modify header so that warning is a comment, not a doc The docstring appears in generated documentation and autocomplete, and the warning isn't helpful in those contexts. --- generator/generate.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/generator/generate.py b/generator/generate.py index 5bf5eb5..428e05d 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -16,22 +16,19 @@ logging.basicConfig(level=log_level) logger = logging.getLogger('generate') -SHARED_HEADER = '''DO NOT EDIT THIS FILE +SHARED_HEADER = '''# DO NOT EDIT THIS FILE! +# +# This file is generated from the CDP specification. If you need to make +# changes, edit the generator and regenerate all of the modules.''' -This file is generated from the CDP specification. If you need to make changes, -edit the generator and regenerate all of the modules.''' - -INIT_HEADER = '''\'\'\' -{} -\'\'\' +INIT_HEADER = '''{} '''.format(SHARED_HEADER) -MODULE_HEADER = '''\'\'\' -{} +MODULE_HEADER = '''{} -Domain: {{}} -Experimental: {{}} +\'\'\' +CDP {{}} Domain{{}} \'\'\' from cdp.util import event_class, T_JSON_DICT @@ -730,7 +727,8 @@ def from_json(cls, domain: dict): def generate_code(self) -> str: ''' Generate the Python module code for a given CDP domain. ''' - code = MODULE_HEADER.format(self.domain, self.experimental) + exp = ' (experimental)' if self.experimental else '' + code = MODULE_HEADER.format(self.domain, exp) import_code = self.generate_imports() if import_code: code += import_code From 48813466ab04d845083d20362779ad98cdf78b29 Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 11:08:14 -0500 Subject: [PATCH 02/81] Update annotations (fixes #11) Annotations are no longer quoted. Instead we use from __future__ import annotations. Tests are updated to match and all tests pass. --- generator/generate.py | 28 ++++++++++++++-------------- generator/test_generate.py | 38 +++++++++++++++++++------------------- mypy.ini | 2 ++ 3 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 mypy.ini diff --git a/generator/generate.py b/generator/generate.py index 428e05d..dcb96bb 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -31,6 +31,7 @@ CDP {{}} Domain{{}} \'\'\' +from __future__ import annotations from cdp.util import event_class, T_JSON_DICT from dataclasses import dataclass import enum @@ -153,14 +154,14 @@ def py_annotation(self) -> str: if self.items: if self.items.ref: py_ref = ref_to_python(self.items.ref) - ann = "typing.List['{}']".format(py_ref) + ann = "typing.List[{}]".format(py_ref) else: ann = 'typing.List[{}]'.format( CdpPrimitiveType.get_annotation(self.items.type)) else: if self.ref: py_ref = ref_to_python(self.ref) - ann = f"'{py_ref}'" + ann = py_ref else: ann = CdpPrimitiveType.get_annotation( typing.cast(str, self.type)) @@ -216,7 +217,7 @@ def generate_to_json(self, dict_: str, use_self: bool=True) -> str: def generate_from_json(self, dict_) -> str: ''' Generate the code that creates an instance from a JSON dict named - ``json``. ''' + ``dict_``. ''' if self.items: if self.items.ref: py_ref = ref_to_python(self.items.ref) @@ -273,10 +274,9 @@ def generate_primitive_code(self) -> str: if self.items: if self.items.ref: nested_type = ref_to_python(self.items.ref) - py_type = f"typing.List['{nested_type}']" else: nested_type = CdpPrimitiveType.get_annotation(self.items.type) - py_type = f'typing.List[{nested_type}]' + py_type = f'typing.List[{nested_type}]' superclass = 'list' else: # A primitive type cannot have a ref, so there is no branch here. @@ -295,7 +295,7 @@ def to_json(self) -> {py_type}: def_from_json = dedent(f'''\ @classmethod - def from_json(cls, json: {py_type}) -> '{self.id}': + def from_json(cls, json: {py_type}) -> {self.id}: return cls(json)''') code += '\n\n' + indent(def_from_json, 4) @@ -321,7 +321,7 @@ def to_json(self) -> str: def_from_json = dedent(f'''\ @classmethod - def from_json(cls, json: str) -> '{self.id}': + def from_json(cls, json: str) -> {self.id}: return cls(json)''') code = f'class {self.id}(enum.Enum):\n' @@ -376,7 +376,7 @@ def to_json(self) -> T_JSON_DICT: # as above for readability. def_from_json = dedent(f'''\ @classmethod - def from_json(cls, json: T_JSON_DICT) -> '{self.id}': + def from_json(cls, json: T_JSON_DICT) -> {self.id}: return cls( ''') from_jsons = list() @@ -418,13 +418,13 @@ def generate_code(self) -> str: if self.items: if self.items.ref: nested_type = ref_to_python(self.items.ref) - py_type = f"typing.List['{nested_type}']" + py_type = f"typing.List[{nested_type}]" else: nested_type = CdpPrimitiveType.get_annotation(self.items.type) py_type = f'typing.List[{nested_type}]' else: if self.ref: - py_type = "'{}'".format(ref_to_python(self.ref)) + py_type = "{}".format(ref_to_python(self.ref)) else: py_type = CdpPrimitiveType.get_annotation( typing.cast(str, self.type)) @@ -469,14 +469,14 @@ def py_annotation(self): if self.items: if self.items.ref: py_ref = ref_to_python(self.items.ref) - ann = f"typing.List['{py_ref}']" + ann = f"typing.List[{py_ref}]" else: py_type = CdpPrimitiveType.get_annotation(self.items.type) ann = f'typing.List[{py_type}]' else: if self.ref: py_ref = ref_to_python(self.ref) - ann = f"'{py_ref}'" + ann = f"{py_ref}" else: ann = CdpPrimitiveType.get_annotation(self.type) if self.optional: @@ -660,7 +660,7 @@ class {self.py_name}:''') if self.deprecated: code = f'@deprecated(version="{current_version}")\n' + code - + code += '\n' if self.description: code += indent(docstring(self.description), 4) @@ -670,7 +670,7 @@ class {self.py_name}:''') code += '\n\n' def_from_json = dedent(f'''\ @classmethod - def from_json(cls, json: T_JSON_DICT) -> '{self.py_name}': + def from_json(cls, json: T_JSON_DICT) -> {self.py_name}: return cls( ''') code += indent(def_from_json, 4) diff --git a/generator/test_generate.py b/generator/test_generate.py index 0f1a83c..8abf51a 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -54,7 +54,7 @@ def to_json(self) -> str: return self @classmethod - def from_json(cls, json: str) -> 'AXNodeId': + def from_json(cls, json: str) -> AXNodeId: return cls(json) def __repr__(self): @@ -81,11 +81,11 @@ class ArrayOfStrings(list): ''' Index of the string in the strings table. ''' - def to_json(self) -> typing.List['StringIndex']: + def to_json(self) -> typing.List[StringIndex]: return self @classmethod - def from_json(cls, json: typing.List['StringIndex']) -> 'ArrayOfStrings': + def from_json(cls, json: typing.List[StringIndex]) -> ArrayOfStrings: return cls(json) def __repr__(self): @@ -128,7 +128,7 @@ def to_json(self) -> str: return self.value @classmethod - def from_json(cls, json: str) -> 'AXValueSourceType': + def from_json(cls, json: str) -> AXValueSourceType: return cls(json)""") type = CdpType.from_json(json_type) @@ -182,16 +182,16 @@ class AXValue: A single computed AX property. ''' #: The type of this value. - type: 'AXValueType' + type: AXValueType #: The computed value of this property. value: typing.Optional[typing.Any] = None #: One or more related nodes, if applicable. - related_nodes: typing.Optional[typing.List['AXRelatedNode']] = None + related_nodes: typing.Optional[typing.List[AXRelatedNode]] = None #: The sources which contributed to the computation of this property. - sources: typing.Optional[typing.List['AXValueSource']] = None + sources: typing.Optional[typing.List[AXValueSource]] = None def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() @@ -205,7 +205,7 @@ def to_json(self) -> T_JSON_DICT: return json @classmethod - def from_json(cls, json: T_JSON_DICT) -> 'AXValue': + def from_json(cls, json: T_JSON_DICT) -> AXValue: return cls( type=AXValueType.from_json(json['type']), value=json['value'] if 'value' in json else None, @@ -264,11 +264,11 @@ def test_cdp_command(): } expected = dedent("""\ def get_partial_ax_tree( - node_id: typing.Optional['dom.NodeId'] = None, - backend_node_id: typing.Optional['dom.BackendNodeId'] = None, - object_id: typing.Optional['runtime.RemoteObjectId'] = None, + node_id: typing.Optional[dom.NodeId] = None, + backend_node_id: typing.Optional[dom.BackendNodeId] = None, + object_id: typing.Optional[runtime.RemoteObjectId] = None, fetch_relatives: typing.Optional[bool] = None - ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List['AXNode']]: + ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[AXNode]]: ''' Fetches the accessibility node and partial accessibility tree for this DOM node, if it exists. @@ -467,7 +467,7 @@ def test_cdp_command_ref_parameter(): expected = dedent("""\ def resolve_animation( animation_id: str - ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,'runtime.RemoteObject']: + ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,runtime.RemoteObject]: ''' Gets the remote object of the Animation. @@ -544,7 +544,7 @@ def test_cdp_command_multiple_return(): } expected = dedent("""\ def get_encoded_response( - request_id: 'network.RequestId', + request_id: network.RequestId, encoding: str, quality: typing.Optional[float] = None, size_only: typing.Optional[bool] = None @@ -615,8 +615,8 @@ def test_cdp_command_array_of_ref_parameter(): expected = dedent("""\ def grant_permissions( origin: str, - permissions: typing.List['PermissionType'], - browser_context_id: typing.Optional['target.BrowserContextID'] = None + permissions: typing.List[PermissionType], + browser_context_id: typing.Optional[target.BrowserContextID] = None ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]: ''' Grant specific permissions to the given origin and reject all others. @@ -666,10 +666,10 @@ class RecordingStateChanged: Called when the recording state for the service has been updated. ''' is_recording: bool - service: 'ServiceName' + service: ServiceName @classmethod - def from_json(cls, json: T_JSON_DICT) -> 'RecordingStateChanged': + def from_json(cls, json: T_JSON_DICT) -> RecordingStateChanged: return cls( is_recording=bool(json['isRecording']), service=ServiceName.from_json(json['service']) @@ -730,7 +730,7 @@ class WindowOpen: user_gesture: bool @classmethod - def from_json(cls, json: T_JSON_DICT) -> 'WindowOpen': + def from_json(cls, json: T_JSON_DICT) -> WindowOpen: return cls( url=str(json['url']), window_name=str(json['windowName']), diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..f9e0f72 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +disallow_any_decorated=False From 3c8461d59e9d4ce92f533908cd03844efb0690ac Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 12:08:01 -0500 Subject: [PATCH 03/81] Don't generate symbols that shadow built-ins (fixes #13) When generating Python names, we now check to see if that name exists in the `builtins` module. If it does, then we append an underscore. Tests are updated and I *think* they are passing but I also have some test failures from a merged branch. --- generator/generate.py | 37 +++++++++++++++----- generator/test_generate.py | 70 +++++++++++++++++--------------------- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/generator/generate.py b/generator/generate.py index d722c23..56c9886 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -1,3 +1,4 @@ +import builtins from dataclasses import dataclass from enum import Enum import itertools @@ -41,10 +42,12 @@ current_version = '' + def indent(s: str, n: int): ''' A shortcut for ``textwrap.indent`` that always uses spaces. ''' return tw_indent(s, n * ' ') + def clear_dirs(package_path: Path): ''' Remove generated code. ''' def rmdir(path): @@ -82,6 +85,24 @@ def docstring(description: typing.Optional[str]) -> str: return dedent("'''\n{}\n'''").format(description) +def is_builtin(name: str) -> bool: + ''' Return True if ``name`` would shadow a builtin. ''' + try: + getattr(builtins, name) + return True + except AttributeError: + return False + + +def snake_case(name: str) -> str: + ''' Convert a camel case name to snake case. If the name would shadow a + Python builtin, then append an underscore. ''' + name = inflection.underscore(name) + if is_builtin(name): + name += '_' + return name + + def ref_to_python(ref: str) -> str: ''' Convert a CDP ``$ref`` to the name of a Python type. @@ -90,7 +111,7 @@ def ref_to_python(ref: str) -> str: ''' if '.' in ref: domain, subtype = ref.split('.') - ref = '{}.{}'.format(inflection.underscore(domain), subtype) + ref = '{}.{}'.format(snake_case(domain), subtype) return f"{ref}" @@ -148,7 +169,7 @@ class CdpProperty: @property def py_name(self) -> str: ''' Get this property's Python name. ''' - return inflection.underscore(self.name) + return snake_case(self.name) @property def py_annotation(self) -> str: @@ -333,8 +354,8 @@ def from_json(cls, json: str) -> {self.id}: if doc: code += indent(doc, 4) + '\n' for enum_member in self.enum: - snake_case = inflection.underscore(enum_member).upper() - enum_code = f'{snake_case} = "{enum_member}"\n' + snake_name = snake_case(enum_member).upper() + enum_code = f'{snake_name} = "{enum_member}"\n' code += indent(enum_code, 4) code += '\n' + indent(def_to_json, 4) code += '\n\n' + indent(def_from_json, 4) @@ -526,7 +547,7 @@ class CdpCommand: @property def py_name(self): ''' Get a Python name for this command. ''' - return inflection.underscore(self.name) + return snake_case(self.name) @classmethod def from_json(cls, command, domain) -> 'CdpCommand': @@ -686,7 +707,7 @@ class {self.py_name}:''') if self.description: desc += self.description - + code += indent(docstring(desc), 4) code += '\n' code += indent( @@ -729,7 +750,7 @@ class CdpDomain: @property def module(self): ''' The name of the Python module for this CDP domain. ''' - return inflection.underscore(self.domain) + return snake_case(self.domain) @classmethod def from_json(cls, domain: dict): @@ -797,7 +818,7 @@ def generate_imports(self): except ValueError: continue if domain != self.domain: - dependencies.add(inflection.underscore(domain)) + dependencies.add(snake_case(domain)) code = '\n'.join(f'from . import {d}' for d in sorted(dependencies)) if needs_deprecation: diff --git a/generator/test_generate.py b/generator/test_generate.py index 8abf51a..8a49a96 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -34,8 +34,6 @@ def test_docstring(): - from 'activedescendant' to 'owns' - relationships between elements other than parent/child/sibling. '''""") actual = docstring(description) - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -62,8 +60,6 @@ def __repr__(self): type = CdpType.from_json(json_type) actual = type.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -93,8 +89,6 @@ def __repr__(self): type = CdpType.from_json(json_type) actual = type.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -133,8 +127,6 @@ def from_json(cls, json: str) -> AXValueSourceType: type = CdpType.from_json(json_type) actual = type.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -182,7 +174,7 @@ class AXValue: A single computed AX property. ''' #: The type of this value. - type: AXValueType + type_: AXValueType #: The computed value of this property. value: typing.Optional[typing.Any] = None @@ -195,7 +187,7 @@ class AXValue: def to_json(self) -> T_JSON_DICT: json: T_JSON_DICT = dict() - json['type'] = self.type.to_json() + json['type'] = self.type_.to_json() if self.value is not None: json['value'] = self.value if self.related_nodes is not None: @@ -207,7 +199,7 @@ def to_json(self) -> T_JSON_DICT: @classmethod def from_json(cls, json: T_JSON_DICT) -> AXValue: return cls( - type=AXValueType.from_json(json['type']), + type_=AXValueType.from_json(json['type']), value=json['value'] if 'value' in json else None, related_nodes=[AXRelatedNode.from_json(i) for i in json['relatedNodes']] if 'relatedNodes' in json else None, sources=[AXValueSource.from_json(i) for i in json['sources']] if 'sources' in json else None, @@ -215,8 +207,6 @@ def from_json(cls, json: T_JSON_DICT) -> AXValue: type = CdpType.from_json(json_type) actual = type.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -297,8 +287,6 @@ def get_partial_ax_tree( cmd = CdpCommand.from_json(json_cmd, 'Accessibility') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -319,8 +307,6 @@ def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]: cmd = CdpCommand.from_json(json_cmd, 'Accessibility') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -345,16 +331,16 @@ def test_cdp_command_return_primitive(): } expected = dedent("""\ def get_current_time( - id: str + id_: str ) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,float]: ''' Returns the current time of the an animation. - :param id: Id of animation. + :param id_: Id of animation. :returns: Current time of the page. ''' params: T_JSON_DICT = dict() - params['id'] = id + params['id'] = id_ cmd_dict: T_JSON_DICT = { 'method': 'Animation.getCurrentTime', 'params': params, @@ -364,8 +350,6 @@ def get_current_time( cmd = CdpCommand.from_json(json_cmd, 'Animation') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -401,8 +385,6 @@ def get_browser_command_line() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typin cmd = CdpCommand.from_json(json_cmd, 'Browser') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -440,8 +422,6 @@ def release_animations( cmd = CdpCommand.from_json(json_cmd, 'Animation') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -485,8 +465,6 @@ def resolve_animation( cmd = CdpCommand.from_json(json_cmd, 'Animation') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -582,8 +560,6 @@ def get_encoded_response( cmd = CdpCommand.from_json(json_cmd, 'Audits') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -638,8 +614,6 @@ def grant_permissions( cmd = CdpCommand.from_json(json_cmd, 'Browser') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -677,8 +651,6 @@ def from_json(cls, json: T_JSON_DICT) -> RecordingStateChanged: cmd = CdpEvent.from_json(json_event, 'BackgroundService') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -740,8 +712,6 @@ def from_json(cls, json: T_JSON_DICT) -> WindowOpen: cmd = CdpEvent.from_json(json_event, 'Page') actual = cmd.generate_code() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual @@ -846,6 +816,30 @@ def test_cdp_domain_imports(): domain = CdpDomain.from_json(json_domain) actual = domain.generate_imports() - print('EXPECTED:', expected) - print('ACTUAL:', actual) assert expected == actual + + +def test_domain_shadows_builtin(): + ''' If a domain name shadows a Python builtin, it should have an underscore + appended to the module name. ''' + input_domain = { + "domain": "Input", + "types": [], + "commands": [], + "events": [], + } + domain = CdpDomain.from_json(input_domain) + assert domain.module == 'input_' + + +def test_domain_shadows_builtin(): + ''' If a domain name shadows a Python builtin, it should have an underscore + appended to the module name. ''' + input_domain = { + "domain": "Input", + "types": [], + "commands": [], + "events": [], + } + domain = CdpDomain.from_json(input_domain) + assert domain.module == 'input_' From 6ae50016520bc8382cbe6e27987fb94eeb2e223f Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 13:00:09 -0500 Subject: [PATCH 04/81] Create a Sphinx document for each module (#10) Each module now has its own Sphinx doc generated in docs/api/. The file api.py contains a toctree directive and a glob so that all modules are automatically listed in the sidebar. This makes it easier to jump to a specific module, and also the API docs load *much faster*. --- Makefile | 7 +- docs/api.rst | 138 ++----------------------------------- docs/develop.rst | 7 +- generator/generate.py | 63 +++++++++++------ generator/test_generate.py | 84 +++++++++++++++++++--- 5 files changed, 133 insertions(+), 166 deletions(-) diff --git a/Makefile b/Makefile index 8d7b78a..ce2693f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,9 @@ -all: mypy-generate test-generate generate test-import mypy-cdp test-cdp +.PHONY: docs + +default: mypy-generate test-generate generate test-import mypy-cdp test-cdp + +docs: + $(MAKE) -C docs html generate: python generator/generate.py diff --git a/docs/api.rst b/docs/api.rst index fbfe6fd..32b57a9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,134 +1,10 @@ -API Reference -============= +API Modules +=========== -.. automodule:: cdp.accessibility - :members: +Each module in the API corresponds to a "domain" in CDP and may contain types, +commands, and events. -.. automodule:: cdp.animation - :members: +.. toctree:: + :glob: -.. automodule:: cdp.application_cache - :members: - -.. automodule:: cdp.audits - :members: - -.. automodule:: cdp.background_service - :members: - -.. automodule:: cdp.browser - :members: - -.. automodule:: cdp.cache_storage - :members: - -.. automodule:: cdp.cast - :members: - -.. automodule:: cdp.console - :members: - -.. automodule:: cdp.css - :members: - -.. automodule:: cdp.database - :members: - -.. automodule:: cdp.debugger - :members: - -.. automodule:: cdp.device_orientation - :members: - -.. automodule:: cdp.dom - :members: - -.. automodule:: cdp.dom_debugger - :members: - -.. automodule:: cdp.dom_snapshot - :members: - -.. automodule:: cdp.dom_storage - :members: - -.. automodule:: cdp.emulation - :members: - -.. automodule:: cdp.fetch - :members: - -.. automodule:: cdp.headless_experimental - :members: - -.. automodule:: cdp.heap_profiler - :members: - -.. automodule:: cdp.indexed_db - :members: - -.. automodule:: cdp.input - :members: - -.. automodule:: cdp.inspector - :members: - -.. automodule:: cdp.io - :members: - -.. automodule:: cdp.layer_tree - :members: - -.. automodule:: cdp.log - :members: - -.. automodule:: cdp.memory - :members: - -.. automodule:: cdp.network - :members: - -.. automodule:: cdp.overlay - :members: - -.. automodule:: cdp.page - :members: - -.. automodule:: cdp.performance - :members: - -.. automodule:: cdp.profiler - :members: - -.. automodule:: cdp.runtime - :members: - -.. automodule:: cdp.schema - :members: - -.. automodule:: cdp.security - :members: - -.. automodule:: cdp.service_worker - :members: - -.. automodule:: cdp.storage - :members: - -.. automodule:: cdp.system_info - :members: - -.. automodule:: cdp.target - :members: - -.. automodule:: cdp.tethering - :members: - -.. automodule:: cdp.tracing - :members: - -.. automodule:: cdp.web_audio - :members: - -.. automodule:: cdp.web_authn - :members: + api/* diff --git a/docs/develop.rst b/docs/develop.rst index 948d1e8..5422d2b 100644 --- a/docs/develop.rst +++ b/docs/develop.rst @@ -35,9 +35,10 @@ Note that the verification in this project occurs in two phases: 2. Run the generator. 3. Verify the *generated* code. -We focus on more effort on step 1, because if the generator is correct then the -generated code is correct by definition. The default ``make`` target runs all of -these targets in order, serving as a quick way to verify the entire project. +We focus most of the effort on step 1, because if the generator is correct then +the generated code is correct by definition. The default ``make`` target runs +all of these targets in order, serving as a quick way to verify the entire +project. To make documentation (i.e. the docs you're reading right now) go into the ``docs/`` directory and run ``make html``. diff --git a/generator/generate.py b/generator/generate.py index 56c9886..61a7466 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -48,26 +48,6 @@ def indent(s: str, n: int): return tw_indent(s, n * ' ') -def clear_dirs(package_path: Path): - ''' Remove generated code. ''' - def rmdir(path): - for subpath in path.iterdir(): - if subpath.is_file(): - subpath.unlink() - elif subpath.is_dir(): - rmdir(subpath) - path.rmdir() - - try: - (package_path / '__init__.py').unlink() - except FileNotFoundError: - pass - - for subpath in package_path.iterdir(): - if subpath.is_dir(): - rmdir(subpath) - - def inline_doc(description) -> str: ''' Generate an inline doc, e.g. ``#: This type is a ...`` ''' if not description: @@ -741,6 +721,7 @@ def get_refs(self): class CdpDomain: ''' A CDP domain contains metadata, types, commands, and events. ''' domain: str + description: typing.Optional[str] experimental: bool dependencies: typing.List[str] types: typing.List[CdpType] @@ -762,6 +743,7 @@ def from_json(cls, domain: dict): return cls( domain_name, + domain.get('description'), domain.get('experimental', False), domain.get('dependencies', list()), [CdpType.from_json(type) for type in types], @@ -826,6 +808,21 @@ def generate_imports(self): return code + def generate_sphinx(self) -> str: + ''' + Generate a Sphinx document for this domain. + ''' + docs = self.domain + '\n' + docs += '=' * len(self.domain) + '\n\n' + + if self.description: + docs += f'{self.description}\n\n' + + docs += f'.. automodule:: cdp.{self.module}\n' + docs += ' :members:\n' + + return docs + def parse(json_path, output_path): ''' @@ -862,6 +859,22 @@ def generate_init(init_path, domains): init_file.write('import cdp.{}\n'.format(domain.module)) +def generate_docs(docs_path, domains): + ''' + Generate Sphinx documents for each domain. + ''' + logger.info('Generating Sphinx documents') + + # Remove generated documents + for subpath in docs_path.iterdir(): + subpath.unlink() + + # Generate document for each domain + for domain in domains: + doc = docs_path / f'{domain.module}.rst' + with doc.open('w') as f: + f.write(domain.generate_sphinx()) + def main(): ''' Main entry point. ''' here = Path(__file__).parent.resolve() @@ -871,8 +884,13 @@ def main(): ] output_path = here.parent / 'cdp' output_path.mkdir(exist_ok=True) - clear_dirs(output_path) + # Remove generated code + for subpath in output_path.iterdir(): + if subpath.is_file() and subpath.name not in ('py.typed', 'util.py'): + subpath.unlink() + + # Parse domains domains = list() for json_path in json_paths: logger.info('Parsing JSON file %s', json_path) @@ -899,6 +917,9 @@ def main(): init_path = output_path / '__init__.py' generate_init(init_path, domains) + docs_path = here.parent / 'docs' / 'api' + generate_docs(docs_path, domains) + py_typed_path = output_path / 'py.typed' py_typed_path.touch() diff --git a/generator/test_generate.py b/generator/test_generate.py index 8a49a96..d607c86 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -832,14 +832,78 @@ def test_domain_shadows_builtin(): assert domain.module == 'input_' -def test_domain_shadows_builtin(): - ''' If a domain name shadows a Python builtin, it should have an underscore - appended to the module name. ''' - input_domain = { - "domain": "Input", - "types": [], - "commands": [], - "events": [], +def test_cdp_domain_sphinx(): + json_domain = { + "domain": "Animation", + "description": "This is the animation domain.", + "experimental": True, + "dependencies": [ + "Runtime", + "DOM" + ], + "types": [ + { + "id": "KeyframeStyle", + "description": "Keyframe Style", + "type": "object", + "properties": [ + { + "name": "offset", + "description": "Keyframe's time offset.", + "type": "string" + }, + { + "name": "easing", + "description": "`AnimationEffect`'s timing function.", + "type": "string" + } + ] + } + ], + "commands": [ + { + "name": "getCurrentTime", + "description": "Returns the current time of the an animation.", + "parameters": [ + { + "name": "id", + "description": "Id of animation.", + "type": "string" + } + ], + "returns": [ + { + "name": "currentTime", + "description": "Current time of the page.", + "type": "number" + } + ] + }, + ], + "events": [ + { + "name": "animationCanceled", + "description": "Event for when an animation has been cancelled.", + "parameters": [ + { + "name": "id", + "description": "Id of the animation that was cancelled.", + "type": "string" + } + ] + }, + ] } - domain = CdpDomain.from_json(input_domain) - assert domain.module == 'input_' + ''' A CDP domain should generate Sphinx documentation. ''' + expected = dedent("""\ + Animation + ========= + + This is the animation domain. + + .. automodule:: cdp.animation + :members: + """) + domain = CdpDomain.from_json(json_domain) + actual = domain.generate_sphinx() + assert expected == actual From 7ba80e0b1dfd68ec3bba5156c81e00945f9cbde1 Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 14:16:23 -0500 Subject: [PATCH 05/81] Fix Sphinx build warnings (#10) There were a few obscure warnings about backticks and pipes, as well as some indendation issues. Those are all fixed now. --- generator/generate.py | 58 ++++++++++++++++++++++++++++++-------- generator/test_generate.py | 10 +++++++ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/generator/generate.py b/generator/generate.py index 61a7466..5308927 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -7,6 +7,7 @@ import operator import os from pathlib import Path +import re from textwrap import dedent, indent as tw_indent import typing @@ -48,11 +49,39 @@ def indent(s: str, n: int): return tw_indent(s, n * ' ') +BACKTICK_RE = re.compile(r'`([^`]+)`(\w+)?') + + +def escape_backticks(docstr: str) -> str: + ''' + Escape backticks in a docstring by doubling them up. + + This is a little tricky because RST requires a non-letter character after + the closing backticks, but some CDPs docs have things like "`AxNodeId`s". + If we double the backticks in that string, then it won't be valid RST. The + fix is to insert an apostrophe if an "s" trails the backticks. + ''' + def replace_one(match): + if match.group(2) == 's': + return f"``{match.group(1)}``'s" + elif match.group(2): + # This case (some trailer other than "s") doesn't currently exist + # in the CDP definitions, but it's here just to be safe. + return f'``{match.group(1)}`` {match.group(2)}' + else: + return f'``{match.group(1)}``' + + # Sometimes pipes are used where backticks should have been used. + docstr = docstr.replace('|', '`') + return BACKTICK_RE.sub(replace_one, docstr) + + def inline_doc(description) -> str: ''' Generate an inline doc, e.g. ``#: This type is a ...`` ''' if not description: return '' + description = escape_backticks(description) lines = ['#: {}'.format(l) for l in description.split('\n')] return '\n'.join(lines) @@ -62,6 +91,7 @@ def docstring(description: typing.Optional[str]) -> str: if not description: return '' + description = escape_backticks(description) return dedent("'''\n{}\n'''").format(description) @@ -501,7 +531,7 @@ def py_annotation(self): def generate_doc(self): ''' Generate the docstring for this return. ''' if self.description: - doc = self.description.replace('`', '``') + doc = self.description.replace('`', '``').replace('\n', ' ') if self.optional: doc = f'*(Optional)* {doc}' else: @@ -575,14 +605,13 @@ def generate_code(self) -> str: code += ret # Generate the docstring + doc = '' + if self.description: + doc = self.description if self.deprecated: - doc = f'.. deprecated:: {current_version}' - else: - doc = '' + doc += f'\n\n.. deprecated:: {current_version}' if self.experimental: - doc += f'**EXPERIMENTAL**\n\n' - if self.description: - doc += self.description + doc += f'\n\n**EXPERIMENTAL**' if self.parameters and doc: doc += '\n\n' elif not self.parameters and self.returns: @@ -875,6 +904,7 @@ def generate_docs(docs_path, domains): with doc.open('w') as f: f.write(domain.generate_sphinx()) + def main(): ''' Main entry point. ''' here = Path(__file__).parent.resolve() @@ -897,15 +927,21 @@ def main(): domains.extend(parse(json_path, output_path)) domains.sort(key=operator.attrgetter('domain')) - # The DOM domain includes an erroneous $ref that refers to itself. It's - # easier to patch that here than it is to modify the generator code. + # Patch up CDP errors. It's easier to patch that here than it is to modify + # the generator code. + # 1. DOM includes an erroneous $ref that refers to itself. + # 2. Page includes an event with an extraneous backtick in the description. for domain in domains: if domain.domain == 'DOM': for cmd in domain.commands: if cmd.name == 'resolveNode': + # Patch 1 cmd.parameters[1].ref = 'BackendNodeId' - break - break + elif domain.domain == 'Page': + for event in domain.events: + if event.name == 'screencastVisibilityChanged': + # Patch 2 + event.description = event.description.replace('`', '') for domain in domains: logger.info('Generating module: %s → %s.py', domain.domain, diff --git a/generator/test_generate.py b/generator/test_generate.py index d607c86..37d8902 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -37,6 +37,16 @@ def test_docstring(): assert expected == actual +def test_escape_docstring(): + description = 'Escape a `Backtick` and some `Backtick`s.' + expected = dedent("""\ + ''' + Escape a ``Backtick`` and some ``Backtick``'s. + '''""") + actual = docstring(description) + assert expected == actual + + def test_cdp_primitive_type(): json_type = { "id": "AXNodeId", From d2be9c8c4a6aeffc561fefb2ce792fcd22ffda88 Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 15:07:03 -0500 Subject: [PATCH 06/81] Clean up CDP module pages #10 Each module page now has separate sections for types, commands, and events. --- generator/generate.py | 24 +++++++++++++++++------- generator/test_generate.py | 24 ++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/generator/generate.py b/generator/generate.py index 5308927..727758e 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -28,10 +28,8 @@ '''.format(SHARED_HEADER) MODULE_HEADER = '''{} - -\'\'\' -CDP {{}} Domain{{}} -\'\'\' +# +# CDP domain: {{}}{{}} from __future__ import annotations from cdp.util import event_class, T_JSON_DICT @@ -843,12 +841,24 @@ def generate_sphinx(self) -> str: ''' docs = self.domain + '\n' docs += '=' * len(self.domain) + '\n\n' - if self.description: docs += f'{self.description}\n\n' + if self.experimental: + docs += '*This CDP domain is experimental.*\n\n' + docs += f'.. module:: cdp.{self.module}\n\n' + docs += '* Types_\n* Commands_\n* Events_\n\n' - docs += f'.. automodule:: cdp.{self.module}\n' - docs += ' :members:\n' + docs += 'Types\n-----\n' + for type in self.types: + docs += f'\n.. autoclass:: {type.id}\n' + + docs += '\nCommands\n--------\n' + for command in sorted(self.commands, key=operator.attrgetter('py_name')): + docs += f'\n.. autofunction:: {command.py_name}\n' + + docs += '\nEvents\n------\n' + for event in self.events: + docs += f'\n.. autoclass:: {event.py_name}\n' return docs diff --git a/generator/test_generate.py b/generator/test_generate.py index 37d8902..ec66160 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -911,8 +911,28 @@ def test_cdp_domain_sphinx(): This is the animation domain. - .. automodule:: cdp.animation - :members: + *This CDP domain is experimental.* + + .. module:: cdp.animation + + * Types_ + * Commands_ + * Events_ + + Types + ----- + + .. autoclass:: KeyframeStyle + + Commands + -------- + + .. autofunction:: get_current_time + + Events + ------ + + .. autoclass:: AnimationCanceled """) domain = CdpDomain.from_json(json_domain) actual = domain.generate_sphinx() From 29d27981363e0a790edcd11f3822362d2579b5ae Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 15:27:49 -0500 Subject: [PATCH 07/81] Fix generator unit tests #10 These were broken when I merged in a branch earlier. --- generator/generate.py | 4 ++-- generator/test_generate.py | 32 +++++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/generator/generate.py b/generator/generate.py index 727758e..83463a1 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -529,7 +529,7 @@ def py_annotation(self): def generate_doc(self): ''' Generate the docstring for this return. ''' if self.description: - doc = self.description.replace('`', '``').replace('\n', ' ') + doc = self.description.replace('\n', ' ') if self.optional: doc = f'*(Optional)* {doc}' else: @@ -622,7 +622,7 @@ def generate_code(self) -> str: elif len(self.returns) > 1: doc += '\n' doc += ':returns: A tuple with the following items:\n\n' - ret_docs = '\n'.join(f'{i+1}. **{r.name}** - {r.generate_doc()}' for i, r + ret_docs = '\n'.join(f'{i}. **{r.name}** – {r.generate_doc()}' for i, r in enumerate(self.returns)) doc += indent(ret_docs, 4) if doc: diff --git a/generator/test_generate.py b/generator/test_generate.py index ec66160..226e1e1 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -272,12 +272,13 @@ def get_partial_ax_tree( ''' Fetches the accessibility node and partial accessibility tree for this DOM node, if it exists. - :param node_id: Identifier of the node to get the partial accessibility tree for. - :param backend_node_id: Identifier of the backend node to get the partial accessibility tree for. - :param object_id: JavaScript object id of the node wrapper to get the partial accessibility tree for. - :param fetch_relatives: Whether to fetch this nodes ancestors, siblings and children. Defaults to true. - :returns: The ``Accessibility.AXNode`` for this DOM node, if it exists, plus its ancestors, siblings and - children, if requested. + **EXPERIMENTAL** + + :param node_id: *(Optional)* Identifier of the node to get the partial accessibility tree for. + :param backend_node_id: *(Optional)* Identifier of the backend node to get the partial accessibility tree for. + :param object_id: *(Optional)* JavaScript object id of the node wrapper to get the partial accessibility tree for. + :param fetch_relatives: *(Optional)* Whether to fetch this nodes ancestors, siblings and children. Defaults to true. + :returns: The ``Accessibility.AXNode`` for this DOM node, if it exists, plus its ancestors, siblings and children, if requested. ''' params: T_JSON_DICT = dict() if node_id is not None: @@ -385,6 +386,8 @@ def get_browser_command_line() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typin Returns the command line switches for the browser process if, and only if --enable-automation is on the commandline. + **EXPERIMENTAL** + :returns: Commandline parameters ''' cmd_dict: T_JSON_DICT = { @@ -543,12 +546,13 @@ def get_encoded_response( :param request_id: Identifier of the network request to get content for. :param encoding: The encoding to use. - :param quality: The quality of the encoding (0-1). (defaults to 1) - :param size_only: Whether to only return the size information (defaults to false). - :returns: a tuple with the following items: - 0. body: (Optional) The encoded body as a base64 string. Omitted if sizeOnly is true. - 1. originalSize: Size before re-encoding. - 2. encodedSize: Size after re-encoding. + :param quality: *(Optional)* The quality of the encoding (0-1). (defaults to 1) + :param size_only: *(Optional)* Whether to only return the size information (defaults to false). + :returns: A tuple with the following items: + + 0. **body** – *(Optional)* The encoded body as a base64 string. Omitted if sizeOnly is true. + 1. **originalSize** – Size before re-encoding. + 2. **encodedSize** – Size after re-encoding. ''' params: T_JSON_DICT = dict() params['requestId'] = request_id.to_json() @@ -607,9 +611,11 @@ def grant_permissions( ''' Grant specific permissions to the given origin and reject all others. + **EXPERIMENTAL** + :param origin: :param permissions: - :param browser_context_id: BrowserContext to override permissions. When omitted, default browser context is used. + :param browser_context_id: *(Optional)* BrowserContext to override permissions. When omitted, default browser context is used. ''' params: T_JSON_DICT = dict() params['origin'] = origin From 9e4d5edd1099a87ecb37324c5b5f01a526c85ddf Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 15:41:21 -0500 Subject: [PATCH 08/81] Show more details in API docs #10 This includes members of enums, plus fields for class types and events. --- generator/generate.py | 10 ++++++++++ generator/test_generate.py | 2 ++ 2 files changed, 12 insertions(+) diff --git a/generator/generate.py b/generator/generate.py index 83463a1..18c76a5 100644 --- a/generator/generate.py +++ b/generator/generate.py @@ -849,8 +849,15 @@ def generate_sphinx(self) -> str: docs += '* Types_\n* Commands_\n* Events_\n\n' docs += 'Types\n-----\n' + docs += '\nGenerally you do not need to instantiate CDP types ' \ + 'yourself. Instead, the API creates objects for you as return ' \ + 'values from commands, and then you can use those objects as ' \ + 'arguments to other commands.\n' for type in self.types: docs += f'\n.. autoclass:: {type.id}\n' + docs += ' :members:\n' + docs += ' :undoc-members:\n' + docs += ' :exclude-members: from_json, to_json\n' docs += '\nCommands\n--------\n' for command in sorted(self.commands, key=operator.attrgetter('py_name')): @@ -859,6 +866,9 @@ def generate_sphinx(self) -> str: docs += '\nEvents\n------\n' for event in self.events: docs += f'\n.. autoclass:: {event.py_name}\n' + docs += ' :members:\n' + docs += ' :undoc-members:\n' + docs += ' :exclude-members: from_json, to_json\n' return docs diff --git a/generator/test_generate.py b/generator/test_generate.py index 226e1e1..0d19459 100644 --- a/generator/test_generate.py +++ b/generator/test_generate.py @@ -928,6 +928,8 @@ def test_cdp_domain_sphinx(): Types ----- + Generally you do not need to instantiate CDP types yourself. Instead, the API creates objects for you as return values from commands, and then you can use those objects as arguments to other commands. + .. autoclass:: KeyframeStyle Commands From acb8a191888eb074e70ab9186e47b82dccfeae16 Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 15:47:31 -0500 Subject: [PATCH 09/81] A little more cleanup of docs #10 --- docs/api.rst | 1 + docs/index.rst | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 32b57a9..7060e46 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,5 +6,6 @@ commands, and events. .. toctree:: :glob: + :maxdepth: 1 api/* diff --git a/docs/index.rst b/docs/index.rst index 4c1142b..76930fa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,8 +3,11 @@ PyCDP ===== +Python wrappers for Chrome DevTools Protocol (CDP). + .. toctree:: :caption: Contents + :maxdepth: 1 overview getting_started @@ -16,7 +19,6 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` -* :ref:`search` .. image:: https://hyperiongray.s3.amazonaws.com/define-hg.svg From 934a42427435692d6d5dc3936b765c4730919a7f Mon Sep 17 00:00:00 2001 From: "Mark E. Haase" Date: Fri, 28 Feb 2020 15:48:58 -0500 Subject: [PATCH 10/81] Regenerate code and docs --- cdp/__init__.py | 12 +- cdp/accessibility.py | 117 ++++---- cdp/animation.py | 112 ++++--- cdp/application_cache.py | 51 ++-- cdp/audits.py | 26 +- cdp/background_service.py | 48 ++- cdp/browser.py | 124 ++++---- cdp/cache_storage.py | 59 ++-- cdp/cast.py | 40 ++- cdp/console.py | 26 +- cdp/css.py | 307 ++++++++++--------- cdp/database.py | 46 ++- cdp/debugger.py | 250 ++++++++-------- cdp/device_orientation.py | 18 +- cdp/dom.py | 429 +++++++++++++-------------- cdp/dom_debugger.py | 76 +++-- cdp/dom_snapshot.py | 210 ++++++------- cdp/dom_storage.py | 48 ++- cdp/emulation.py | 82 ++--- cdp/fetch.py | 90 +++--- cdp/headless_experimental.py | 37 ++- cdp/heap_profiler.py | 64 ++-- cdp/indexed_db.py | 82 +++-- cdp/{input.py => input_.py} | 94 +++--- cdp/inspector.py | 24 +- cdp/io.py | 36 ++- cdp/layer_tree.py | 90 +++--- cdp/log.py | 38 ++- cdp/memory.py | 46 ++- cdp/network.py | 460 +++++++++++++++-------------- cdp/overlay.py | 88 +++--- cdp/page.py | 454 ++++++++++++++-------------- cdp/performance.py | 30 +- cdp/profiler.py | 98 +++--- cdp/runtime.py | 342 +++++++++++---------- cdp/schema.py | 22 +- cdp/security.py | 70 ++--- cdp/service_worker.py | 60 ++-- cdp/storage.py | 42 ++- cdp/system_info.py | 89 +++--- cdp/target.py | 164 +++++----- cdp/tethering.py | 18 +- cdp/tracing.py | 56 ++-- cdp/web_audio.py | 52 ++-- cdp/web_authn.py | 50 ++-- docs/api/accessibility.rst | 79 +++++ docs/api/animation.rst | 76 +++++ docs/api/application_cache.rst | 54 ++++ docs/api/audits.rst | 25 ++ docs/api/background_service.rst | 56 ++++ docs/api/browser.rst | 77 +++++ docs/api/cache_storage.rst | 61 ++++ docs/api/cast.rst | 49 +++ docs/api/console.rst | 37 +++ docs/api/css.rst | 212 +++++++++++++ docs/api/database.rst | 49 +++ docs/api/debugger.rst | 145 +++++++++ docs/api/device_orientation.rst | 25 ++ docs/api/dom.rst | 236 +++++++++++++++ docs/api/dom_debugger.rst | 50 ++++ docs/api/dom_snapshot.rst | 106 +++++++ docs/api/dom_storage.rst | 65 ++++ docs/api/emulation.rst | 78 +++++ docs/api/fetch.rst | 79 +++++ docs/api/headless_experimental.rst | 39 +++ docs/api/heap_profiler.rst | 90 ++++++ docs/api/indexed_db.rst | 74 +++++ docs/api/input_.rst | 52 ++++ docs/api/inspector.rst | 40 +++ docs/api/io.rst | 32 ++ docs/api/layer_tree.rst | 84 ++++++ docs/api/log.rst | 46 +++ docs/api/memory.rst | 61 ++++ docs/api/network.rst | 363 +++++++++++++++++++++++ docs/api/overlay.rst | 89 ++++++ docs/api/page.rst | 329 +++++++++++++++++++++ docs/api/performance.rst | 37 +++ docs/api/profiler.rst | 98 ++++++ docs/api/runtime.rst | 214 ++++++++++++++ docs/api/schema.rst | 28 ++ docs/api/security.rst | 71 +++++ docs/api/service_worker.rst | 90 ++++++ docs/api/storage.rst | 63 ++++ docs/api/system_info.rst | 67 +++++ docs/api/target.rst | 113 +++++++ docs/api/tethering.rst | 32 ++ docs/api/tracing.rst | 66 +++++ docs/api/web_audio.rst | 70 +++++ docs/api/web_authn.rst | 65 ++++ 89 files changed, 6228 insertions(+), 2421 deletions(-) rename cdp/{input.py => input_.py} (91%) create mode 100644 docs/api/accessibility.rst create mode 100644 docs/api/animation.rst create mode 100644 docs/api/application_cache.rst create mode 100644 docs/api/audits.rst create mode 100644 docs/api/background_service.rst create mode 100644 docs/api/browser.rst create mode 100644 docs/api/cache_storage.rst create mode 100644 docs/api/cast.rst create mode 100644 docs/api/console.rst create mode 100644 docs/api/css.rst create mode 100644 docs/api/database.rst create mode 100644 docs/api/debugger.rst create mode 100644 docs/api/device_orientation.rst create mode 100644 docs/api/dom.rst create mode 100644 docs/api/dom_debugger.rst create mode 100644 docs/api/dom_snapshot.rst create mode 100644 docs/api/dom_storage.rst create mode 100644 docs/api/emulation.rst create mode 100644 docs/api/fetch.rst create mode 100644 docs/api/headless_experimental.rst create mode 100644 docs/api/heap_profiler.rst create mode 100644 docs/api/indexed_db.rst create mode 100644 docs/api/input_.rst create mode 100644 docs/api/inspector.rst create mode 100644 docs/api/io.rst create mode 100644 docs/api/layer_tree.rst create mode 100644 docs/api/log.rst create mode 100644 docs/api/memory.rst create mode 100644 docs/api/network.rst create mode 100644 docs/api/overlay.rst create mode 100644 docs/api/page.rst create mode 100644 docs/api/performance.rst create mode 100644 docs/api/profiler.rst create mode 100644 docs/api/runtime.rst create mode 100644 docs/api/schema.rst create mode 100644 docs/api/security.rst create mode 100644 docs/api/service_worker.rst create mode 100644 docs/api/storage.rst create mode 100644 docs/api/system_info.rst create mode 100644 docs/api/target.rst create mode 100644 docs/api/tethering.rst create mode 100644 docs/api/tracing.rst create mode 100644 docs/api/web_audio.rst create mode 100644 docs/api/web_authn.rst diff --git a/cdp/__init__.py b/cdp/__init__.py index ec58a08..de3a091 100644 --- a/cdp/__init__.py +++ b/cdp/__init__.py @@ -1,9 +1,7 @@ -''' -DO NOT EDIT THIS FILE - -This file is generated from the CDP specification. If you need to make changes, -edit the generator and regenerate all of the modules. -''' +# DO NOT EDIT THIS FILE! +# +# This file is generated from the CDP specification. If you need to make +# changes, edit the generator and regenerate all of the modules. import cdp.util @@ -30,7 +28,7 @@ import cdp.heap_profiler import cdp.io import cdp.indexed_db -import cdp.input +import cdp.input_ import cdp.inspector import cdp.layer_tree import cdp.log diff --git a/cdp/accessibility.py b/cdp/accessibility.py index 8c38e79..f355f85 100644 --- a/cdp/accessibility.py +++ b/cdp/accessibility.py @@ -1,13 +1,11 @@ -''' -DO NOT EDIT THIS FILE - -This file is generated from the CDP specification. If you need to make changes, -edit the generator and regenerate all of the modules. - -Domain: Accessibility -Experimental: True -''' - +# DO NOT EDIT THIS FILE! +# +# This file is generated from the CDP specification. If you need to make +# changes, edit the generator and regenerate all of the modules. +# +# CDP domain: Accessibility (experimental) + +from __future__ import annotations from cdp.util import event_class, T_JSON_DICT from dataclasses import dataclass import enum @@ -25,7 +23,7 @@ def to_json(self) -> str: return self @classmethod - def from_json(cls, json: str) -> 'AXNodeId': + def from_json(cls, json: str) -> AXNodeId: return cls(json) def __repr__(self): @@ -58,7 +56,7 @@ def to_json(self) -> str: return self.value @classmethod - def from_json(cls, json: str) -> 'AXValueType': + def from_json(cls, json: str) -> AXValueType: return cls(json) @@ -77,7 +75,7 @@ def to_json(self) -> str: return self.value @classmethod - def from_json(cls, json: str) -> 'AXValueSourceType': + def from_json(cls, json: str) -> AXValueSourceType: return cls(json) @@ -98,7 +96,7 @@ def to_json(self) -> str: return self.value @classmethod - def from_json(cls, json: str) -> 'AXValueNativeSourceType': + def from_json(cls, json: str) -> AXValueNativeSourceType: return cls(json) @@ -108,25 +106,25 @@ class AXValueSource: A single source for a computed AX property. ''' #: What type of source this is. - type: 'AXValueSourceType' + type_: AXValueSourceType #: The value of this property source. - value: typing.Optional['AXValue'] = None + value: typing.Optional[AXValue] = None #: The name of the relevant attribute, if any. attribute: typing.Optional[str] = None #: The value of the relevant attribute, if any. - attribute_value: typing.Optional['AXValue'] = None + attribute_value: typing.Optional[AXValue] = None #: Whether this source is superseded by a higher priority source. superseded: typing.Optional[bool] = None #: The native markup source for this value, e.g. a