Skip to content

Added WordPress Authentication Plugin#1954

Merged
liiight merged 15 commits intoFlexget:developfrom
transverberate:add_wp_auth
Oct 19, 2017
Merged

Added WordPress Authentication Plugin#1954
liiight merged 15 commits intoFlexget:developfrom
transverberate:add_wp_auth

Conversation

@transverberate
Copy link
Copy Markdown
Contributor

@transverberate transverberate commented Aug 25, 2017

Motivation for changes:

I wanted to access an rss feed that required WordPress Authentication. WordPress uses cookies for authentication and they expire quickly. I was sick of using the headers plugin and having to update it every couple days. WordPress cookies are obtained through a POST request to a wp-login.php page with data of the form:

log=some_username
pwd=some_password
wp-submit='Log In'
redirect_to='wp_admin.php'

WordPress will return cookies for authentication in the form:

example.org	FALSE	/	TRUE		wordpress_logged_in_[some_hash]	[username][some_hash]
example.org	FALSE	/	TRUE		wordpress_test_cookie	WP+Cookie+check
example.org	FALSE	/wp-admin	TRUE		wordpress_sec_[some_hash]	[username][some_hash]
example.org	FALSE	/wp-content/plugins	TRUE		wordpress_sec_[some_hash]	[username][some_hash]

Detailed changes:

  • Added WordPress Plugin
    • On task start the wordpress_auth plugin sends a request to the WordPress login page with the URL, username, and password specified in the config
    • Authentication cookies are collected from the response
    • The cookies are validated to see if they contain recognized WordPress authentication information
    • The collected cookies are added to task.requests
  • Added WordPress Plugin Test
    • Test that confirms task aborts if the WordPress login URL is unreachable
    • Test that confirms task aborts if the WordPress login response status is NOT okay (>300)
    • Test that confirms task aborts if there are no recognized WordPress cookies in the response
    • Test that confirms that cookies are collected when the WordPress response contains redirects

Addressed issues:

Added new plugin wordpress_auth

Implemented feature requests:

Config usage if relevant (new plugin or updated schema):

tasks:
  sometask:
    wordpress_auth:
      url: http://example.org/wp-login.php
      username: johndoe
      password: qwerty

Log and/or tests output (preferably both):

PyCharm says all the tests pass or are skipped.
I don't know what all the errors in the log are.

Testing started at 1:12 AM ...
Launching py.test with arguments C:/workspace/python/Flexget/flexget/tests in C:\workspace\python\Flexget

============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-3.1.3, py-1.4.34, pluggy-0.4.0
rootdir: C:\workspace\python\Flexget, inifile: setup.cfg
plugins: capturelog-0.7
collected 1248 items
flexget\tests\test_abort.py 
flexget\tests\test_abort_if_exists.py 
flexget\tests\test_archives.py 
flexget\tests\test_assume_quality.py 
flexget\tests\test_backlog.py 
flexget\tests\test_cached_input.py 
flexget\tests\test_config.py 
flexget\tests\test_config_schema.py 
flexget\tests\test_configure_series_betaseries_list.py 
flexget\tests\test_content_filter.py 
flexget\tests\test_cookies.py 
flexget\tests\test_couchpotato_list.py 
flexget\tests\test_crossmatch.py 
flexget\tests\test_decompress.py 
flexget\tests\test_delay.py 
flexget\tests\test_digest.py 
flexget\tests\test_discover.py 
flexget\tests\test_download.py 
flexget\tests\test_entry_list.py 
flexget\tests\test_exists_movie.py 
flexget\tests\test_exists_series.py 
flexget\tests\test_feed_control.py 
flexget\tests\test_filesystem.py 
flexget\tests\test_html5lib.py 
flexget\tests\test_imdb.py 
flexget\tests\test_imdb_list_interface.py 
flexget\tests\test_imdb_parser.py 
flexget\tests\test_include.py 
flexget\tests\test_input_sites.py 
flexget\tests\test_inputs.py 
flexget\tests\test_lazy_fields.py 
flexget\tests\test_limit_new.py 
flexget\tests\test_list_interface.py 
flexget\tests\test_manipulate.py 
flexget\tests\test_metainfo.py 
flexget\tests\test_migrate.py 
flexget\tests\test_misc.py 
flexget\tests\test_movie_list.py 
flexget\tests\test_movieparser.py 
flexget\tests\test_myepisodes.py 
flexget\tests\test_next_series_episodes.py 
flexget\tests\test_nfo_lookup.py 
flexget\tests\test_npo_watchlist.py 
flexget\tests\test_parsingapi.py 
flexget\tests\test_path_by_space.py 
flexget\tests\test_pending_approval.py 
flexget\tests\test_plugin_interfaces.py 
flexget\tests\test_pluginapi.py 
flexget\tests\test_qualities.py 
flexget\tests\test_regex_extract.py 
flexget\tests\test_regexp.py 
flexget\tests\test_regexp_list.py 
flexget\tests\test_reorder_quality.py 
flexget\tests\test_rottentomatoes.py 
flexget\tests\test_rtorrent.py 
flexget\tests\test_seen.py 
flexget\tests\test_series.py 
flexget\tests\test_series_premiere.py 
flexget\tests\test_seriesparser.py 
flexget\tests\test_simple_persistence.py 
flexget\tests\test_sns.py 
flexget\tests\test_sort_by.py 
flexget\tests\test_subtitle_list.py 
flexget\tests\test_symlink.py 
flexget\tests\test_t411.py 
flexget\tests\test_task.py 
flexget\tests\test_template.py 
flexget\tests\test_thetvdb.py 
flexget\tests\test_thetvdb_list.py 
flexget\tests\test_tmdb.py 
flexget\tests\test_torrent.py 
flexget\tests\test_trakt.py 
flexget\tests\test_trakt_list_interface.py 
flexget\tests\test_tvmaze.py 
flexget\tests\test_unique.py 
flexget\tests\test_urlfix.py 
flexget\tests\test_urlrewriting.py 
flexget\tests\test_utils.py 
flexget\tests\test_validator.py 
flexget\tests\test_variables.py 
flexget\tests\test_wordpress.py 
flexget\tests\api_tests\test_api_validator.py 
flexget\tests\notifiers\test_notify_entry.py 
flexget\tests\notifiers\test_notify_task.py 

============================== warnings summary ===============================
None
  [pytest] section in setup.cfg files is deprecated, use [tool:pytest] instead.

:171
  'pytest_runtest_makereport' hook uses deprecated __multicall__ argument

None
  pytest_funcarg__caplog: declaring fixtures using "pytest_funcarg__" prefix is deprecated and scheduled to be removed in pytest 4.0.  Please remove the prefix and use the @pytest.fixture decorator instead.
  pytest_funcarg__capturelog: declaring fixtures using "pytest_funcarg__" prefix is deprecated and scheduled to be removed in pytest 4.0.  Please remove the prefix and use the @pytest.fixture decorator instead.

-- Docs: http://doc.pytest.org/en/latest/warnings.html
= 1205 passed, 38 skipped, 3 xfailed, 2 xpassed, 4 warnings in 361.91 seconds =...s
Skipped: rarfile module required
...
flexget\tests\test_argparse.py ..........................
flexget\tests\test_condition.py ..............................
flexget\tests\test_content_size.py ...................s
Skipped: rarfile module required
s
Skipped: rarfile module required
...couldn't remove C:\Users\TRANSV~1\AppData\Local\Temp\pytest-of-Transverberate\pytest-6\test_delete_zip0\test_zip.zip: [WinError 2] The system cannot find the file specified: Path('C:\\Users\\TRANSV~1\\AppData\\Local\\Temp\\pytest-of-Transverberate\\pytest-6\\test_delete_zip0\\test_zip.zip')
......................s
Skipped: Windows does not have a guaranteed "private" directory afaik
s
Skipped: TODO: These are really just config validation tests, and I have config validation turned off at the moment for unit tests due to some problems
s
Skipped: TODO: These are really just config validation tests, and I have config validation turned off at the moment for unit tests due to some problems
s
Skipped: TODO: These are really just config validation tests, and I have config validation turned off at the moment for unit tests due to some problems
s
Skipped: TODO: These are really just config validation tests, and I have config validation turned off at the moment for unit tests due to some problems
.
flexget\tests\test_exec.py ....s
Skipped: This doesn't work on linux
..............s
Skipped: test is broken
s
Skipped: test is broken
s
Skipped: test is broken
s
Skipped: test is broken
s
Skipped: test is broken
s
Skipped: test is broken
..................s
Skipped: 1.2 we need to test this with execute command
..
flexget\tests\test_headers.py ..........................s
Skipped: It rarely works
......s
Skipped: Missing a usable urlrewriter for uploadgig?
...........................................s
Skipped: FAILS - DISABLED
.....................s
Skipped: Test myepisodes (DISABLED) -- account locked?
.................................................
flexget\tests\test_next_series_seasons.py ..........................................
flexget\tests\test_only_new.py ....
flexget\tests\test_pathscrub.py ..................
flexget\tests\test_proper_movies.py ..............................................................................................................................................................
flexget\tests\test_remember_rejected.py ...x
self = <LazyLookup([<bound method MetainfoQuality.get_quality of <flexget.plugins.metainfo.quality.MetainfoQuality object at 0x08843A10>>])>
key = 'rt_name'

    def __getitem__(self, key):
        from flexget.plugin import PluginError
        while self.store.is_lazy(key):
            index = next((i for i, keys in enumerate(self.key_list) if key in keys), None)
            if index is None:
                # All lazy lookup functions for this key were tried unsuccessfully
                return None
            func = self.func_list.pop(index)
            self.key_list.pop(index)
            try:
>               func(self.store)

..\utils\lazy_dict.py:37: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.plugins.metainfo.rottentomatoes_lookup.PluginRottenTomatoesLookup object at 0x087CF5D0>
entry = <Entry(title=[Group] Taken 720p,state=undecided)>

    def lazy_loader(self, entry):
        """Does the lookup for this entry and populates the entry fields.
    
            :param entry: entry to perform lookup on
            :param field: the field to be populated (others may be populated as well)
            :returns: the field value
    
            """
        try:
>           self.lookup(entry, key=self.key)

..\plugins\metainfo\rottentomatoes_lookup.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.plugins.metainfo.rottentomatoes_lookup.PluginRottenTomatoesLookup object at 0x087CF5D0>
entry = <Entry(title=[Group] Taken 720p,state=undecided)>, search_allowed = True
key = 'rh8chjzp8vu6gnpwj88736uv'

    def lookup(self, entry, search_allowed=True, key=None):
        """
            Perform Rotten Tomatoes lookup for entry.
    
            :param entry: Entry instance
            :param search_allowed: Allow fallback to search
            :param key: optionally specify an API key to use
            :raises PluginError: Failure reason
            """
        if not key:
            key = self.key or API_KEY
        movie = lookup_movie(smart_match=entry['title'],
                             rottentomatoes_id=entry.get('rt_id', eval_lazy=False),
                             only_cached=(not search_allowed),
>                            api_key=key
                             )

..\plugins\metainfo\rottentomatoes_lookup.py:96: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = ()
kwargs = {'api_key': 'rh8chjzp8vu6gnpwj88736uv', 'only_cached': False, 'rottentomatoes_id': 770680780, 'smart_match': '[Group] Taken 720p'}

    def wrapped_func(*args, **kwargs):
        try:
>           return func(*args, **kwargs)

..\plugin.py:118: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = ()
kwargs = {'api_key': 'rh8chjzp8vu6gnpwj88736uv', 'only_cached': False, 'rottentomatoes_id': 770680780, 'session': <sqlalchemy.orm.session.ContextSession object at 0x09BC98D0>, ...}
session = <sqlalchemy.orm.session.ContextSession object at 0x09BC98D0>

    def wrapper(*args, **kwargs):
        if kwargs.get('session'):
            return func(*args, **kwargs)
        with _Session() as session:
            kwargs['session'] = session
>           return func(*args, **kwargs)

..\utils\database.py:34: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

title = 'Taken', year = None, rottentomatoes_id = 770680780
smart_match = '[Group] Taken 720p', only_cached = False
session = <sqlalchemy.orm.session.ContextSession object at 0x09BC98D0>
api_key = 'rh8chjzp8vu6gnpwj88736uv'

    @internet(log)
    @with_session
    def lookup_movie(title=None, year=None, rottentomatoes_id=None, smart_match=None,
                     only_cached=False, session=None, api_key=None):
        """
        Do a lookup from Rotten Tomatoes for the movie matching the passed arguments.
        Any combination of criteria can be passed, the most specific criteria specified will be used.
    
        :param rottentomatoes_id: rottentomatoes_id of desired movie
        :param string title: title of desired movie
        :param year: release year of desired movie
        :param smart_match: attempt to clean and parse title and year from a string
        :param only_cached: if this is specified, an online lookup will not occur if the movie is not in the cache
        :param session: optionally specify a session to use, if specified, returned Movie will be live in that session
        :param api_key: optionaly specify an API key to use
        :returns: The Movie object populated with data from Rotten Tomatoes
        :raises: PluginError if a match cannot be found or there are other problems with the lookup
    
        """
    
        if smart_match:
            # If smart_match was specified, and we don't have more specific criteria, parse it into a title and year
            title_parser = get_plugin_by_name('parsing').instance.parse_movie(smart_match)
            title = title_parser.name
            year = title_parser.year
            if title == '' and not (rottentomatoes_id or title):
                raise PluginError('Failed to parse name from %s' % smart_match)
    
        if title:
            search_string = title.lower()
            if year:
                search_string = '%s %s' % (search_string, year)
        elif not rottentomatoes_id:
            raise PluginError('No criteria specified for rotten tomatoes lookup')
    
        def id_str():
            return '<title=%s,year=%s,rottentomatoes_id=%s>' % (title, year, rottentomatoes_id)
    
        log.debug('Looking up rotten tomatoes information for %s' % id_str())
    
        movie = None
    
        # Try to lookup from cache
        if rottentomatoes_id:
            movie = session.query(RottenTomatoesMovie). \
                filter(RottenTomatoesMovie.id == rottentomatoes_id).first()
        if not movie and title:
            movie_filter = session.query(RottenTomatoesMovie).filter(func.lower(RottenTomatoesMovie.title) == title.lower())
            if year:
                movie_filter = movie_filter.filter(RottenTomatoesMovie.year == year)
            movie = movie_filter.first()
            if not movie:
                log.debug('No matches in movie cache found, checking search cache.')
                found = session.query(RottenTomatoesSearchResult). \
                    filter(func.lower(RottenTomatoesSearchResult.search) == search_string).first()
                if found and found.movie:
                    log.debug('Movie found in search cache.')
                    movie = found.movie
        if movie:
            # Movie found in cache, check if cache has expired.
            if movie.expired and not only_cached:
                log.debug('Cache has expired for %s, attempting to refresh from Rotten Tomatoes.' % id_str())
                try:
                    result = movies_info(movie.id, api_key)
                    movie = _set_movie_details(movie, session, result, api_key)
                    session.merge(movie)
                except URLError:
                    log.error('Error refreshing movie details from Rotten Tomatoes, cached info being used.')
            else:
                log.debug('Movie %s information restored from cache.' % id_str())
        else:
            if only_cached:
                raise PluginError('Movie %s not found from cache' % id_str())
            # There was no movie found in the cache, do a lookup from Rotten Tomatoes
            log.debug('Movie %s not found in cache, looking up from rotten tomatoes.' % id_str())
            try:
                if not movie and rottentomatoes_id:
>                   result = movies_info(rottentomatoes_id, api_key)

..\plugins\internal\api_rottentomatoes.py:313: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

id = 770680780, api_key = 'rh8chjzp8vu6gnpwj88736uv'

    def movies_info(id, api_key=None):
        if not api_key:
            api_key = API_KEY
        url = '%s/%s/movies/%s.json?apikey=%s' % (SERVER, API_VER, id, api_key)
>       result = get_json(url)

..\plugins\internal\api_rottentomatoes.py:480: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

url = 'http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv'

    def get_json(url):
        try:
            log.debug('fetching json at %s' % url)
>           data = session.get(url)

..\plugins\internal\api_rottentomatoes.py:528: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.utils.requests.Session object at 0x0829E510>
url = 'http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv'
kwargs = {'allow_redirects': True}

    def get(self, url, **kwargs):
        r"""Sends a GET request. Returns :class:`Response` object.
    
            :param url: URL for the new :class:`Request` object.
            :param \*\*kwargs: Optional arguments that ``request`` takes.
            :rtype: requests.Response
            """
    
        kwargs.setdefault('allow_redirects', True)
>       return self.request('GET', url, **kwargs)

C:\Python\lib\site-packages\requests\sessions.py:521: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.utils.requests.Session object at 0x0829E510>, method = 'GET'
url = 'http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv'
args = (), kwargs = {'allow_redirects': True, 'timeout': 30}
raise_status = True

    def request(self, method, url, *args, **kwargs):
        """
            Does a request, but raises Timeout immediately if site is known to timeout, and records sites that timeout.
            Also raises errors getting the content by default.
    
            :param bool raise_status: If True, non-success status code responses will be raised as errors (True by default)
            """
    
        # Raise Timeout right away if site is known to timeout
        if is_unresponsive(url):
            raise requests.Timeout('Requests to this site (%s) have timed out recently. Waiting before trying again.' %
                                   urlparse(url).hostname)
    
        # Run domain limiters for this url
        limit_domains(url, self.domain_limiters)
    
        kwargs.setdefault('timeout', self.timeout)
        raise_status = kwargs.pop('raise_status', True)
    
        # If we do not have an adapter for this url, pass it off to urllib
        if not any(url.startswith(adapter) for adapter in self.adapters):
            log.debug('No adaptor, passing off to urllib')
            return _wrap_urlopen(url, timeout=kwargs['timeout'])
    
        try:
            log.debug('Fetching URL %s with args %s and kwargs %s', url, args, kwargs)
>           result = super(Session, self).request(method, url, *args, **kwargs)

..\utils\requests.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.utils.requests.Session object at 0x0829E510>, method = 'GET'
url = 'http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv'
params = None, data = None, headers = None, cookies = None, files = None
auth = None, timeout = 30, allow_redirects = True, proxies = {}, hooks = None
stream = None, verify = None, cert = None, json = None

    def request(self, method, url,
            params=None, data=None, headers=None, cookies=None, files=None,
            auth=None, timeout=None, allow_redirects=True, proxies=None,
            hooks=None, stream=None, verify=None, cert=None, json=None):
        """Constructs a :class:`Request <Request>`, prepares it and sends it.
            Returns :class:`Response <Response>` object.
    
            :param method: method for the new :class:`Request` object.
            :param url: URL for the new :class:`Request` object.
            :param params: (optional) Dictionary or bytes to be sent in the query
                string for the :class:`Request`.
            :param data: (optional) Dictionary, bytes, or file-like object to send
                in the body of the :class:`Request`.
            :param json: (optional) json to send in the body of the
                :class:`Request`.
            :param headers: (optional) Dictionary of HTTP Headers to send with the
                :class:`Request`.
            :param cookies: (optional) Dict or CookieJar object to send with the
                :class:`Request`.
            :param files: (optional) Dictionary of ``'filename': file-like-objects``
                for multipart encoding upload.
            :param auth: (optional) Auth tuple or callable to enable
                Basic/Digest/Custom HTTP Auth.
            :param timeout: (optional) How long to wait for the server to send
                data before giving up, as a float, or a :ref:`(connect timeout,
                read timeout) <timeouts>` tuple.
            :type timeout: float or tuple
            :param allow_redirects: (optional) Set to True by default.
            :type allow_redirects: bool
            :param proxies: (optional) Dictionary mapping protocol or protocol and
                hostname to the URL of the proxy.
            :param stream: (optional) whether to immediately download the response
                content. Defaults to ``False``.
            :param verify: (optional) Either a boolean, in which case it controls whether we verify
                the server's TLS certificate, or a string, in which case it must be a path
                to a CA bundle to use. Defaults to ``True``.
            :param cert: (optional) if String, path to ssl client cert file (.pem).
                If Tuple, ('cert', 'key') pair.
            :rtype: requests.Response
            """
        # Create the Request.
        req = Request(
            method=method.upper(),
            url=url,
            headers=headers,
            files=files,
            data=data or {},
            json=json,
            params=params or {},
            auth=auth,
            cookies=cookies,
            hooks=hooks,
        )
        prep = self.prepare_request(req)
    
        proxies = proxies or {}
    
        settings = self.merge_environment_settings(
            prep.url, proxies, stream, verify, cert
        )
    
        # Send the request.
        send_kwargs = {
            'timeout': timeout,
            'allow_redirects': allow_redirects,
        }
        send_kwargs.update(settings)
>       resp = self.send(prep, **send_kwargs)

C:\Python\lib\site-packages\requests\sessions.py:508: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.utils.requests.Session object at 0x0829E510>
request = <PreparedRequest [GET]>
kwargs = {'cert': None, 'proxies': OrderedDict(), 'stream': True, 'timeout': 30, ...}
allow_redirects = True, stream = True, hooks = {'response': []}
adapter = <requests.adapters.HTTPAdapter object at 0x0829EC10>
start = 165.79319181500418

    def send(self, request, **kwargs):
        """Send a given PreparedRequest.
    
            :rtype: requests.Response
            """
        # Set defaults that the hooks can utilize to ensure they always have
        # the correct parameters to reproduce the previous request.
        kwargs.setdefault('stream', self.stream)
        kwargs.setdefault('verify', self.verify)
        kwargs.setdefault('cert', self.cert)
        kwargs.setdefault('proxies', self.proxies)
    
        # It's possible that users might accidentally send a Request object.
        # Guard against that specific failure case.
        if isinstance(request, Request):
            raise ValueError('You can only send PreparedRequests.')
    
        # Set up variables needed for resolve_redirects and dispatching of hooks
        allow_redirects = kwargs.pop('allow_redirects', True)
        stream = kwargs.get('stream')
        hooks = request.hooks
    
        # Get the appropriate adapter to use
        adapter = self.get_adapter(url=request.url)
    
        # Start time (approximately) of the request
        start = preferred_clock()
    
        # Send the request
>       r = adapter.send(request, **kwargs)

C:\Python\lib\site-packages\requests\sessions.py:618: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <requests.adapters.HTTPAdapter object at 0x0829EC10>
request = <PreparedRequest [GET]>, stream = True
timeout = <urllib3.util.timeout.Timeout object at 0x0136EF10>, verify = True
cert = None, proxies = OrderedDict()

    def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
        """Sends PreparedRequest object. Returns Response object.
    
            :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
            :param stream: (optional) Whether to stream the request content.
            :param timeout: (optional) How long to wait for the server to send
                data before giving up, as a float, or a :ref:`(connect timeout,
                read timeout) <timeouts>` tuple.
            :type timeout: float or tuple or urllib3 Timeout object
            :param verify: (optional) Either a boolean, in which case it controls whether
                we verify the server's TLS certificate, or a string, in which case it
                must be a path to a CA bundle to use
            :param cert: (optional) Any user-provided SSL certificate to be trusted.
            :param proxies: (optional) The proxies dictionary to apply to the request.
            :rtype: requests.Response
            """
    
        conn = self.get_connection(request.url, proxies)
    
        self.cert_verify(conn, request.url, verify, cert)
        url = self.request_url(request, proxies)
        self.add_headers(request)
    
        chunked = not (request.body is None or 'Content-Length' in request.headers)
    
        if isinstance(timeout, tuple):
            try:
                connect, read = timeout
                timeout = TimeoutSauce(connect=connect, read=read)
            except ValueError as e:
                # this may raise a string formatting error.
                err = ("Invalid timeout {0}. Pass a (connect, read) "
                       "timeout tuple, or a single float to set "
                       "both timeouts to the same value".format(timeout))
                raise ValueError(err)
        elif isinstance(timeout, TimeoutSauce):
            pass
        else:
            timeout = TimeoutSauce(connect=timeout, read=timeout)
    
        try:
            if not chunked:
                resp = conn.urlopen(
                    method=request.method,
                    url=url,
                    body=request.body,
                    headers=request.headers,
                    redirect=False,
                    assert_same_host=False,
                    preload_content=False,
                    decode_content=False,
                    retries=self.max_retries,
>                   timeout=timeout
                )

C:\Python\lib\site-packages\requests\adapters.py:440: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <urllib3.connectionpool.HTTPConnectionPool object at 0x08B2EB30>
method = 'GET'
url = '/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv'
body = None
headers = {'User-Agent': 'FlexGet/2.10.83.dev (www.flexget.com)', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
retries = Retry(total=1, connect=None, read=None, redirect=0, status=None)
redirect = False, assert_same_host = False
timeout = <urllib3.util.timeout.Timeout object at 0x0136EF10>
pool_timeout = None, release_conn = False, chunked = False, body_pos = None
response_kw = {'decode_content': False, 'preload_content': False}, conn = None
release_this_conn = True, err = None, clean_exit = False
timeout_obj = <urllib3.util.timeout.Timeout object at 0x01399270>
is_new_proxy_conn = False

    def urlopen(self, method, url, body=None, headers=None, retries=None,
                redirect=True, assert_same_host=True, timeout=_Default,
                pool_timeout=None, release_conn=None, chunked=False,
                body_pos=None, **response_kw):
        """
            Get a connection from the pool and perform an HTTP request. This is the
            lowest level call for making a request, so you'll need to specify all
            the raw details.
    
            .. note::
    
               More commonly, it's appropriate to use a convenience method provided
               by :class:`.RequestMethods`, such as :meth:`request`.
    
            .. note::
    
               `release_conn` will only behave as expected if
               `preload_content=False` because we want to make
               `preload_content=False` the default behaviour someday soon without
               breaking backwards compatibility.
    
            :param method:
                HTTP request method (such as GET, POST, PUT, etc.)
    
            :param body:
                Data to send in the request body (useful for creating
                POST requests, see HTTPConnectionPool.post_url for
                more convenience).
    
            :param headers:
                Dictionary of custom headers to send, such as User-Agent,
                If-None-Match, etc. If None, pool headers are used. If provided,
                these headers completely replace any pool-specific headers.
    
            :param retries:
                Configure the number of retries to allow before raising a
                :class:`~urllib3.exceptions.MaxRetryError` exception.
    
                Pass ``None`` to retry until you receive a response. Pass a
                :class:`~urllib3.util.retry.Retry` object for fine-grained control
                over different types of retries.
                Pass an integer number to retry connection errors that many times,
                but no other types of errors. Pass zero to never retry.
    
                If ``False``, then retries are disabled and any exception is raised
                immediately. Also, instead of raising a MaxRetryError on redirects,
                the redirect response will be returned.
    
            :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.
    
            :param redirect:
                If True, automatically handle redirects (status codes 301, 302,
                303, 307, 308). Each redirect counts as a retry. Disabling retries
                will disable redirect, too.
    
            :param assert_same_host:
                If ``True``, will make sure that the host of the pool requests is
                consistent else will raise HostChangedError. When False, you can
                use the pool on an HTTP proxy and request foreign hosts.
    
            :param timeout:
                If specified, overrides the default timeout for this one
                request. It may be a float (in seconds) or an instance of
                :class:`urllib3.util.Timeout`.
    
            :param pool_timeout:
                If set and the pool is set to block=True, then this method will
                block for ``pool_timeout`` seconds and raise EmptyPoolError if no
                connection is available within the time period.
    
            :param release_conn:
                If False, then the urlopen call will not release the connection
                back into the pool once a response is received (but will release if
                you read the entire contents of the response such as when
                `preload_content=True`). This is useful if you're not preloading
                the response's content immediately. You will need to call
                ``r.release_conn()`` on the response ``r`` to return the connection
                back into the pool. If None, it takes the value of
                ``response_kw.get('preload_content', True)``.
    
            :param chunked:
                If True, urllib3 will send the body using chunked transfer
                encoding. Otherwise, urllib3 will send the body using the standard
                content-length form. Defaults to False.
    
            :param int body_pos:
                Position to seek to in file-like body in the event of a retry or
                redirect. Typically this won't need to be set because urllib3 will
                auto-populate the value when needed.
    
            :param \\**response_kw:
                Additional parameters are passed to
                :meth:`urllib3.response.HTTPResponse.from_httplib`
            """
        if headers is None:
            headers = self.headers
    
        if not isinstance(retries, Retry):
            retries = Retry.from_int(retries, redirect=redirect, default=self.retries)
    
        if release_conn is None:
            release_conn = response_kw.get('preload_content', True)
    
        # Check host
        if assert_same_host and not self.is_same_host(url):
            raise HostChangedError(self, url, retries)
    
        conn = None
    
        # Track whether `conn` needs to be released before
        # returning/raising/recursing. Update this variable if necessary, and
        # leave `release_conn` constant throughout the function. That way, if
        # the function recurses, the original value of `release_conn` will be
        # passed down into the recursive call, and its value will be respected.
        #
        # See issue #651 [1] for details.
        #
        # [1] <https://github.com/shazow/urllib3/issues/651>
        release_this_conn = release_conn
    
        # Merge the proxy headers. Only do this in HTTP. We have to copy the
        # headers dict so we can safely change it without those changes being
        # reflected in anyone else's copy.
        if self.scheme == 'http':
            headers = headers.copy()
            headers.update(self.proxy_headers)
    
        # Must keep the exception bound to a separate variable or else Python 3
        # complains about UnboundLocalError.
        err = None
    
        # Keep track of whether we cleanly exited the except block. This
        # ensures we do proper cleanup in finally.
        clean_exit = False
    
        # Rewind body position, if needed. Record current position
        # for future rewinds in the event of a redirect/retry.
        body_pos = set_file_position(body, body_pos)
    
        try:
            # Request a connection from the queue.
            timeout_obj = self._get_timeout(timeout)
            conn = self._get_conn(timeout=pool_timeout)
    
            conn.timeout = timeout_obj.connect_timeout
    
            is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None)
            if is_new_proxy_conn:
                self._prepare_proxy(conn)
    
            # Make the request on the httplib connection object.
            httplib_response = self._make_request(conn, method, url,
                                                  timeout=timeout_obj,
                                                  body=body, headers=headers,
>                                                 chunked=chunked)

C:\Python\lib\site-packages\urllib3\connectionpool.py:601: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <urllib3.connectionpool.HTTPConnectionPool object at 0x08B2EB30>
conn = <vcr.patch.VCRRequestsHTTPConnectionC:\workspace\python\Flexget\flexget\tests\cassettes\test_rottentomatoes.TestRottenTomatoesLookup.test_rottentomatoes_lookup object at 0x01399850>
method = 'GET'
url = '/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv'
timeout = <urllib3.util.timeout.Timeout object at 0x01399270>, chunked = False
httplib_request_kw = {'body': None, 'headers': {'User-Agent': 'FlexGet/2.10.83.dev (www.flexget.com)', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}}
timeout_obj = <urllib3.util.timeout.Timeout object at 0x083992B0>
read_timeout = 30

    def _make_request(self, conn, method, url, timeout=_Default, chunked=False,
                      **httplib_request_kw):
        """
            Perform a request on a given urllib connection object taken from our
            pool.
    
            :param conn:
                a connection from one of our connection pools
    
            :param timeout:
                Socket timeout in seconds for the request. This can be a
                float or integer, which will set the same timeout value for
                the socket connect and the socket read, or an instance of
                :class:`urllib3.util.Timeout`, which gives you more fine-grained
                control over your timeouts.
            """
        self.num_requests += 1
    
        timeout_obj = self._get_timeout(timeout)
        timeout_obj.start_connect()
        conn.timeout = timeout_obj.connect_timeout
    
        # Trigger any extra validation we need to do.
        try:
            self._validate_conn(conn)
        except (SocketTimeout, BaseSSLError) as e:
            # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout.
            self._raise_timeout(err=e, url=url, timeout_value=conn.timeout)
            raise
    
        # conn.request() calls httplib.*.request, not the method in
        # urllib3.request. It also calls makefile (recv) on the socket.
        if chunked:
            conn.request_chunked(method, url, **httplib_request_kw)
        else:
            conn.request(method, url, **httplib_request_kw)
    
        # Reset the timeout for the recv() on the socket
        read_timeout = timeout_obj.read_timeout
    
        # App Engine doesn't have a sock attr
        if getattr(conn, 'sock', None):
            # In Python 3 socket.py will catch EAGAIN and return None when you
            # try and read into the file pointer created by http.client, which
            # instead raises a BadStatusLine exception. Instead of catching
            # the exception and assuming all BadStatusLine exceptions are read
            # timeouts, check for a zero timeout before making the request.
            if read_timeout == 0:
                raise ReadTimeoutError(
                    self, url, "Read timed out. (read timeout=%s)" % read_timeout)
            if read_timeout is Timeout.DEFAULT_TIMEOUT:
                conn.sock.settimeout(socket.getdefaulttimeout())
            else:  # None or a value
                conn.sock.settimeout(read_timeout)
    
        # Receive the response from the server
        try:
            try:  # Python 2.7, use buffering of HTTP responses
>               httplib_response = conn.getresponse(buffering=True)

C:\Python\lib\site-packages\urllib3\connectionpool.py:380: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <vcr.patch.VCRRequestsHTTPConnectionC:\workspace\python\Flexget\flexget\tests\cassettes\test_rottentomatoes.TestRottenTomatoesLookup.test_rottentomatoes_lookup object at 0x01399850>
_ = False, kwargs = {'buffering': True}

    def getresponse(self, _=False, **kwargs):
        '''Retrieve the response'''
        # Check to see if the cassette has a response for this request. If so,
        # then return it
        if self.cassette.can_play_response_for(self._vcr_request):
            log.info(
                "Playing response for {0} from cassette".format(
                    self._vcr_request
                )
            )
            response = self.cassette.play_response(self._vcr_request)
            return VCRHTTPResponse(response)
        else:
            if self.cassette.write_protected and self.cassette.filter_request(
                self._vcr_request
            ):
                raise CannotOverwriteExistingCassetteException(
                    "No match for the request (%r) was found. "
                    "Can't overwrite existing cassette (%r) in "
                    "your current record mode (%r)."
                    % (self._vcr_request, self.cassette._path,
>                      self.cassette.record_mode)
                )
E               vcr.errors.CannotOverwriteExistingCassetteException: No match for the request (<Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv>) was found. Can't overwrite existing cassette ('C:\\workspace\\python\\Flexget\\flexget\\tests\\cassettes\\test_rottentomatoes.TestRottenTomatoesLookup.test_rottentomatoes_lookup') in your current record mode ('once').

C:\Python\lib\site-packages\vcr\stubs\__init__.py:232: CannotOverwriteExistingCassetteException

During handling of the above exception, another exception occurred:

self = <flexget.tests.test_rottentomatoes.TestRottenTomatoesLookup object at 0x0BC28A50>
execute_task = <function execute_task.<locals>.execute at 0x0B586300>

    @pytest.mark.xfail(reason='This plugin seems to be broken')
    def test_rottentomatoes_lookup(self, execute_task):
        task = execute_task('test')
        # check that these were created
>       assert task.find_entry(rt_name='Toy Story', rt_year=1995, rt_id=9559, imdb_id='tt0114709'), \
            'Didn\'t populate RT info for Toy Story'

test_rottentomatoes.py:30: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\task.py:385: in find_entry
    if not (k in entry and entry[k] == v):
C:\Python\lib\_collections_abc.py:601: in __contains__
    self[key]
..\utils\lazy_dict.py:73: in __getitem__
    return item[key]
..\utils\lazy_dict.py:44: in __getitem__
    manager.crash_report()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <flexget.tests.conftest.MockManager object at 0x0AE28670>

    def crash_report(self):
        # We don't want to silently swallow crash reports during unit tests
        log.error('Crash Report Traceback:', exc_info=True)
>       raise CrashReport('Crash report created during unit test, check log for traceback.')
E       flexget.tests.conftest.CrashReport: Crash report created during unit test, check log for traceback.

conftest.py:337: CrashReport
-------------------------------- Captured log ---------------------------------
manager.py                 115 INFO     last manager was not torn down correctly
manager.py                 172 DEBUG    sys.defaultencoding: utf-8
manager.py                 173 DEBUG    sys.getfilesystemencoding: mbcs
manager.py                 174 DEBUG    flexget detected io encoding: utf8
manager.py                 175 DEBUG    os.path.supports_unicode_filenames: True
conftest.py                309 DEBUG    database_uri: sqlite:///:memory:
plugin.py                  384 DEBUG    Trying to load plugins from: ['C:\\workspace\\python\\Flexget\\flexget\\tests\\plugins', 'C:\\workspace\\python\\Flexget\\flexget\\plugins']
plugin.py                  405 DEBUG    ('Plugin `%s` requires `%s` to load.', 'memusage', 'ext lib `guppy`')
plugin.py                  469 DEBUG    Plugins took 0.11 seconds to load. 290 plugins in registry.
manager.py                 713 DEBUG    Connecting to: sqlite:///:memory:
db_schema.py                92 DEBUG    Initializing plugin pogcal_acquired schema version to 0
db_schema.py                92 DEBUG    Initializing plugin status schema version to 2
db_schema.py                92 DEBUG    Initializing plugin input_cache schema version to 1
db_schema.py                92 DEBUG    Initializing plugin morethantv schema version to 0
db_schema.py                92 DEBUG    Initializing plugin failed schema version to 3
db_schema.py                92 DEBUG    Initializing plugin entry_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin log_once schema version to 0
db_schema.py                92 DEBUG    Initializing plugin version_checker schema version to 0
db_schema.py                92 DEBUG    Initializing plugin digest schema version to 1
db_schema.py                92 DEBUG    Initializing plugin tail schema version to 0
db_schema.py                92 DEBUG    Initializing plugin trakt_auth schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_tvdb schema version to 7
db_schema.py                92 DEBUG    Initializing plugin series schema version to 14
db_schema.py                92 DEBUG    Initializing plugin torrent411 schema version to 0
db_schema.py                92 DEBUG    Initializing plugin archive schema version to 0
db_schema.py                92 DEBUG    Initializing plugin delay schema version to 2
db_schema.py                92 DEBUG    Initializing plugin t411 schema version to 0
db_schema.py                92 DEBUG    Initializing plugin make_rss schema version to 0
db_schema.py                92 DEBUG    Initializing plugin subtitle_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin discover schema version to 0
db_schema.py                92 DEBUG    Initializing plugin simple_persistence schema version to 4
db_schema.py                92 DEBUG    Initializing plugin regexp_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin api_bluray schema version to 0
db_schema.py                92 DEBUG    Initializing plugin imdb_lookup schema version to 8
db_schema.py                92 DEBUG    Initializing plugin tvmaze schema version to 7
db_schema.py                92 DEBUG    Initializing plugin seen schema version to 4
db_schema.py                92 DEBUG    Initializing plugin imdb_list schema version to 0
db_schema.py                92 DEBUG    Initializing plugin myepisodes schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_tmdb schema version to 5
db_schema.py                92 DEBUG    Initializing plugin backlog schema version to 2
db_schema.py                92 DEBUG    Initializing plugin api_rottentomatoes schema version to 2
db_schema.py                92 DEBUG    Initializing plugin pending_approval schema version to 0
db_schema.py                92 DEBUG    Initializing plugin telegram_chat_ids schema version to 0
db_schema.py                92 DEBUG    Initializing plugin feed schema version to 0
db_schema.py                92 DEBUG    Initializing plugin alpharatio schema version to 0
db_schema.py                92 DEBUG    Initializing plugin movie_list schema version to 0
db_schema.py                92 DEBUG    Initializing plugin remember_rejected schema version to 3
db_schema.py                92 DEBUG    Initializing plugin variables schema version to 0
db_schema.py                92 DEBUG    Initializing plugin filelist schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_trakt schema version to 7
db_schema.py                92 DEBUG    Initializing plugin rutracker_auth schema version to 0
manager.py                 639 DEBUG    New config data loaded.
db_schema.py                39 DEBUG    entering flexget version 2.10.83.dev to db
plugin_parsing.py           32 DEBUG    setting default movie parser to internal. (options: {'internal': <flexget.plugins.parsers.parser_internal.ParserInternal object at 0x0878F070>, 'guessit': <flexget.plugins.parsers.parser_guessit.ParserGuessit object at 0x0874A490>})
plugin_parsing.py           32 DEBUG    setting default series parser to internal. (options: {'internal': <flexget.plugins.parsers.parser_internal.ParserInternal object at 0x0878F070>, 'guessit': <flexget.plugins.parsers.parser_guessit.ParserGuessit object at 0x0874A490>})
conftest.py                131 DEBUG    Disabling domain limiters during VCR playback.
cassette.py                 64 DEBUG    Entering context for cassette at C:\workspace\python\Flexget\flexget\tests\cassettes\test_rottentomatoes.TestRottenTomatoesLookup.test_rottentomatoes_lookup.
conftest.py                 89 INFO     ********** Running task: test ********** 
task.py                    580 DEBUG    executing test
status.py                  110 DEBUG    Adding new task test
backlog.py                 182 DEBUG    0 entries purged from backlog
logger.py                  133 VERBOSE  Produced 6 entries.
urlrewriting.py             29 DEBUG    Checking 0 entries
logger.py                  133 VERBOSE  Summary - Accepted: 0 (Rejected: 0 Undecided: 6 Failed: 0)
simple_persistence.py      150 DEBUG    Flushing simple persistence for task test to db.
parser_internal.py          22 DEBUG    Parsing movie: `Toy Story` kwargs: {}
movie.py                   140 DEBUG    after parts check, cut data would be: `Toy Story` abs_cut: 9
movie.py                   158 DEBUG    data cut to `Toy Story` - this will be the name
parser_internal.py          30 DEBUG    Parsing result: <MovieParser(name=Toy Story,year=None,quality=unknown)> (in 0.2143055965007079 ms)
api_rottentomatoes.py      274 DEBUG    Looking up rotten tomatoes information for <title=Toy Story,year=None,rottentomatoes_id=None>
api_rottentomatoes.py      288 DEBUG    No matches in movie cache found, checking search cache.
api_rottentomatoes.py      310 DEBUG    Movie <title=Toy Story,year=None,rottentomatoes_id=None> not found in cache, looking up from rotten tomatoes.
logger.py                  133 VERBOSE  Searching from rt `toy story`
api_rottentomatoes.py      527 DEBUG    fetching json at http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv
requests.py                239 DEBUG    Fetching URL http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv with args () and kwargs {'timeout': 30, 'allow_redirects': True}
__init__.py                164 DEBUG    Got <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv>
__init__.py                218 INFO     Playing response for <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv> from cassette
api_rottentomatoes.py      531 WARNING  Request failed http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv: 403 Client Error: Forbidden for url: http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv
log.py                      83 INFO     No results found from rotten tomatoes for <title=toy story,year=none,rottentomatoes_id=none>
parser_internal.py          22 DEBUG    Parsing movie: `The Matrix` kwargs: {}
movie.py                   140 DEBUG    after parts check, cut data would be: `The Matrix` abs_cut: 10
movie.py                   158 DEBUG    data cut to `The Matrix` - this will be the name
parser_internal.py          30 DEBUG    Parsing result: <MovieParser(name=The Matrix,year=None,quality=unknown)> (in 0.19624152324126953 ms)
api_rottentomatoes.py      274 DEBUG    Looking up rotten tomatoes information for <title=The Matrix,year=None,rottentomatoes_id=None>
api_rottentomatoes.py      288 DEBUG    No matches in movie cache found, checking search cache.
api_rottentomatoes.py      310 DEBUG    Movie <title=The Matrix,year=None,rottentomatoes_id=None> not found in cache, looking up from rotten tomatoes.
logger.py                  133 VERBOSE  Searching from rt `the matrix`
api_rottentomatoes.py      527 DEBUG    fetching json at http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv
requests.py                239 DEBUG    Fetching URL http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv with args () and kwargs {'timeout': 30, 'allow_redirects': True}
__init__.py                164 DEBUG    Got <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv>
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
__init__.py                218 INFO     Playing response for <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv> from cassette
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
api_rottentomatoes.py      531 WARNING  Request failed http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv: 403 Client Error: Forbidden for url: http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv
log.py                      83 INFO     No results found from rotten tomatoes for <title=the matrix,year=none,rottentomatoes_id=none>
parser_internal.py          22 DEBUG    Parsing movie: `Star Wars: Episode I - The Phantom Menace (3D)` kwargs: {}
movie.py                   135 DEBUG    parts: ['Star', 'Wars:', 'Episode', 'I', '-', 'The', 'Phantom', 'Menace', '3D'], cut is: 3D
movie.py                   140 DEBUG    after parts check, cut data would be: `Star Wars: Episode I - The Phantom Menace` abs_cut: 41
movie.py                   158 DEBUG    data cut to `Star Wars: Episode I - The Phantom Menace` - this will be the name
parser_internal.py          30 DEBUG    Parsing result: <MovieParser(name=Star Wars: Episode I - The Phantom Menace,year=None,quality=unknown)> (in 0.3128369052092239 ms)
api_rottentomatoes.py      274 DEBUG    Looking up rotten tomatoes information for <title=Star Wars: Episode I - The Phantom Menace,year=None,rottentomatoes_id=None>
api_rottentomatoes.py      288 DEBUG    No matches in movie cache found, checking search cache.
api_rottentomatoes.py      310 DEBUG    Movie <title=Star Wars: Episode I - The Phantom Menace,year=None,rottentomatoes_id=None> not found in cache, looking up from rotten tomatoes.
logger.py                  133 VERBOSE  Searching from rt `star wars: episode i - the phantom menace`
api_rottentomatoes.py      527 DEBUG    fetching json at http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv
requests.py                239 DEBUG    Fetching URL http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv with args () and kwargs {'timeout': 30, 'allow_redirects': True}
__init__.py                164 DEBUG    Got <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv>
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
__init__.py                218 INFO     Playing response for <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv> from cassette
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
api_rottentomatoes.py      531 WARNING  Request failed http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv: 403 Client Error: Forbidden for url: http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv
log.py                      83 INFO     No results found from rotten tomatoes for <title=star wars: episode i - the phantom menace,year=none,rottentomatoes_id=none>
parser_internal.py          22 DEBUG    Parsing movie: `[Group] Taken 720p` kwargs: {}
movie.py                   140 DEBUG    after parts check, cut data would be: `Taken 720p Group` abs_cut: 16
movie.py                   151 DEBUG    quality start: 6
movie.py                   153 DEBUG    quality cut is even shorter
movie.py                   158 DEBUG    data cut to `Taken` - this will be the name
parser_internal.py          30 DEBUG    Parsing result: <MovieParser(name=Taken,year=None,quality=720p)> (in 0.2820458712733398 ms)
api_rottentomatoes.py      274 DEBUG    Looking up rotten tomatoes information for <title=Taken,year=None,rottentomatoes_id=770680780>
api_rottentomatoes.py      288 DEBUG    No matches in movie cache found, checking search cache.
api_rottentomatoes.py      310 DEBUG    Movie <title=Taken,year=None,rottentomatoes_id=770680780> not found in cache, looking up from rotten tomatoes.
api_rottentomatoes.py      527 DEBUG    fetching json at http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv
requests.py                239 DEBUG    Fetching URL http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv with args () and kwargs {'timeout': 30, 'allow_redirects': True}
__init__.py                164 DEBUG    Got <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv>
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=toy+story&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=the+matrix&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=star+wars%3A+episode+i+-+the+phantom+menace&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=rush+hour+1998&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv> and <Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=up&apikey=rh8chjzp8vu6gnpwj88736uv> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
lazy_dict.py                41 ERROR    Unhandled error in lazy lookup plugin
conftest.py                336 ERROR    Crash Report Traceback:
Traceback (most recent call last):
  File "C:\workspace\python\Flexget\flexget\utils\lazy_dict.py", line 37, in __getitem__
    func(self.store)
  File "C:\workspace\python\Flexget\flexget\plugins\metainfo\rottentomatoes_lookup.py", line 78, in lazy_loader
    self.lookup(entry, key=self.key)
  File "C:\workspace\python\Flexget\flexget\plugins\metainfo\rottentomatoes_lookup.py", line 96, in lookup
    api_key=key
  File "C:\workspace\python\Flexget\flexget\plugin.py", line 118, in wrapped_func
    return func(*args, **kwargs)
  File "C:\workspace\python\Flexget\flexget\utils\database.py", line 34, in wrapper
    return func(*args, **kwargs)
  File "C:\workspace\python\Flexget\flexget\plugins\internal\api_rottentomatoes.py", line 313, in lookup_movie
    result = movies_info(rottentomatoes_id, api_key)
  File "C:\workspace\python\Flexget\flexget\plugins\internal\api_rottentomatoes.py", line 480, in movies_info
    result = get_json(url)
  File "C:\workspace\python\Flexget\flexget\plugins\internal\api_rottentomatoes.py", line 528, in get_json
    data = session.get(url)
  File "C:\Python\lib\site-packages\requests\sessions.py", line 521, in get
    return self.request('GET', url, **kwargs)
  File "C:\workspace\python\Flexget\flexget\utils\requests.py", line 240, in request
    result = super(Session, self).request(method, url, *args, **kwargs)
  File "C:\Python\lib\site-packages\requests\sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Python\lib\site-packages\requests\sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "C:\Python\lib\site-packages\requests\adapters.py", line 440, in send
    timeout=timeout
  File "C:\Python\lib\site-packages\urllib3\connectionpool.py", line 601, in urlopen
    chunked=chunked)
  File "C:\Python\lib\site-packages\urllib3\connectionpool.py", line 380, in _make_request
    httplib_response = conn.getresponse(buffering=True)
  File "C:\Python\lib\site-packages\vcr\stubs\__init__.py", line 232, in getresponse
    self.cassette.record_mode)
vcr.errors.CannotOverwriteExistingCassetteException: No match for the request (<Request (GET) http://api.rottentomatoes.com/api/public/v1.0/movies/770680780.json?apikey=rh8chjzp8vu6gnpwj88736uv>) was found. Can't overwrite existing cassette ('C:\\workspace\\python\\Flexget\\flexget\\tests\\cassettes\\test_rottentomatoes.TestRottenTomatoesLookup.test_rottentomatoes_lookup') in your current record mode ('once').

flexget\tests\test_rss.py .........X.................................................................................................................................................................................................................................................s
Skipped: FIX: #402 .. a bit hard to do
...................................................s
Skipped: FIX: #402 .. a bit hard to do
...........................................................s
Skipped: requires subliminal
s
Skipped: requires subliminal
s
Skipped: Test sporadically fails
s
Skipped: requires subliminal
.s
Skipped: requires subliminal
.....s
Skipped: symlinks do not work on windows
s
Skipped: symlinks do not work on windows
s
Skipped: symlinks do not work on windows
s
Skipped: symlinks do not work on windows
s
Skipped: symlinks do not work on windows
s
Skipped: Need a valid user for testing
s
Skipped: Need a valid user for testing
s
Skipped: Need a valid user for testing
s
Skipped: Need a valid user for testing
s
Skipped: Need a valid user for testing
s
Skipped: Need a valid user for testing
.............................x
self = <flexget.tests.test_tmdb.TestTmdbUnicodeLookup object at 0x0B7BE2F0>
execute_task = <function execute_task.<locals>.execute at 0x0B7DEC48>

    @pytest.mark.xfail(reason='VCR attempts to compare str to unicode')
    def test_unicode(self, execute_task):
        execute_task('test_unicode')
        with Session() as session:
            r = session.query(TMDBSearchResult).all()
>           assert len(r) == 1, 'Should have added a search result'
E           AssertionError: Should have added a search result
E           assert 0 == 1
E            +  where 0 = len([])

test_tmdb.py:48: AssertionError
-------------------------------- Captured log ---------------------------------
manager.py                 115 INFO     last manager was not torn down correctly
manager.py                 172 DEBUG    sys.defaultencoding: utf-8
manager.py                 173 DEBUG    sys.getfilesystemencoding: mbcs
manager.py                 174 DEBUG    flexget detected io encoding: utf8
manager.py                 175 DEBUG    os.path.supports_unicode_filenames: True
conftest.py                309 DEBUG    database_uri: sqlite:///:memory:
plugin.py                  384 DEBUG    Trying to load plugins from: ['C:\\workspace\\python\\Flexget\\flexget\\tests\\plugins', 'C:\\workspace\\python\\Flexget\\flexget\\plugins']
plugin.py                  405 DEBUG    ('Plugin `%s` requires `%s` to load.', 'memusage', 'ext lib `guppy`')
plugin.py                  469 DEBUG    Plugins took 0.10 seconds to load. 290 plugins in registry.
manager.py                 713 DEBUG    Connecting to: sqlite:///:memory:
db_schema.py                92 DEBUG    Initializing plugin pogcal_acquired schema version to 0
db_schema.py                92 DEBUG    Initializing plugin status schema version to 2
db_schema.py                92 DEBUG    Initializing plugin input_cache schema version to 1
db_schema.py                92 DEBUG    Initializing plugin morethantv schema version to 0
db_schema.py                92 DEBUG    Initializing plugin failed schema version to 3
db_schema.py                92 DEBUG    Initializing plugin entry_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin log_once schema version to 0
db_schema.py                92 DEBUG    Initializing plugin version_checker schema version to 0
db_schema.py                92 DEBUG    Initializing plugin digest schema version to 1
db_schema.py                92 DEBUG    Initializing plugin tail schema version to 0
db_schema.py                92 DEBUG    Initializing plugin trakt_auth schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_tvdb schema version to 7
db_schema.py                92 DEBUG    Initializing plugin series schema version to 14
db_schema.py                92 DEBUG    Initializing plugin torrent411 schema version to 0
db_schema.py                92 DEBUG    Initializing plugin archive schema version to 0
db_schema.py                92 DEBUG    Initializing plugin delay schema version to 2
db_schema.py                92 DEBUG    Initializing plugin t411 schema version to 0
db_schema.py                92 DEBUG    Initializing plugin make_rss schema version to 0
db_schema.py                92 DEBUG    Initializing plugin subtitle_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin discover schema version to 0
db_schema.py                92 DEBUG    Initializing plugin simple_persistence schema version to 4
db_schema.py                92 DEBUG    Initializing plugin regexp_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin api_bluray schema version to 0
db_schema.py                92 DEBUG    Initializing plugin imdb_lookup schema version to 8
db_schema.py                92 DEBUG    Initializing plugin tvmaze schema version to 7
db_schema.py                92 DEBUG    Initializing plugin seen schema version to 4
db_schema.py                92 DEBUG    Initializing plugin imdb_list schema version to 0
db_schema.py                92 DEBUG    Initializing plugin myepisodes schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_tmdb schema version to 5
db_schema.py                92 DEBUG    Initializing plugin backlog schema version to 2
db_schema.py                92 DEBUG    Initializing plugin api_rottentomatoes schema version to 2
db_schema.py                92 DEBUG    Initializing plugin pending_approval schema version to 0
db_schema.py                92 DEBUG    Initializing plugin telegram_chat_ids schema version to 0
db_schema.py                92 DEBUG    Initializing plugin feed schema version to 0
db_schema.py                92 DEBUG    Initializing plugin alpharatio schema version to 0
db_schema.py                92 DEBUG    Initializing plugin movie_list schema version to 0
db_schema.py                92 DEBUG    Initializing plugin remember_rejected schema version to 3
db_schema.py                92 DEBUG    Initializing plugin variables schema version to 0
db_schema.py                92 DEBUG    Initializing plugin filelist schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_trakt schema version to 7
db_schema.py                92 DEBUG    Initializing plugin rutracker_auth schema version to 0
manager.py                 639 DEBUG    New config data loaded.
db_schema.py                39 DEBUG    entering flexget version 2.10.83.dev to db
plugin_parsing.py           32 DEBUG    setting default movie parser to internal. (options: {'internal': <flexget.plugins.parsers.parser_internal.ParserInternal object at 0x0878F070>, 'guessit': <flexget.plugins.parsers.parser_guessit.ParserGuessit object at 0x0874A490>})
plugin_parsing.py           32 DEBUG    setting default series parser to internal. (options: {'internal': <flexget.plugins.parsers.parser_internal.ParserInternal object at 0x0878F070>, 'guessit': <flexget.plugins.parsers.parser_guessit.ParserGuessit object at 0x0874A490>})
conftest.py                131 DEBUG    Disabling domain limiters during VCR playback.
cassette.py                 64 DEBUG    Entering context for cassette at C:\workspace\python\Flexget\flexget\tests\cassettes\test_tmdb.TestTmdbUnicodeLookup.test_unicode.
conftest.py                 89 INFO     ********** Running task: test_unicode ********** 
task.py                    580 DEBUG    executing test_unicode
template.py                 92 DEBUG    Merging template global into task test_unicode
disable.py                  71 DEBUG    Disabled built-in plugin(s): seen
status.py                  110 DEBUG    Adding new task test_unicode
backlog.py                 182 DEBUG    0 entries purged from backlog
logger.py                  133 VERBOSE  Produced 1 entries.
parser_internal.py          22 DEBUG    Parsing movie: `Зеркала Mirrors 2008` kwargs: {}
movie.py                   135 DEBUG    parts: ['Зеркала', 'Mirrors', '2008'], cut is: 2008
movie.py                   140 DEBUG    after parts check, cut data would be: `Зеркала Mirrors` abs_cut: 15
movie.py                   158 DEBUG    data cut to `Зеркала Mirrors` - this will be the name
parser_internal.py          30 DEBUG    Parsing result: <MovieParser(name=Зеркала Mirrors,year=2008,quality=unknown)> (in 0.27301383460098805 ms)
api_tmdb.py                295 DEBUG    Looking up TMDb information for <title=Зеркала Mirrors, year=2008, tmdb_id=None, imdb_id=None>
logger.py                  133 VERBOSE  Searching from TMDb <title=Зеркала Mirrors, year=2008, tmdb_id=None, imdb_id=None>
requests.py                239 DEBUG    Fetching URL https://api.themoviedb.org/3/search/movie with args () and kwargs {'params': {'query': 'Зеркала Mirrors', 'api_key': 'bdfc018dbdb7c243dc7cb1454ff74b95', 'year': 2008}, 'timeout': 30, 'allow_redirects': True}
__init__.py                164 DEBUG    Got <Request (GET) https://api.themoviedb.org/3/search/movie?query=%D0%97%D0%B5%D1%80%D0%BA%D0%B0%D0%BB%D0%B0+Mirrors&api_key=bdfc018dbdb7c243dc7cb1454ff74b95&year=2008>
__init__.py                218 INFO     Playing response for <Request (GET) https://api.themoviedb.org/3/search/movie?query=%D0%97%D0%B5%D1%80%D0%BA%D0%B0%D0%BB%D0%B0+Mirrors&api_key=bdfc018dbdb7c243dc7cb1454ff74b95&year=2008> from cassette
requests.py                239 DEBUG    Fetching URL https://api.themoviedb.org/3/movie/13515 with args () and kwargs {'params': {'append_to_response': 'alternative_titles', 'api_key': 'bdfc018dbdb7c243dc7cb1454ff74b95'}, 'timeout': 30, 'allow_redirects': True}
__init__.py                164 DEBUG    Got <Request (GET) https://api.themoviedb.org/3/movie/13515?append_to_response=alternative_titles&api_key=bdfc018dbdb7c243dc7cb1454ff74b95>
matchers.py                 94 DEBUG    Requests <Request (GET) https://api.themoviedb.org/3/movie/13515?append_to_response=alternative_titles&api_key=bdfc018dbdb7c243dc7cb1454ff74b95> and <Request (GET) https://api.themoviedb.org/3/search/movie?query=%D0%97%D0%B5%D1%80%D0%BA%D0%B0%D0%BB%D0%B0+Mirrors&api_key=bdfc018dbdb7c243dc7cb1454ff74b95&year=2008> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) https://api.themoviedb.org/3/movie/13515?append_to_response=alternative_titles&api_key=bdfc018dbdb7c243dc7cb1454ff74b95> and <Request (GET) https://api.themoviedb.org/3/movie/13515?api_key=bdfc018dbdb7c243dc7cb1454ff74b95&append_to_response=alternative_titles%2Cimages> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) https://api.themoviedb.org/3/movie/13515?append_to_response=alternative_titles&api_key=bdfc018dbdb7c243dc7cb1454ff74b95> and <Request (GET) https://api.themoviedb.org/3/search/movie?query=%D0%97%D0%B5%D1%80%D0%BA%D0%B0%D0%BB%D0%B0+Mirrors&api_key=bdfc018dbdb7c243dc7cb1454ff74b95&year=2008> differ according to the following matchers: [(False, <function path at 0x05AF3F18>), (False, <function query at 0x05AF3F60>)]
matchers.py                 94 DEBUG    Requests <Request (GET) https://api.themoviedb.org/3/movie/13515?append_to_response=alternative_titles&api_key=bdfc018dbdb7c243dc7cb1454ff74b95> and <Request (GET) https://api.themoviedb.org/3/movie/13515?api_key=bdfc018dbdb7c243dc7cb1454ff74b95&append_to_response=alternative_titles%2Cimages> differ according to the following matchers: [(False, <function query at 0x05AF3F60>)]
lazy_dict.py                41 ERROR    Unhandled error in lazy lookup plugin
conftest.py                336 ERROR    Crash Report Traceback:
Traceback (most recent call last):
  File "C:\workspace\python\Flexget\flexget\utils\lazy_dict.py", line 37, in __getitem__
    func(self.store)
  File "C:\workspace\python\Flexget\flexget\plugins\metainfo\tmdb_lookup.py", line 64, in lazy_loader
    session=session)
  File "C:\workspace\python\Flexget\flexget\utils\database.py", line 31, in wrapper
    return func(*args, **kwargs)
  File "C:\workspace\python\Flexget\flexget\plugins\internal\api_tmdb.py", line 361, in lookup
    movie = TMDBMovie(id=tmdb_id)
  File "<string>", line 4, in __init__
  File "C:\Python\lib\site-packages\sqlalchemy\orm\state.py", line 414, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "C:\Python\lib\site-packages\sqlalchemy\util\langhelpers.py", line 66, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "C:\Python\lib\site-packages\sqlalchemy\util\compat.py", line 187, in reraise
    raise value
  File "C:\Python\lib\site-packages\sqlalchemy\orm\state.py", line 411, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "C:\workspace\python\Flexget\flexget\plugins\internal\api_tmdb.py", line 121, in __init__
    movie = tmdb_request('movie/{}'.format(self.id), append_to_response='alternative_titles')
  File "C:\workspace\python\Flexget\flexget\plugins\internal\api_tmdb.py", line 69, in tmdb_request
    return requests.get(full_url, params=params).json()
  File "C:\workspace\python\Flexget\flexget\utils\requests.py", line 275, in get
    return request('get', url, **kwargs)
  File "C:\workspace\python\Flexget\flexget\utils\requests.py", line 255, in request
    return s.request(method=method, url=url, **kwargs)
  File "C:\workspace\python\Flexget\flexget\utils\requests.py", line 240, in request
    result = super(Session, self).request(method, url, *args, **kwargs)
  File "C:\Python\lib\site-packages\requests\sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Python\lib\site-packages\requests\sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "C:\Python\lib\site-packages\requests\adapters.py", line 440, in send
    timeout=timeout
  File "C:\Python\lib\site-packages\urllib3\connectionpool.py", line 601, in urlopen
    chunked=chunked)
  File "C:\Python\lib\site-packages\urllib3\connectionpool.py", line 380, in _make_request
    httplib_response = conn.getresponse(buffering=True)
  File "C:\Python\lib\site-packages\vcr\stubs\__init__.py", line 232, in getresponse
    self.cassette.record_mode)
vcr.errors.CannotOverwriteExistingCassetteException: No match for the request (<Request (GET) https://api.themoviedb.org/3/movie/13515?append_to_response=alternative_titles&api_key=bdfc018dbdb7c243dc7cb1454ff74b95>) was found. Can't overwrite existing cassette ('C:\\workspace\\python\\Flexget\\flexget\\tests\\cassettes\\test_tmdb.TestTmdbUnicodeLookup.test_unicode') in your current record mode ('once').
if_condition.py             58 ERROR    Error occurred while evaluating statement `tmdb_year > now.year - 1`. (Crash report created during unit test, check log for traceback.)
urlrewriting.py             29 DEBUG    Checking 0 entries
logger.py                  133 VERBOSE  Summary - Accepted: 0 (Rejected: 0 Undecided: 1 Failed: 0)
disable.py                  82 DEBUG    Re-enabled builtin plugin(s): seen
simple_persistence.py      150 DEBUG    Flushing simple persistence for task test_unicode to db.
...................shameless
...................x
self = <flexget.tests.test_trakt.TestTraktUnicodeLookup object at 0x0BB34E30>
execute_task = <function execute_task.<locals>.execute at 0x0BA9FC90>

    @pytest.mark.xfail(reason='VCR attempts to compare str to unicode')
    def test_unicode(self, execute_task):
        execute_task('test_unicode')
        with Session() as session:
            r = session.query(TraktMovieSearchResult).all()
>           assert len(r) == 1, 'Should have added a search result'
E           AssertionError: Should have added a search result
E           assert 0 == 1
E            +  where 0 = len([])

test_trakt.py:491: AssertionError
-------------------------------- Captured log ---------------------------------
manager.py                 115 INFO     last manager was not torn down correctly
manager.py                 172 DEBUG    sys.defaultencoding: utf-8
manager.py                 173 DEBUG    sys.getfilesystemencoding: mbcs
manager.py                 174 DEBUG    flexget detected io encoding: utf8
manager.py                 175 DEBUG    os.path.supports_unicode_filenames: True
conftest.py                309 DEBUG    database_uri: sqlite:///:memory:
plugin.py                  384 DEBUG    Trying to load plugins from: ['C:\\workspace\\python\\Flexget\\flexget\\tests\\plugins', 'C:\\workspace\\python\\Flexget\\flexget\\plugins']
plugin.py                  405 DEBUG    ('Plugin `%s` requires `%s` to load.', 'memusage', 'ext lib `guppy`')
plugin.py                  469 DEBUG    Plugins took 0.10 seconds to load. 290 plugins in registry.
manager.py                 713 DEBUG    Connecting to: sqlite:///:memory:
db_schema.py                92 DEBUG    Initializing plugin pogcal_acquired schema version to 0
db_schema.py                92 DEBUG    Initializing plugin status schema version to 2
db_schema.py                92 DEBUG    Initializing plugin input_cache schema version to 1
db_schema.py                92 DEBUG    Initializing plugin morethantv schema version to 0
db_schema.py                92 DEBUG    Initializing plugin failed schema version to 3
db_schema.py                92 DEBUG    Initializing plugin entry_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin log_once schema version to 0
db_schema.py                92 DEBUG    Initializing plugin version_checker schema version to 0
db_schema.py                92 DEBUG    Initializing plugin digest schema version to 1
db_schema.py                92 DEBUG    Initializing plugin tail schema version to 0
db_schema.py                92 DEBUG    Initializing plugin trakt_auth schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_tvdb schema version to 7
db_schema.py                92 DEBUG    Initializing plugin series schema version to 14
db_schema.py                92 DEBUG    Initializing plugin torrent411 schema version to 0
db_schema.py                92 DEBUG    Initializing plugin archive schema version to 0
db_schema.py                92 DEBUG    Initializing plugin delay schema version to 2
db_schema.py                92 DEBUG    Initializing plugin t411 schema version to 0
db_schema.py                92 DEBUG    Initializing plugin make_rss schema version to 0
db_schema.py                92 DEBUG    Initializing plugin subtitle_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin discover schema version to 0
db_schema.py                92 DEBUG    Initializing plugin simple_persistence schema version to 4
db_schema.py                92 DEBUG    Initializing plugin regexp_list schema version to 1
db_schema.py                92 DEBUG    Initializing plugin api_bluray schema version to 0
db_schema.py                92 DEBUG    Initializing plugin imdb_lookup schema version to 8
db_schema.py                92 DEBUG    Initializing plugin tvmaze schema version to 7
db_schema.py                92 DEBUG    Initializing plugin seen schema version to 4
db_schema.py                92 DEBUG    Initializing plugin imdb_list schema version to 0
db_schema.py                92 DEBUG    Initializing plugin myepisodes schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_tmdb schema version to 5
db_schema.py                92 DEBUG    Initializing plugin backlog schema version to 2
db_schema.py                92 DEBUG    Initializing plugin api_rottentomatoes schema version to 2
db_schema.py                92 DEBUG    Initializing plugin pending_approval schema version to 0
db_schema.py                92 DEBUG    Initializing plugin telegram_chat_ids schema version to 0
db_schema.py                92 DEBUG    Initializing plugin feed schema version to 0
db_schema.py                92 DEBUG    Initializing plugin alpharatio schema version to 0
db_schema.py                92 DEBUG    Initializing plugin movie_list schema version to 0
db_schema.py                92 DEBUG    Initializing plugin remember_rejected schema version to 3
db_schema.py                92 DEBUG    Initializing plugin variables schema version to 0
db_schema.py                92 DEBUG    Initializing plugin filelist schema version to 0
db_schema.py                92 DEBUG    Initializing plugin api_trakt schema version to 7
db_schema.py                92 DEBUG    Initializing plugin rutracker_auth schema version to 0
manager.py                 639 DEBUG    New config data loaded.
db_schema.py                39 DEBUG    entering flexget version 2.10.83.dev to db
plugin_parsing.py           32 DEBUG    setting default movie parser to internal. (options: {'internal': <flexget.plugins.parsers.parser_internal.ParserInternal object at 0x0878F070>, 'guessit': <flexget.plugins.parsers.parser_guessit.ParserGuessit object at 0x0874A490>})
plugin_parsing.py           32 DEBUG    setting default series parser to internal. (options: {'internal': <flexget.plugins.parsers.parser_internal.ParserInternal object at 0x0878F070>, 'guessit': <flexget.plugins.parsers.parser_guessit.ParserGuessit object at 0x0874A490>})
cassette.py                 64 DEBUG    Entering context for cassette at C:\workspace\python\Flexget\flexget\tests\cassettes\test_trakt.TestTraktUnicodeLookup.test_unicode.
conftest.py                 89 INFO     ********** Running task: test_unicode ********** 
task.py                    580 DEBUG    executing test_unicode
template.py                 92 DEBUG    Merging template global into task test_unicode
disable.py                  71 DEBUG    Disabled built-in plugin(s): seen
status.py                  110 DEBUG    Adding new task test_unicode
backlog.py                 182 DEBUG    0 entries purged from backlog
logger.py                  133 VERBOSE  Produced 1 entries.
if_condition.py             56 DEBUG    Зеркала Mirrors 2008 does not contain the field trakt_year
urlrewriting.py             29 DEBUG    Checking 0 entries
logger.py                  133 VERBOSE  Summary - Accepted: 0 (Rejected: 0 Undecided: 1 Failed: 0)
disable.py                  82 DEBUG    Re-enabled builtin plugin(s): seen
simple_persistence.py      150 DEBUG    Flushing simple persistence for task test_unicode to db.
...............shameless
............X............................................
flexget\tests\api_tests\test_authentication_api.py .
flexget\tests\api_tests\test_cached_api.py .
flexget\tests\api_tests\test_database_api.py .
flexget\tests\api_tests\test_entry_list_api.py .....
flexget\tests\api_tests\test_etag.py ......
flexget\tests\api_tests\test_execute_api.py .
flexget\tests\api_tests\test_failed_api.py ........
flexget\tests\api_tests\test_format_checker_api.py ....
flexget\tests\api_tests\test_history_api.py ............
flexget\tests\api_tests\test_imdb_lookup_api.py ...
flexget\tests\api_tests\test_movie_list_api.py .
flexget\tests\api_tests\test_pending_api.py .........
flexget\tests\api_tests\test_plugins_api.py ......
flexget\tests\api_tests\test_rejected_api.py .
flexget\tests\api_tests\test_schedule_api.py .......
flexget\tests\api_tests\test_seen_api.py .................
flexget\tests\api_tests\test_series_api.py ......
flexget\tests\api_tests\test_server_api.py ........................
flexget\tests\api_tests\test_status_api.py ......
flexget\tests\api_tests\test_tasks_api.py .......
flexget\tests\api_tests\test_tmdb_lookup.py ........
flexget\tests\api_tests\test_trakt_lookup_api.py ....
flexget\tests\api_tests\test_tvdb_lookup_api.py .................
flexget\tests\api_tests\test_tvmaze_lookup_api.py ...............
flexget\tests\api_tests\test_user_api.py .....
flexget\tests\api_tests\test_variables_api.py ..
flexget\tests\notifiers\test_notify_abort.py ........
flexget\tests\notifiers\test_pushover.py .
Process finished with exit code 0

To Do:

  • Remember non-expired wordpress cookies (sqlite?)

log = logging.getLogger('wordpress_auth')


class WPLoginHeaders(dict):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I like this. Why not just create a regular dict in on_task_start? Same goes for the other classes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I'm pretty new to the python way of doing things.
I created the WPLogin Classes so that I could have a very clean

get_cookies(WPSession(), WPLoginRequest(url, username=username, password=password).prepare())

with WPSession and WPLoginRquest dependency injected into get_cookies
rather than having to imperatively write

wpsession = Session()
wpsession.max_redirects = 5
wp_login_request = Request()
#etc

I felt it was a way to keep the on_task_start function short.
Again,
I don't have a very strong background in python, I'm more familiar with compiled languages.
So maybe this type of reasoning is faulty.

log = logging.getLogger('wordpress_auth')


def _get_wp_login_data(username='', password='', redirect='/wp-admin/'):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to have these as separate functions/classes is what I was thinking...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, there isn't
cleaning it up now

Copy link
Copy Markdown
Contributor

@cvium cvium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @liiight has an opinion as well...

username = config['username']
password = config['password']

resp = send_request(Session(), construct_wp_login_request(url, username=username, password=password))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use task.requests instead of Session() and don't close it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, idea. Done


resp = send_request(Session(), construct_wp_login_request(url, username=username, password=password))
cookies = collect_cookies_from_response(resp)
validate_cookies(cookies, match_wordpress_cookie)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the point of this is. Should the task just continue if the cookies are invalid?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the initial thought. That the task should continue, the reasoning behind it was that in the future, wordpress might update the cookie names. However, that doesn't seem likely and in the event they do such a thing this plugin would need to be updated. I've changed the code so that the task will abort if there are no valid cookies found.

return re.match(r'wordpress(?!_test)[A-z0-9]*', key, re.IGNORECASE)


def send_request(session, prep_request, redirects=5):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd honestly prefer having this inside on_task_start. You generally create a lot of global functions that could just as well be made inline in on_task_start.

I think the code becomes harder to read when having to scroll up and down to see what each function does. construct_wp_login_request is also just essentially two dicts.

It looks fine otherwise. I just don't agree with the code style.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the code now so there are only 3 global functions.
The request is now sent in on_task_start
All exceptions are now handled or raised in on_task_start

@transverberate
Copy link
Copy Markdown
Contributor Author

Has this PR gone stale?

@liiight
Copy link
Copy Markdown
Member

liiight commented Oct 18, 2017

A little...
You didn't answer my question, why mess with low level requests?

@transverberate
Copy link
Copy Markdown
Contributor Author

The format of the authentication that the WordPress site requires is not standard form authentication. The username is submitted under the log key and the password is submitted under the pwd key. Additionally a wp-submit: Log In key value pair is needed as well. No plugin that exists has the capability to generate this request.
The formlogin.py plugin in the operate folder comes close to this functionality. It has the ability to rename the username and password keys, however it does not allow the sending of the needed wp-submit: Login key value pair, nor is the plugin compatible with Python 3 due to a dependency with mechanize.
That is why I've been messing with low level requests.

@liiight liiight merged commit 9942c4b into Flexget:develop Oct 19, 2017
@liiight
Copy link
Copy Markdown
Member

liiight commented Oct 19, 2017

Gotcha.
Thanks for this! Care to update the wiki as well?

@transverberate
Copy link
Copy Markdown
Contributor Author

Yeah, the wiki is updated.
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants