-
Notifications
You must be signed in to change notification settings - Fork 0
/
generate.py
105 lines (85 loc) · 4.28 KB
/
generate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import inspect
import typing
from itertools import starmap
from pathlib import Path
from typing import Callable
import playwright.async_api._generated as aapi
import playwright.sync_api._generated as api
from playwright._repo_version import version
def additional_method_matches(additional_methods: dict[str, int], fn: Callable, fn_name: str, expected_arg_type: type):
if fn_name in additional_methods:
pos = additional_methods[fn_name]
fn_annotations = fn.__annotations__
if [*fn_annotations.values()][pos] in (expected_arg_type, typing.Optional[expected_arg_type]):
return True
else:
print(
f"warning: {fn=} arg={[*fn_annotations.items()][pos]} does not match required type {expected_arg_type}")
return False
def generate_css_js_injections() -> tuple[list[tuple[str, int, str]], ...]:
"""
Generates of list of (method, argument index, module) for both CSS and JS based on the function argument names,
types, as well as a list of edge cases. This tuple is necessary to configure language injections for IntelliJ IDEs
"""
css_found = []
js_found = []
# these are language injections for methods that do not follow variable naming conventions
# positional argument indexes do not count self argument
additional_js_to_idx = {
"add_init_script": 0
}
additional_css_to_idx = {
"drag_and_drop": 1
}
# since playwright is consistent with how they name all css selector args "selector", we can simply look for that
CSS_SELECTOR_ARG_NAME = "selector"
CSS_SELECTOR_ARG_TYPE = str
JS_EXPRESSION_ARG_NAME = "expression"
JS_EXPRESSION_ARG_TYPE = str
for a in [api, aapi]:
classes = [cls for cls in a.__dict__.values() if isinstance(cls, type)]
for c in classes:
methods = inspect.getmembers(c, predicate=inspect.isfunction)
for fn_name, fn in methods:
fn_annotations = fn.__annotations__
if additional_method_matches(additional_js_to_idx, fn, fn_name, JS_EXPRESSION_ARG_TYPE):
js_found.append((fn_name, additional_js_to_idx[fn_name], f"{c.__module__}.{c.__name__}"))
if additional_method_matches(additional_css_to_idx, fn, fn_name, CSS_SELECTOR_ARG_TYPE):
css_found.append((fn_name, additional_css_to_idx[fn_name], f"{c.__module__}.{c.__name__}"))
for pos, (arg, type_) in enumerate(fn_annotations.items()):
if arg == CSS_SELECTOR_ARG_NAME and type_ == CSS_SELECTOR_ARG_TYPE:
css_found.append((fn_name, pos, f"{c.__module__}.{c.__name__}"))
elif arg == JS_EXPRESSION_ARG_NAME and type_ == JS_EXPRESSION_ARG_TYPE:
js_found.append((fn_name, pos, f"{c.__module__}.{c.__name__}"))
return css_found, js_found
def generate_expression(method: str, index: int, module: str) -> str:
return f'pyLiteralExpression().and(pyMethodArgument("{method}", {index}, "{module}"))'
def generate_xml(method: str, index: int, module: str) -> str:
return f"<place><![CDATA[{generate_expression(method, index, module)}]]></place>"
def main():
language_injection_template = """
<injection language="{language}" injector-id="python">
<display-name>{name}</display-name>
<single-file value="false" />
{places}
</injection>
"""
css, js = generate_css_js_injections()
css.sort()
js.sort()
print(f"generated {len(css)} css and {len(js)} js language injection definitions")
major_version = version.split(".")[0]
output_file = f"playwright-{major_version}.xml"
css_injections = language_injection_template.format(language="CSS", name="playwright - CSS",
places="\n".join(starmap(generate_xml, css)))
js_injections = language_injection_template.format(language="javascript", name="playwright - JS",
places="\n".join(starmap(generate_xml, js)))
Path(output_file).write_text(f"""
<LanguageInjectionConfiguration>
{css_injections}
{js_injections}
</LanguageInjectionConfiguration>
""")
print(f"wrote output file: {output_file}")
if __name__ == '__main__':
main()