Python Forum
urllib3 Problem? - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: urllib3 Problem? (/thread-24545.html)



urllib3 Problem? - gw1500se - Feb-18-2020

I have a script that uses the MailJet REST API which has just started failing. I don't understand what the problem is because the error message referencing urllib3 is not clear to me. I have that library installed. Can someone interpret this error for me?

Traceback (most recent call last):
File "/usr/local/sbin/mailjet.py", line 6, in <module>
from mailjet_rest import Client
File "/usr/lib/python2.7/site-packages/mailjet_rest/__init__.py", line 4, in <module>
from .client import Client
File "/usr/lib/python2.7/site-packages/mailjet_rest/client.py", line 11, in <module>
requests.packages.urllib3.disable_warnings()
AttributeError: 'module' object has no attribute 'packages'

If I try 'pip install urllib3' I get this:

Requirement already satisfied: urllib3 in /usr/lib/python2.7/site-packages (1.10.2)


RE: urllib3 Problem? - buran - Feb-18-2020

do you have a newly created file named requests.py?


RE: urllib3 Problem? - gw1500se - Feb-18-2020

No, that is part of the MailJet API. I don't have access to that source.


RE: urllib3 Problem? - buran - Feb-18-2020

it's using requests package - that is clear (it's a dependency). However (if I understand correctly) it worked and now - it doesn't. The error says module object has no attribute packages. That is why I think there is something that override the installed requests package which has packages module inside it, so requests.packages should work unless something overrides the package :-)
This is common newbie mistake - to override module from Standard Library or installed third-party package by naming their own script with the same name.


RE: urllib3 Problem? - gw1500se - Feb-18-2020

There is no requests.py on my system. I gather that the issue is NOT urllib3, right? It sounds like I need to go to Mailajet tech support.


RE: urllib3 Problem? - buran - Feb-18-2020

no, it's not urlib3 issue. However it's something on your system, not problem with the mailjet-api-client in my opinion


RE: urllib3 Problem? - buran - Feb-18-2020

can you make a small test.
Create a file with following content
import requests
print(requests.__file__)
in the same folder where your current script is and run it the same way. what does it print?


RE: urllib3 Problem? - gw1500se - Feb-24-2020

Here is the output:

Python 2.7.5 (default, Aug 7 2019, 00:51:29)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> print(requests.__file__)
/usr/lib/python2.7/site-packages/requests/__init__.pyc


RE: urllib3 Problem? - gw1500se - Mar-02-2020

I've been able to track down the source for the offending MailJet script. To me, it looks like the offending line can be commented out:

!/usr/bin/env python
# coding=utf-8

import json
import logging

import requests
from requests.compat import urljoin
from ._version import __version__

requests.packages.urllib3.disable_warnings()


class Config(object):
    API_URL = 'https://api.mailjet.com/'
    API_REF = 'http://dev.mailjet.com/email-api/v3/'
    version = 'v3'
    user_agent = 'mailjet-apiv3-python/' + __version__

    def __init__(self, version=None):
        if version is not None:
            self.version = version
    def __getitem__(self, key):
        url = self.API_URL[0:]
        # Append version to URL.
        # Forward slash is ignored if present in self.version.
        url = urljoin(url, self.version + '/')
        headers = {'Content-type': 'application/json', 'User-agent': self.user_agent}
        if key.lower() == 'contactslist_csvdata':
            url = urljoin(url, 'DATA/')
            headers['Content-type'] = 'text/plain'
        elif key.lower() == 'batchjob_csverror':
            url = urljoin(url, 'DATA/')
            headers['Content-type'] = 'text/csv'
        elif key.lower() != 'send':
            url = urljoin(url, 'REST/')
        url = url + key.split('_')[0].lower()
        return url, headers


class Endpoint(object):

    def __init__(self, url, headers, auth, action=None):
       self._url, self.headers, self._auth, self.action = url, headers, auth, action

    def __doc__(self):
        return self._doc

    def _get(self, filters=None, action_id=None, id=None, **kwargs):
        return api_call(self._auth, 'get', self._url, headers=self.headers, action=self.action, action_id=action_id, filters=filters, resource_id=id, **kwargs)

    def get_many(self, filters=None, action_id=None, **kwargs):
        return self._get(filters=filters, **kwargs)

    def get(self, id=None, filters=None, action_id=None, **kwargs):
        return self._get(id=id, filters=filters, **kwargs)

    def create(self, data=None, filters=None, id=None, action_id=None, **kwargs):
        if self.headers['Content-type'] == 'application/json':
            data = json.dumps(data)
        return api_call(self._auth, 'post', self._url, headers=self.headers, resource_id=id, data=data, action=self.action, action_id=action_id, filters=filters, **kwargs)
    def update(self, id, data, filters=None, action_id=None, **kwargs):
        if self.headers['Content-type'] == 'application/json':
            data = json.dumps(data)
        return api_call(self._auth, 'put', self._url, resource_id=id, headers=self.headers, data=data, action=self.action, action_id=action_id, filters=filters, **kwargs)

    def delete(self, id, **kwargs):
        return api_call(self._auth, 'delete', self._url, action=self.action, headers=self.headers, resource_id=id, **kwargs)


class Client(object):

    def __init__(self, auth=None, **kwargs):
        self.auth = auth
        version = kwargs.get('version', None)
        self.config = Config(version=version)

    def __getattr__(self, name):
        split = name.split('_')
        fname = split[0]
        action = None
        if (len(split) > 1):
            action = split[1]
            if action == 'csvdata':
                action = 'csvdata/text:plain'
            if action == 'csverror':
                action = 'csverror/text:csv'
        url, headers = self.config[name]
        return type(fname, (Endpoint,), {})(url=url, headers=headers, action=action, auth=self.auth)


def api_call(auth, method, url, headers, data=None, filters=None, resource_id=None,
             timeout=60, debug=False, action=None, action_id=None, **kwargs):
    url = build_url(url, method=method, action=action, resource_id=resource_id, action_id=action_id)
    req_method = getattr(requests, method)

    try:
        response = req_method(url, data=data, params=filters, headers=headers, auth=auth,
                              timeout=timeout, verify=True, stream=False)
        return response

    except requests.exceptions.Timeout:
        raise TimeoutError
    except requests.RequestException as e:
        raise ApiError(e)
    except Exception as e:
        raise


def build_headers(resource, action=None, extra_headers=None):
    headers = {'Content-type': 'application/json'}

    if resource.lower() == 'contactslist' and action.lower() == 'csvdata':
        headers = {'Content-type': 'text/plain'}
    elif resource.lower() == 'batchjob' and action.lower() == 'csverror':
        headers = {'Content-type': 'text/csv'}

    if extra_headers:
        headers.update(extra_headers)

    return headers


def build_url(url, method, action=None, resource_id=None, action_id=None):
    if resource_id:
        url += '/%s' % str(resource_id)
    if action:
        url += '/%s' % action
        if action_id:
            url += '/%d' % action_id

    return url


def parse_response(response, debug=False):
    data = response.json()

    if debug:
        logging.debug('REQUEST: %s' % response.request.url)
        logging.debug('REQUEST_HEADERS: %s' % response.request.headers)
        logging.debug('REQUEST_CONTENT: %s' % response.request.body)

        logging.debug('RESPONSE: %s' % response.content)
        logging.debug('RESP_HEADERS: %s' % response.headers)
        logging.debug('RESP_CODE: %s' % response.status_code)

    return data


class ApiError(Exception):
    pass


class AuthorizationError(ApiError):
    pass


class ActionDeniedError(ApiError):
    pass


class CriticalApiError(ApiError):
    pass


class ApiRateLimitError(ApiError):
    pass

class TimeoutError(ApiError):
    pass


class DoesNotExistError(ApiError):
    pass


class ValidationError(ApiError):
    pass
Although that does not answer the underlying question of why did this suddenly start happening and what changed, where?