1
- import json
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
2
15
import re
3
- from typing import Dict , List
16
+ from sys import stderr
17
+ from typing import Any , Dict , List , cast
4
18
5
19
import requests
6
20
@@ -26,44 +40,91 @@ def load(self) -> None:
26
40
class_name = None
27
41
method_name = None
28
42
in_a_code_block = False
43
+ in_options = False
44
+ pending_empty_line = False
45
+
29
46
for line in api_md .split ("\n " ):
30
- matches = re .search (r"(class: (\w+)|(Playwright) module)" , line )
31
- if matches :
32
- class_name = matches .group (2 ) or matches .group (3 )
33
- method_name = None
34
- if class_name :
35
- if class_name not in self .documentation :
36
- self .documentation [class_name ] = {}
37
- matches = re .search (r"#### \w+\.(.+?)(\(|$)" , line )
38
- if matches :
39
- method_name = matches .group (1 )
40
- # Skip heading
41
- continue
42
47
if "```js" in line :
43
48
in_a_code_block = True
44
49
elif "```" in line :
45
50
in_a_code_block = False
46
- elif method_name and not in_a_code_block :
47
- if method_name not in self .documentation [class_name ]: # type: ignore
51
+ continue
52
+ if in_a_code_block :
53
+ continue
54
+
55
+ if line .startswith ("### " ):
56
+ class_name = None
57
+ method_name = None
58
+ match = re .search (r"### class: (\w+)" , line ) or re .search (
59
+ r"### Playwright module" , line
60
+ )
61
+ if match :
62
+ class_name = match .group (1 ) if match .groups () else "Playwright"
63
+ self .documentation [class_name ] = {} # type: ignore
64
+ continue
65
+ if line .startswith ("#### " ):
66
+ match = re .search (r"#### (\w+)\.(.+?)(\(|$)" , line )
67
+ if match :
68
+ if not class_name or match .group (1 ).lower () != class_name .lower ():
69
+ print ("Error: " + line + " in " + cast (str , class_name ))
70
+ method_name = match .group (2 )
71
+ pending_empty_line = False
48
72
self .documentation [class_name ][method_name ] = [] # type: ignore
49
- self .documentation [class_name ][method_name ].append (line ) # type: ignore
50
-
51
- def _transform_doc_entry (self , entries : List [str ]) -> List [str ]:
52
- trimmed = "\n " .join (entries ).strip ().replace ("\\ " , "\\ \\ " )
53
- trimmed = re .sub (r"<\[Array\]<\[(.*?)\]>>" , r"<List[\1]>" , trimmed )
54
- trimmed = trimmed .replace ("Object" , "Dict" )
55
- trimmed = trimmed .replace ("Array" , "List" )
56
- trimmed = trimmed .replace ("boolean" , "bool" )
57
- trimmed = trimmed .replace ("string" , "str" )
58
- trimmed = trimmed .replace ("number" , "int" )
59
- trimmed = trimmed .replace ("Buffer" , "bytes" )
60
- trimmed = re .sub (r"<\?\[(.*?)\]>" , r"<Optional[\1]>" , trimmed )
61
- trimmed = re .sub (r"<\[Promise\]<(.*)>>" , r"<\1>" , trimmed )
62
- trimmed = re .sub (r"<\[(\w+?)\]>" , r"<\1>" , trimmed )
63
-
64
- return trimmed .replace ("\n \n \n " , "\n \n " ).split ("\n " )
65
-
66
- def print_entry (self , class_name : str , method_name : str ) -> None :
73
+ continue
74
+
75
+ if not method_name : # type: ignore
76
+ continue
77
+
78
+ if (
79
+ line .startswith ("- `options` <[Object]>" )
80
+ or line .startswith ("- `options` <[string]|[Object]>" )
81
+ or line .startswith ("- `overrides` <" )
82
+ or line .startswith ("- `response` <" )
83
+ ):
84
+ in_options = True
85
+ continue
86
+ if not line .startswith (" " ):
87
+ in_options = False
88
+ if in_options :
89
+ line = line [2 :]
90
+ # if not line.strip():
91
+ # continue
92
+ if "Shortcut for" in line :
93
+ continue
94
+ if not line .strip ():
95
+ pending_empty_line = bool (self .documentation [class_name ][method_name ]) # type: ignore
96
+ continue
97
+ else :
98
+ if pending_empty_line :
99
+ pending_empty_line = False
100
+ self .documentation [class_name ][method_name ].append ("" ) # type: ignore
101
+ self .documentation [class_name ][method_name ].append (line ) # type: ignore
102
+
103
+ def _transform_doc_entry (self , line : str ) -> str :
104
+ line = line .replace ("\\ " , "\\ \\ " )
105
+ line = re .sub (r"<\[Array\]<\[(.*?)\]>>" , r"<List[\1]>" , line )
106
+ line = line .replace ("Object" , "Dict" )
107
+ line = line .replace ("Array" , "List" )
108
+ line = line .replace ("boolean" , "bool" )
109
+ line = line .replace ("string" , "str" )
110
+ line = line .replace ("number" , "int" )
111
+ line = line .replace ("Buffer" , "bytes" )
112
+ line = re .sub (r"<\?\[(.*?)\]>" , r"<Optional[\1]>" , line )
113
+ line = re .sub (r"<\[Promise\]<(.*)>>" , r"<\1>" , line )
114
+ line = re .sub (r"<\[(\w+?)\]>" , r"<\1>" , line )
115
+
116
+ # Following should be fixed in the api.md upstream
117
+ line = re .sub (r"- `pageFunction` <[^>]+>" , "- `expression` <[str]>" , line )
118
+ line = re .sub ("- `urlOrPredicate`" , "- `url`" , line )
119
+ line = re .sub ("- `playwrightBinding`" , "- `binding`" , line )
120
+ line = re .sub ("- `playwrightFunction`" , "- `binding`" , line )
121
+ line = re .sub ("- `script`" , "- `source`" , line )
122
+
123
+ return line
124
+
125
+ def print_entry (
126
+ self , class_name : str , method_name : str , signature : Dict [str , Any ] = None
127
+ ) -> None :
67
128
if class_name == "BindingCall" or method_name == "pid" :
68
129
return
69
130
if method_name in self .method_name_rewrites :
@@ -75,19 +136,55 @@ def print_entry(self, class_name: str, method_name: str) -> None:
75
136
raw_doc = self .documentation ["JSHandle" ][method_name ]
76
137
else :
77
138
raw_doc = self .documentation [class_name ][method_name ]
139
+
78
140
ident = " " * 4 * 2
79
- doc_entries = self ._transform_doc_entry (raw_doc )
141
+
142
+ if signature :
143
+ if "return" in signature :
144
+ del signature ["return" ]
145
+
80
146
print (f'{ ident } """' )
81
- for line in doc_entries :
82
- print (f"{ ident } { line } " )
147
+
148
+ # Validate signature
149
+ validate_parameters = True
150
+ for line in raw_doc :
151
+ if not line .strip ():
152
+ validate_parameters = (
153
+ False # Stop validating parameters after a blank line
154
+ )
155
+
156
+ transformed = self ._transform_doc_entry (line )
157
+ match = re .search (r"^\- `(\w+)`" , transformed )
158
+ if validate_parameters and signature and match :
159
+ name = match .group (1 )
160
+ if name not in signature :
161
+ print (
162
+ f"Not implemented parameter { class_name } .{ method_name } ({ name } =)" ,
163
+ file = stderr ,
164
+ )
165
+ continue
166
+ else :
167
+ del signature [name ]
168
+ print (f"{ ident } { transformed } " )
169
+ if name == "expression" and "force_expr" in signature :
170
+ print (
171
+ f"{ ident } - `force_expr` <[bool]> Whether to treat given expression as JavaScript evaluate expression, even though it looks like an arrow function"
172
+ )
173
+ del signature ["force_expr" ]
174
+ else :
175
+ print (f"{ ident } { transformed } " )
176
+
83
177
print (f'{ ident } """' )
84
178
179
+ if signature :
180
+ print (
181
+ f"Not documented parameters: { class_name } .{ method_name } ({ signature .keys ()} )" ,
182
+ file = stderr ,
183
+ )
184
+
85
185
86
186
if __name__ == "__main__" :
87
- print (
88
- json .dumps (
89
- DocumentationProvider ().documentation ["Page" ].get ("keyboard" ),
90
- sort_keys = True ,
91
- indent = 4 ,
92
- )
93
- )
187
+ DocumentationProvider ().print_entry ("Page" , "goto" )
188
+ DocumentationProvider ().print_entry ("Page" , "evaluateHandle" )
189
+ DocumentationProvider ().print_entry ("ElementHandle" , "click" )
190
+ DocumentationProvider ().print_entry ("Page" , "screenshot" )
0 commit comments