Source code for woob.browser.switch
# Copyright(C) 2010-2017 Vincent Ardisson
#
# This file is part of woob.
#
# woob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# woob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with woob. If not, see <http://www.gnu.org/licenses/>.
from functools import wraps
[docs]class SiteSwitch(Exception):
"""Exception to raise to switch to another Browser."""
def __init__(self, name):
"""
:param name: key of the `SwitchingBrowser.BROWSERS` dict to indicate
the new browser class to use
:type name: str
"""
super(SiteSwitch, self).__init__('Switching to site %s' % name)
self.name = name
[docs]class SwitchingBrowser:
"""Proxy browser to use multiple (exclusive) browsers.
When some sites have mutually exclusive sub-sites, it may be better to
split a browser in multiple browsers. If it's not possible to know in
advance what browser should be used, the SwitchingBrowser can help.
Multiple browsers should be configured in the `BROWSERS` attribute as
a dict. When first used, SwitchingBrowser will instanciate the browser
class with the `'main'` key and proxy all method calls to it.
If that browser raises :class:`SiteSwitch` exception, another browser
(associated to the exception key parameter) will be instanciated and will
be used to retry the call which failed.
"""
BROWSERS = None
"""dict association keys to browser classes.
It should contain a `'main'` key for the first browser class to use.
"""
KEEP_SESSION = False
"""Whether to pass the :class:`requests.session.Session` between browsers.
"""
KEEP_ATTRS = ()
"""Pass the values stored in __states__
"""
def __init__(self, *args, **kwargs):
super(SwitchingBrowser, self).__init__()
self._browser_args = args
self._browser_kwargs = kwargs
self._browser = None
self.set_browser('main')
[docs] def set_browser(self, name):
klass = self.BROWSERS[name]
obj = klass(*self._browser_args, **self._browser_kwargs)
if self._browser is not None:
for attrname in self.KEEP_ATTRS:
if hasattr(self._browser, attrname):
setattr(obj, attrname, getattr(self._browser, attrname))
if self.KEEP_SESSION:
obj.session = self._browser.session
else:
self._browser.session.close()
self._browser = obj
self._browser.logger.info('using %r browser', name)
def __getattr__(self, attr):
val = getattr(self._browser, attr)
if not callable(val):
return val
@wraps(val)
def wrapper(*args, **kwargs):
try:
return val(*args, **kwargs)
except SiteSwitch as e:
self.set_browser(e.name)
val2 = getattr(self._browser, attr)
return val2(*args, **kwargs)
return wrapper
[docs]class SwitchingBrowserWithState(SwitchingBrowser):
"""Use state to transmit knowledge of last browser used during a previous sync to later start on the same browser"""
[docs] def set_browser(self, name):
super(SwitchingBrowserWithState, self).set_browser(name)
self.last_browser = name
[docs] def load_state(self, state):
"""Get the last used browser from the state, if any"""
self.set_browser(name=state.get('last_browser', 'main'))
self._browser.load_state(state)
[docs] def dump_state(self):
"""Store the last used browser in the state"""
ret = self._browser.dump_state()
ret['last_browser'] = self.last_browser
return ret