Skip to content

Commit 06948e9

Browse files
mehaaseBrian Mackintosh
andcommitted
Simplified API
This is inpsired by PR #6 but with a couple changes: 1. Use context vars instead of a singleton instance. 2. Use code generation instead of decoration. This improves type inference for tools like MyPy. 3. Extend the same changes to the connection class. This commit includes the generator, changes to the trio_cdp module to support the simplified API, and updated examples--including an example of running inside Jupyter notebook. The generated code will be added in the next commit to make review easier. Co-authored-by: Brian Mackintosh <bckmackintosh@gmail.com>
1 parent 65fa030 commit 06948e9

9 files changed

Lines changed: 515 additions & 69 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.coverage
22
*.egg-info
3+
.ipynb_checkpoints
34
.mypy_cache
45
.vscode
56
__pycache__

examples/get_title.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
import os
1717
import sys
1818

19-
from cdp import dom, page, target
2019
import trio
21-
from trio_cdp import open_cdp
20+
from trio_cdp import open_cdp, dom, page, target
2221

2322

2423
log_level = os.environ.get('LOG_LEVEL', 'info').upper()
@@ -31,7 +30,7 @@ async def main():
3130
logger.info('Connecting to browser: %s', sys.argv[1])
3231
async with open_cdp(sys.argv[1]) as conn:
3332
logger.info('Listing targets')
34-
targets = await conn.execute(target.get_targets())
33+
targets = await target.get_targets()
3534

3635
for t in targets:
3736
if (t.type == 'page' and
@@ -41,19 +40,18 @@ async def main():
4140
break
4241

4342
logger.info('Attaching to target id=%s', target_id)
44-
session = await conn.open_session(target_id)
45-
46-
logger.info('Navigating to %s', sys.argv[2])
47-
await session.execute(page.enable())
48-
async with session.wait_for(page.LoadEventFired):
49-
await session.execute(page.navigate(sys.argv[2]))
50-
51-
logger.info('Extracting page title')
52-
root_node = await session.execute(dom.get_document())
53-
title_node_id = await session.execute(
54-
dom.query_selector(root_node.node_id, 'title'))
55-
html = await session.execute(dom.get_outer_html(title_node_id))
56-
print(html)
43+
async with conn.open_session(target_id) as session:
44+
45+
logger.info('Navigating to %s', sys.argv[2])
46+
await page.enable()
47+
async with session.wait_for(page.LoadEventFired):
48+
await page.navigate(sys.argv[2])
49+
50+
logger.info('Extracting page title')
51+
root_node = await dom.get_document()
52+
title_node_id = await dom.query_selector(root_node.node_id, 'title')
53+
html = await dom.get_outer_html(title_node_id)
54+
print(html)
5755

5856

5957
if __name__ == '__main__':

examples/notebook.ipynb

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Using Trio CDP Inside Jupyter Notebook\n",
8+
"\n",
9+
"Trio CDP can be used in Jupyter notebook, but some caveats are warranted. First, Trio support in Jupyter is experimental. Read [the instructions](https://github.com/ipython/ipykernel/pull/479) for setting up Trio inside Jupyter.\n",
10+
"\n",
11+
"The second caveat is that Trio CDP uses [context variables](https://docs.python.org/3.7/library/contextvars.html?highlight=contextvar#contextvars.ContextVar) to keep track of which connection and/or session are associated with each task. In Jupyter notebook, however, each cell executes in a separate task, so connections and sessions are not automatically shared between cells. This notebook contains a workaround that allows a single connection and session to be shared across cells."
12+
]
13+
},
14+
{
15+
"cell_type": "code",
16+
"execution_count": 1,
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"from trio_cdp import connect_cdp, dom, page, target"
21+
]
22+
},
23+
{
24+
"cell_type": "code",
25+
"execution_count": 2,
26+
"metadata": {},
27+
"outputs": [],
28+
"source": [
29+
"conn = await connect_cdp(GLOBAL_NURSERY,\n",
30+
" 'ws://127.0.0.1:9000/devtools/browser/f01f56ba-993e-4c83-adc0-10a1feb56449')"
31+
]
32+
},
33+
{
34+
"cell_type": "code",
35+
"execution_count": 3,
36+
"metadata": {},
37+
"outputs": [],
38+
"source": [
39+
"# Here is where we have to do some magic to make the connection from the\n",
40+
"# previous cell available to other cells.\n",
41+
"import trio_cdp.context\n",
42+
"trio_cdp.context.set_global_connection(conn)"
43+
]
44+
},
45+
{
46+
"cell_type": "code",
47+
"execution_count": 4,
48+
"metadata": {},
49+
"outputs": [],
50+
"source": [
51+
"# Now we can run Trio CDP commands and they execute on the connection\n",
52+
"# automatically.\n",
53+
"targets = await target.get_targets()"
54+
]
55+
},
56+
{
57+
"cell_type": "code",
58+
"execution_count": 5,
59+
"metadata": {},
60+
"outputs": [
61+
{
62+
"data": {
63+
"text/plain": [
64+
"[TargetInfo(target_id=TargetID('0AF1752B5834E8ED9F36D7BC3D1AEF51'), type='page', title='Hyperion Gray', url='https://www.hyperiongray.com/', attached=False, opener_id=None, browser_context_id=BrowserContextID('B2A138B23272D6E4920555C2DE424E05')),\n",
65+
" TargetInfo(target_id=TargetID('A0CF2CC043BDF2E4F8F8C0514B9A2FD2'), type='page', title='Hyperion Gray', url='https://www.hyperiongray.com/', attached=False, opener_id=None, browser_context_id=BrowserContextID('B2A138B23272D6E4920555C2DE424E05'))]"
66+
]
67+
},
68+
"execution_count": 5,
69+
"metadata": {},
70+
"output_type": "execute_result"
71+
}
72+
],
73+
"source": [
74+
"targets"
75+
]
76+
},
77+
{
78+
"cell_type": "code",
79+
"execution_count": 6,
80+
"metadata": {},
81+
"outputs": [],
82+
"source": [
83+
"session = await conn.connect_session(targets[0].target_id)"
84+
]
85+
},
86+
{
87+
"cell_type": "code",
88+
"execution_count": 7,
89+
"metadata": {},
90+
"outputs": [],
91+
"source": [
92+
"# We have to do something similar for the session so that it can be\n",
93+
"# reused across multiple cells.\n",
94+
"trio_cdp.context.set_global_session(session)"
95+
]
96+
},
97+
{
98+
"cell_type": "code",
99+
"execution_count": 8,
100+
"metadata": {},
101+
"outputs": [],
102+
"source": [
103+
"async with session.page_enable():\n",
104+
" async with session.wait_for(page.LoadEventFired):\n",
105+
" await page.navigate('https://www.hyperiongray.com')"
106+
]
107+
},
108+
{
109+
"cell_type": "code",
110+
"execution_count": 9,
111+
"metadata": {},
112+
"outputs": [],
113+
"source": [
114+
"doc = await dom.get_document()"
115+
]
116+
},
117+
{
118+
"cell_type": "code",
119+
"execution_count": 10,
120+
"metadata": {},
121+
"outputs": [],
122+
"source": [
123+
"title = await dom.query_selector(doc.node_id, 'title')"
124+
]
125+
},
126+
{
127+
"cell_type": "code",
128+
"execution_count": 11,
129+
"metadata": {},
130+
"outputs": [
131+
{
132+
"data": {
133+
"text/plain": [
134+
"'<title>Hyperion Gray</title>'"
135+
]
136+
},
137+
"execution_count": 11,
138+
"metadata": {},
139+
"output_type": "execute_result"
140+
}
141+
],
142+
"source": [
143+
"await dom.get_outer_html(title)"
144+
]
145+
}
146+
],
147+
"metadata": {
148+
"kernelspec": {
149+
"display_name": "Python 3 Trio",
150+
"language": "python",
151+
"name": "python3-trio"
152+
},
153+
"language_info": {
154+
"codemirror_mode": {
155+
"name": "ipython",
156+
"version": 3
157+
},
158+
"file_extension": ".py",
159+
"mimetype": "text/x-python",
160+
"name": "python",
161+
"nbconvert_exporter": "python",
162+
"pygments_lexer": "ipython3",
163+
"version": "3.7.5"
164+
}
165+
},
166+
"nbformat": 4,
167+
"nbformat_minor": 4
168+
}

examples/screenshot.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@
1717
import os
1818
import sys
1919

20-
from cdp import emulation, page, target
2120
import trio
22-
from trio_cdp import open_cdp
21+
from trio_cdp import open_cdp, emulation, page, target
2322

2423

2524
log_level = os.environ.get('LOG_LEVEL', 'info').upper()
@@ -32,7 +31,7 @@ async def main():
3231
logger.info('Connecting to browser: %s', sys.argv[1])
3332
async with open_cdp(sys.argv[1]) as conn:
3433
logger.info('Listing targets')
35-
targets = await conn.execute(target.get_targets())
34+
targets = await target.get_targets()
3635

3736
for t in targets:
3837
if (t.type == 'page' and
@@ -42,28 +41,26 @@ async def main():
4241
break
4342

4443
logger.info('Attaching to target id=%s', target_id)
45-
session = await conn.open_session(target_id)
46-
47-
logger.info('Setting device emulation')
48-
await session.execute(emulation.set_device_metrics_override(
49-
width=800, height=600, device_scale_factor=1, mobile=False
50-
))
51-
52-
logger.info('Enabling page events')
53-
await session.execute(page.enable())
54-
55-
logger.info('Navigating to %s', sys.argv[2])
56-
async with session.wait_for(page.LoadEventFired):
57-
await session.execute(page.navigate(url=sys.argv[2]))
58-
59-
logger.info('Making a screenshot')
60-
img_data = await session.execute(page.capture_screenshot(
61-
format='png'
62-
))
63-
logger.info('Saving to file')
64-
screenshot_file = await trio.open_file('test.png', 'wb')
65-
async with screenshot_file:
66-
await screenshot_file.write(b64decode(img_data))
44+
async with conn.open_session(target_id) as session:
45+
46+
logger.info('Setting device emulation')
47+
await emulation.set_device_metrics_override(
48+
width=800, height=600, device_scale_factor=1, mobile=False
49+
)
50+
51+
logger.info('Enabling page events')
52+
await page.enable()
53+
54+
logger.info('Navigating to %s', sys.argv[2])
55+
async with session.wait_for(page.LoadEventFired):
56+
await page.navigate(url=sys.argv[2])
57+
58+
logger.info('Making a screenshot')
59+
img_data = await page.capture_screenshot(format='png')
60+
logger.info('Saving to file')
61+
screenshot_file = await trio.open_file('test.png', 'wb')
62+
async with screenshot_file:
63+
await screenshot_file.write(b64decode(img_data))
6764

6865

6966
if __name__ == '__main__':

examples/take_heap_snapshot.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
import os
1616
import sys
1717

18-
from cdp import browser, dom, heap_profiler, page, target
1918
import trio
20-
from trio_cdp import open_cdp
19+
from trio_cdp import open_cdp, browser, dom, heap_profiler, page, target
2120

2221

2322
log_level = os.environ.get('LOG_LEVEL', 'info').upper()
@@ -46,26 +45,26 @@ async def progress_helper():
4645
nursery.start_soon(chunk_helper)
4746
if report_progress:
4847
nursery.start_soon(progress_helper)
49-
await session.execute(heap_profiler.take_heap_snapshot(report_progress))
48+
await heap_profiler.take_heap_snapshot(report_progress)
5049
nursery.cancel_scope.cancel()
5150

5251

5352
async def main():
5453
cdp_uri = sys.argv[1]
5554
async with open_cdp(cdp_uri) as conn:
5655
logger.info('Connecting')
57-
targets = await conn.execute(target.get_targets())
56+
targets = await target.get_targets()
5857
target_id = targets[0].target_id
5958

6059
# First page
6160
logger.info('Attaching to target id=%s', target_id)
62-
session = await conn.open_session(target_id)
61+
async with conn.open_session(target_id) as session:
6362

64-
logger.info('Started heap snapshot')
65-
outfile_path = trio.Path('%s.heapsnapshot' % datetime.today().isoformat())
66-
async with await outfile_path.open('a') as outfile:
67-
logger.info('Started writing heap snapshot')
68-
await _take_heap_snapshot(session, outfile, report_progress=True)
63+
logger.info('Started heap snapshot')
64+
outfile_path = trio.Path('%s.heapsnapshot' % datetime.today().isoformat())
65+
async with await outfile_path.open('a') as outfile:
66+
logger.info('Started writing heap snapshot')
67+
await _take_heap_snapshot(session, outfile, report_progress=True)
6968

7069

7170
if __name__ == '__main__':

0 commit comments

Comments
 (0)