77import operator
88import os
99from pathlib import Path
10+ import re
1011from textwrap import dedent , indent as tw_indent
1112import typing
1213
@@ -48,11 +49,39 @@ def indent(s: str, n: int):
4849 return tw_indent (s , n * ' ' )
4950
5051
52+ BACKTICK_RE = re .compile (r'`([^`]+)`(\w+)?' )
53+
54+
55+ def escape_backticks (docstr : str ) -> str :
56+ '''
57+ Escape backticks in a docstring by doubling them up.
58+
59+ This is a little tricky because RST requires a non-letter character after
60+ the closing backticks, but some CDPs docs have things like "`AxNodeId`s".
61+ If we double the backticks in that string, then it won't be valid RST. The
62+ fix is to insert an apostrophe if an "s" trails the backticks.
63+ '''
64+ def replace_one (match ):
65+ if match .group (2 ) == 's' :
66+ return f"``{ match .group (1 )} ``'s"
67+ elif match .group (2 ):
68+ # This case (some trailer other than "s") doesn't currently exist
69+ # in the CDP definitions, but it's here just to be safe.
70+ return f'``{ match .group (1 )} `` { match .group (2 )} '
71+ else :
72+ return f'``{ match .group (1 )} ``'
73+
74+ # Sometimes pipes are used where backticks should have been used.
75+ docstr = docstr .replace ('|' , '`' )
76+ return BACKTICK_RE .sub (replace_one , docstr )
77+
78+
5179def inline_doc (description ) -> str :
5280 ''' Generate an inline doc, e.g. ``#: This type is a ...`` '''
5381 if not description :
5482 return ''
5583
84+ description = escape_backticks (description )
5685 lines = ['#: {}' .format (l ) for l in description .split ('\n ' )]
5786 return '\n ' .join (lines )
5887
@@ -62,6 +91,7 @@ def docstring(description: typing.Optional[str]) -> str:
6291 if not description :
6392 return ''
6493
94+ description = escape_backticks (description )
6595 return dedent ("'''\n {}\n '''" ).format (description )
6696
6797
@@ -501,7 +531,7 @@ def py_annotation(self):
501531 def generate_doc (self ):
502532 ''' Generate the docstring for this return. '''
503533 if self .description :
504- doc = self .description .replace ('`' , '``' )
534+ doc = self .description .replace ('`' , '``' ). replace ( ' \n ' , ' ' )
505535 if self .optional :
506536 doc = f'*(Optional)* { doc } '
507537 else :
@@ -575,14 +605,13 @@ def generate_code(self) -> str:
575605 code += ret
576606
577607 # Generate the docstring
608+ doc = ''
609+ if self .description :
610+ doc = self .description
578611 if self .deprecated :
579- doc = f'.. deprecated:: { current_version } '
580- else :
581- doc = ''
612+ doc += f'\n \n .. deprecated:: { current_version } '
582613 if self .experimental :
583- doc += f'**EXPERIMENTAL**\n \n '
584- if self .description :
585- doc += self .description
614+ doc += f'\n \n **EXPERIMENTAL**'
586615 if self .parameters and doc :
587616 doc += '\n \n '
588617 elif not self .parameters and self .returns :
@@ -875,6 +904,7 @@ def generate_docs(docs_path, domains):
875904 with doc .open ('w' ) as f :
876905 f .write (domain .generate_sphinx ())
877906
907+
878908def main ():
879909 ''' Main entry point. '''
880910 here = Path (__file__ ).parent .resolve ()
@@ -897,15 +927,21 @@ def main():
897927 domains .extend (parse (json_path , output_path ))
898928 domains .sort (key = operator .attrgetter ('domain' ))
899929
900- # The DOM domain includes an erroneous $ref that refers to itself. It's
901- # easier to patch that here than it is to modify the generator code.
930+ # Patch up CDP errors. It's easier to patch that here than it is to modify
931+ # the generator code.
932+ # 1. DOM includes an erroneous $ref that refers to itself.
933+ # 2. Page includes an event with an extraneous backtick in the description.
902934 for domain in domains :
903935 if domain .domain == 'DOM' :
904936 for cmd in domain .commands :
905937 if cmd .name == 'resolveNode' :
938+ # Patch 1
906939 cmd .parameters [1 ].ref = 'BackendNodeId'
907- break
908- break
940+ elif domain .domain == 'Page' :
941+ for event in domain .events :
942+ if event .name == 'screencastVisibilityChanged' :
943+ # Patch 2
944+ event .description = event .description .replace ('`' , '' )
909945
910946 for domain in domains :
911947 logger .info ('Generating module: %s → %s.py' , domain .domain ,
0 commit comments