Skip to content

Commit 0edd882

Browse files
committed
Improve library
- Add private tabs - Minor fixes
1 parent c6c755b commit 0edd882

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

pychrome/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
from .tab import *
88
from .exceptions import *
99

10-
__version__ = '0.2.3'
10+
__version__ = '0.2.4'

pychrome/browser.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,33 @@ class Browser(object):
1616

1717
def __init__(self, url="http://127.0.0.1:9222"):
1818
self.dev_url = url
19+
self.context_ids = {}
20+
self._ws_api = None
1921

2022
if self.dev_url not in self._all_tabs:
2123
self._tabs = self._all_tabs[self.dev_url] = {}
2224
else:
2325
self._tabs = self._all_tabs[self.dev_url]
2426

27+
def _get_websocket_url(self):
28+
r = requests.get("%s/json/version" % self.dev_url)
29+
r.raise_for_status()
30+
version_data = r.json()
31+
return version_data['webSocketDebuggerUrl']
32+
33+
@property
34+
def ws_api(self):
35+
"""The main Websocket API connection."""
36+
if not self._ws_api:
37+
#
38+
# This object is just a websocket with event handling, not a new Tab.
39+
#
40+
self._ws_api = Tab(
41+
id='browser', type='browser', webSocketDebuggerUrl=self._get_websocket_url()
42+
)
43+
self._ws_api.start()
44+
return self._ws_api
45+
2546
def new_tab(self, url=None, timeout=None):
2647
url = url or ''
2748
rp = requests.get("%s/json/new?%s" % (self.dev_url, url), json=True, timeout=timeout)
@@ -36,7 +57,8 @@ def list_tab(self, timeout=None):
3657
if tab_json['type'] != 'page': # pragma: no cover
3758
continue
3859

39-
if tab_json['id'] in self._tabs and self._tabs[tab_json['id']].status != Tab.status_stopped:
60+
active_tab = tab_json['id'] in self._tabs
61+
if active_tab and self._tabs[tab_json['id']].status != Tab.status_stopped:
4062
tabs_map[tab_json['id']] = self._tabs[tab_json['id']]
4163
else:
4264
tabs_map[tab_json['id']] = Tab(**tab_json)
@@ -56,6 +78,13 @@ def close_tab(self, tab_id, timeout=None):
5678
tab_id = tab_id.id
5779

5880
tab = self._tabs.pop(tab_id, None)
81+
82+
if tab and tab_id in self.context_ids:
83+
self.ws_api.call_method(
84+
'Target.disposeBrowserContext',
85+
browserContextId=self.context_ids[tab_id]
86+
)
87+
5988
if tab and tab.status == Tab.status_started: # pragma: no cover
6089
tab.stop()
6190

@@ -69,4 +98,37 @@ def version(self, timeout=None):
6998
def __str__(self):
7099
return '<Browser %s>' % self.dev_url
71100

101+
def new_private_tab(self, timeout=None):
102+
"""Create a new tab in a new browser context.
103+
104+
This tab will be isolated from other tabs.
105+
106+
https://chromedevtools.github.io/devtools-protocol/tot/Target/#method-createBrowserContext
107+
108+
:param timeout: The timeout for API calls.
109+
:rtype: Tab
110+
"""
111+
context = self.ws_api.Target.createBrowserContext(_timeout=timeout)
112+
try:
113+
context_id = context['browserContextId']
114+
except KeyError:
115+
raise RuntimeError("Can't create a new private context.")
116+
117+
target = self.ws_api.Target.createTarget(
118+
url='about:blank', browserContextId=context_id, _timeout=timeout
119+
)
120+
121+
self.list_tab(timeout=timeout)
122+
123+
if target['targetId'] in self._tabs:
124+
tab = self._tabs[target['targetId']]
125+
tab.context_id = context_id
126+
self.context_ids[tab.id] = context_id
127+
else:
128+
raise RuntimeError("Failed to create a new private tab: %s" % target['targetId'])
129+
return tab
130+
131+
def __del__(self):
132+
self.ws_api.stop()
133+
72134
__repr__ = __str__

pychrome/tab.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,17 @@
33

44
from __future__ import unicode_literals
55

6-
import os
6+
import functools
77
import json
88
import logging
9-
import warnings
9+
import queue
1010
import threading
11-
import functools
11+
import warnings
1212

1313
import websocket
1414

1515
from .exceptions import *
1616

17-
try:
18-
import Queue as queue
19-
except ImportError:
20-
import queue
21-
22-
2317
__all__ = ["Tab"]
2418

2519

@@ -52,7 +46,6 @@ class Tab(object):
5246
def __init__(self, **kwargs):
5347
self.id = kwargs.get("id")
5448
self.type = kwargs.get("type")
55-
self.debug = os.getenv("DEBUG", False)
5649

5750
self._websocket_url = kwargs.get("webSocketDebuggerUrl")
5851
self._kwargs = kwargs
@@ -73,6 +66,7 @@ def __init__(self, **kwargs):
7366
self.event_handlers = {}
7467
self.method_results = {}
7568
self.event_queue = queue.Queue()
69+
self.context_id = None
7670

7771
def _send(self, message, timeout=None):
7872
if 'id' not in message:
@@ -81,8 +75,7 @@ def _send(self, message, timeout=None):
8175

8276
message_json = json.dumps(message)
8377

84-
if self.debug: # pragma: no cover
85-
print("SEND > %s" % message_json)
78+
logging.debug("SEND > %s" % message_json)
8679

8780
if not isinstance(timeout, (int, float)) or timeout > 1:
8881
q_timeout = 1
@@ -128,8 +121,7 @@ def _recv_loop(self):
128121
self._stopped.set()
129122
return
130123

131-
if self.debug: # pragma: no cover
132-
print('< RECV %s' % message_json)
124+
logging.debug('< RECV %s' % message_json)
133125

134126
if "method" in message:
135127
self.event_queue.put(message)
@@ -174,7 +166,9 @@ def call_method(self, _method, *args, **kwargs):
174166
result = self._send({"method": _method, "params": kwargs}, timeout=timeout)
175167
if 'result' not in result and 'error' in result:
176168
warnings.warn("%s error: %s" % (_method, result['error']['message']))
177-
raise CallMethodException("calling method: %s error: %s" % (_method, result['error']['message']))
169+
raise CallMethodException(
170+
"calling method: %s error: %s" % (_method, result['error']['message'])
171+
)
178172

179173
return result['result']
180174

setup.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[bdist_wheel]
2-
universal = 1
2+
universal = 1
3+
4+
[flake8]
5+
max-line-length = 100

0 commit comments

Comments
 (0)