Skip to content

Commit 6ae5001

Browse files
committed
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*.
1 parent 3c8461d commit 6ae5001

5 files changed

Lines changed: 133 additions & 166 deletions

File tree

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
all: mypy-generate test-generate generate test-import mypy-cdp test-cdp
1+
.PHONY: docs
2+
3+
default: mypy-generate test-generate generate test-import mypy-cdp test-cdp
4+
5+
docs:
6+
$(MAKE) -C docs html
27

38
generate:
49
python generator/generate.py

docs/api.rst

Lines changed: 7 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,10 @@
1-
API Reference
2-
=============
1+
API Modules
2+
===========
33

4-
.. automodule:: cdp.accessibility
5-
:members:
4+
Each module in the API corresponds to a "domain" in CDP and may contain types,
5+
commands, and events.
66

7-
.. automodule:: cdp.animation
8-
:members:
7+
.. toctree::
8+
:glob:
99

10-
.. automodule:: cdp.application_cache
11-
:members:
12-
13-
.. automodule:: cdp.audits
14-
:members:
15-
16-
.. automodule:: cdp.background_service
17-
:members:
18-
19-
.. automodule:: cdp.browser
20-
:members:
21-
22-
.. automodule:: cdp.cache_storage
23-
:members:
24-
25-
.. automodule:: cdp.cast
26-
:members:
27-
28-
.. automodule:: cdp.console
29-
:members:
30-
31-
.. automodule:: cdp.css
32-
:members:
33-
34-
.. automodule:: cdp.database
35-
:members:
36-
37-
.. automodule:: cdp.debugger
38-
:members:
39-
40-
.. automodule:: cdp.device_orientation
41-
:members:
42-
43-
.. automodule:: cdp.dom
44-
:members:
45-
46-
.. automodule:: cdp.dom_debugger
47-
:members:
48-
49-
.. automodule:: cdp.dom_snapshot
50-
:members:
51-
52-
.. automodule:: cdp.dom_storage
53-
:members:
54-
55-
.. automodule:: cdp.emulation
56-
:members:
57-
58-
.. automodule:: cdp.fetch
59-
:members:
60-
61-
.. automodule:: cdp.headless_experimental
62-
:members:
63-
64-
.. automodule:: cdp.heap_profiler
65-
:members:
66-
67-
.. automodule:: cdp.indexed_db
68-
:members:
69-
70-
.. automodule:: cdp.input
71-
:members:
72-
73-
.. automodule:: cdp.inspector
74-
:members:
75-
76-
.. automodule:: cdp.io
77-
:members:
78-
79-
.. automodule:: cdp.layer_tree
80-
:members:
81-
82-
.. automodule:: cdp.log
83-
:members:
84-
85-
.. automodule:: cdp.memory
86-
:members:
87-
88-
.. automodule:: cdp.network
89-
:members:
90-
91-
.. automodule:: cdp.overlay
92-
:members:
93-
94-
.. automodule:: cdp.page
95-
:members:
96-
97-
.. automodule:: cdp.performance
98-
:members:
99-
100-
.. automodule:: cdp.profiler
101-
:members:
102-
103-
.. automodule:: cdp.runtime
104-
:members:
105-
106-
.. automodule:: cdp.schema
107-
:members:
108-
109-
.. automodule:: cdp.security
110-
:members:
111-
112-
.. automodule:: cdp.service_worker
113-
:members:
114-
115-
.. automodule:: cdp.storage
116-
:members:
117-
118-
.. automodule:: cdp.system_info
119-
:members:
120-
121-
.. automodule:: cdp.target
122-
:members:
123-
124-
.. automodule:: cdp.tethering
125-
:members:
126-
127-
.. automodule:: cdp.tracing
128-
:members:
129-
130-
.. automodule:: cdp.web_audio
131-
:members:
132-
133-
.. automodule:: cdp.web_authn
134-
:members:
10+
api/*

docs/develop.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ Note that the verification in this project occurs in two phases:
3535
2. Run the generator.
3636
3. Verify the *generated* code.
3737

38-
We focus on more effort on step 1, because if the generator is correct then the
39-
generated code is correct by definition. The default ``make`` target runs all of
40-
these targets in order, serving as a quick way to verify the entire project.
38+
We focus most of the effort on step 1, because if the generator is correct then
39+
the generated code is correct by definition. The default ``make`` target runs
40+
all of these targets in order, serving as a quick way to verify the entire
41+
project.
4142

4243
To make documentation (i.e. the docs you're reading right now) go into the
4344
``docs/`` directory and run ``make html``.

generator/generate.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,26 +48,6 @@ def indent(s: str, n: int):
4848
return tw_indent(s, n * ' ')
4949

5050

51-
def clear_dirs(package_path: Path):
52-
''' Remove generated code. '''
53-
def rmdir(path):
54-
for subpath in path.iterdir():
55-
if subpath.is_file():
56-
subpath.unlink()
57-
elif subpath.is_dir():
58-
rmdir(subpath)
59-
path.rmdir()
60-
61-
try:
62-
(package_path / '__init__.py').unlink()
63-
except FileNotFoundError:
64-
pass
65-
66-
for subpath in package_path.iterdir():
67-
if subpath.is_dir():
68-
rmdir(subpath)
69-
70-
7151
def inline_doc(description) -> str:
7252
''' Generate an inline doc, e.g. ``#: This type is a ...`` '''
7353
if not description:
@@ -741,6 +721,7 @@ def get_refs(self):
741721
class CdpDomain:
742722
''' A CDP domain contains metadata, types, commands, and events. '''
743723
domain: str
724+
description: typing.Optional[str]
744725
experimental: bool
745726
dependencies: typing.List[str]
746727
types: typing.List[CdpType]
@@ -762,6 +743,7 @@ def from_json(cls, domain: dict):
762743

763744
return cls(
764745
domain_name,
746+
domain.get('description'),
765747
domain.get('experimental', False),
766748
domain.get('dependencies', list()),
767749
[CdpType.from_json(type) for type in types],
@@ -826,6 +808,21 @@ def generate_imports(self):
826808

827809
return code
828810

811+
def generate_sphinx(self) -> str:
812+
'''
813+
Generate a Sphinx document for this domain.
814+
'''
815+
docs = self.domain + '\n'
816+
docs += '=' * len(self.domain) + '\n\n'
817+
818+
if self.description:
819+
docs += f'{self.description}\n\n'
820+
821+
docs += f'.. automodule:: cdp.{self.module}\n'
822+
docs += ' :members:\n'
823+
824+
return docs
825+
829826

830827
def parse(json_path, output_path):
831828
'''
@@ -862,6 +859,22 @@ def generate_init(init_path, domains):
862859
init_file.write('import cdp.{}\n'.format(domain.module))
863860

864861

862+
def generate_docs(docs_path, domains):
863+
'''
864+
Generate Sphinx documents for each domain.
865+
'''
866+
logger.info('Generating Sphinx documents')
867+
868+
# Remove generated documents
869+
for subpath in docs_path.iterdir():
870+
subpath.unlink()
871+
872+
# Generate document for each domain
873+
for domain in domains:
874+
doc = docs_path / f'{domain.module}.rst'
875+
with doc.open('w') as f:
876+
f.write(domain.generate_sphinx())
877+
865878
def main():
866879
''' Main entry point. '''
867880
here = Path(__file__).parent.resolve()
@@ -871,8 +884,13 @@ def main():
871884
]
872885
output_path = here.parent / 'cdp'
873886
output_path.mkdir(exist_ok=True)
874-
clear_dirs(output_path)
875887

888+
# Remove generated code
889+
for subpath in output_path.iterdir():
890+
if subpath.is_file() and subpath.name not in ('py.typed', 'util.py'):
891+
subpath.unlink()
892+
893+
# Parse domains
876894
domains = list()
877895
for json_path in json_paths:
878896
logger.info('Parsing JSON file %s', json_path)
@@ -899,6 +917,9 @@ def main():
899917
init_path = output_path / '__init__.py'
900918
generate_init(init_path, domains)
901919

920+
docs_path = here.parent / 'docs' / 'api'
921+
generate_docs(docs_path, domains)
922+
902923
py_typed_path = output_path / 'py.typed'
903924
py_typed_path.touch()
904925

generator/test_generate.py

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -832,14 +832,78 @@ def test_domain_shadows_builtin():
832832
assert domain.module == 'input_'
833833

834834

835-
def test_domain_shadows_builtin():
836-
''' If a domain name shadows a Python builtin, it should have an underscore
837-
appended to the module name. '''
838-
input_domain = {
839-
"domain": "Input",
840-
"types": [],
841-
"commands": [],
842-
"events": [],
835+
def test_cdp_domain_sphinx():
836+
json_domain = {
837+
"domain": "Animation",
838+
"description": "This is the animation domain.",
839+
"experimental": True,
840+
"dependencies": [
841+
"Runtime",
842+
"DOM"
843+
],
844+
"types": [
845+
{
846+
"id": "KeyframeStyle",
847+
"description": "Keyframe Style",
848+
"type": "object",
849+
"properties": [
850+
{
851+
"name": "offset",
852+
"description": "Keyframe's time offset.",
853+
"type": "string"
854+
},
855+
{
856+
"name": "easing",
857+
"description": "`AnimationEffect`'s timing function.",
858+
"type": "string"
859+
}
860+
]
861+
}
862+
],
863+
"commands": [
864+
{
865+
"name": "getCurrentTime",
866+
"description": "Returns the current time of the an animation.",
867+
"parameters": [
868+
{
869+
"name": "id",
870+
"description": "Id of animation.",
871+
"type": "string"
872+
}
873+
],
874+
"returns": [
875+
{
876+
"name": "currentTime",
877+
"description": "Current time of the page.",
878+
"type": "number"
879+
}
880+
]
881+
},
882+
],
883+
"events": [
884+
{
885+
"name": "animationCanceled",
886+
"description": "Event for when an animation has been cancelled.",
887+
"parameters": [
888+
{
889+
"name": "id",
890+
"description": "Id of the animation that was cancelled.",
891+
"type": "string"
892+
}
893+
]
894+
},
895+
]
843896
}
844-
domain = CdpDomain.from_json(input_domain)
845-
assert domain.module == 'input_'
897+
''' A CDP domain should generate Sphinx documentation. '''
898+
expected = dedent("""\
899+
Animation
900+
=========
901+
902+
This is the animation domain.
903+
904+
.. automodule:: cdp.animation
905+
:members:
906+
""")
907+
domain = CdpDomain.from_json(json_domain)
908+
actual = domain.generate_sphinx()
909+
assert expected == actual

0 commit comments

Comments
 (0)