Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Twilio Python Module Errors After Compiling

Tags:

python

sms

twilio

I have written a simple program that opens a csv file and text all the numbers in it. I am using Twilio (twilio-python) as a service provider. My code works fine as a python script. However, when I compile the script (using py2exe), the exe file errors. This is the error I receive from the log file....

 Traceback (most recent call last):
 File "sms.py", line 39, in <module>
 File "twilio\rest\resources\messages.pyc", line 112, in create
 File "twilio\rest\resources\base.pyc", line 352, in create_instance
 File "twilio\rest\resources\base.pyc", line 204, in request
 File "twilio\rest\resources\base.pyc", line 129, in make_twilio_request
 File "twilio\rest\resources\base.pyc", line 101, in make_request
 File "httplib2\__init__.pyc", line 1570, in request
 File "httplib2\__init__.pyc", line 1317, in _request
 File "httplib2\__init__.pyc", line 1252, in _conn_request
 File "httplib2\__init__.pyc", line 1021, in connect
 File "httplib2\__init__.pyc", line 80, in _ssl_wrap_socket
 File "ssl.pyc", line 387, in wrap_socket
 File "ssl.pyc", line 141, in __init__
 ssl.SSLError: [Errno 185090050] _ssl.c:340: error:0B084002:x509 certificate               
 routines:X509_load_cert_crl_file:system lib

I don't recieve this error when i am using the un-compiled code (below)

  import sys #2 params --- /path/to/contact/file    --- up to 160 char msg
  import csv
  import time

  from twilio.rest import TwilioRestClient 
  ACCOUNT_SID = "**************************" 
  AUTH_TOKEN  = "**************************" 
  client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN) 

  sys.argv.pop(0)
  contactFile = sys.argv[0]
  sys.argv.pop(0)
  msg = (' ').join(sys.argv)

  print contactFile
  print " "
  print msg

  info = []
  with open(contactFile,'rb') as csvfile:
   reader = csv.reader(csvfile, delimiter=',', quotechar='|')
        for row in reader:
        info.append(row)

  contactCount = len(info)-1

  if contactCount > 0:
     #remove first item from list because its not a value that is needed....
     info.pop(0)

   for i in info:
       print " "
       contactName = i[0]
       phoneNumber = i[1]
       print "Texting " + contactName + "... \n"
       client.messages.create(
       to=phoneNumber, 
       from_="+14782856136",
       body=msg   
       )
       time.sleep(1.5)
    else:
     print("SMSify Error \n The contact file doesn't have any contacts in it.")

Any thoughts on what is going on??

EDIT:

Here is my setup.py file

from distutils.core import setup
import py2exe, sys, os
sys.argv.append('py2exe')
Mydata_files = [('cacert.pem', ['C:\\Python27\\Lib\\site-      
packages\\twilio\\conf\\cacert.pem'])]

setup(
   console=['sms.py'],
   data_files = Mydata_files,
   options={
              "py2exe":{
                    "bundle_files": 1,
                    "compressed": True
                }
         }
 )
like image 282
Daniel Sikes Avatar asked Jan 18 '14 07:01

Daniel Sikes


Video Answer


2 Answers

It's happened because self-signed certificate file missed in bundle.

This problem is same for requests and httplib2 modules.

For example, if you have a file named req_example.py that using request module:

import requests
url = 'https://google.com/'
requests.get(url)

It works when you run it as python req_example.py, but when bundle it, it's not working.

Or if you have a file named http2_example.py that using http2 module:

import httplib2
url = 'https://google.com/'
http = httplib2.Http()
http.request(url)

It works when you run it as python http2_example.py , but when bundle it, it's not working.

To fix that, you have two options, one bad and one good.

  1. Disable verifying SSL certificates:

    To do that for requests module:

    import requests
    url = 'https://google.com/'
    requests.get(url, verify=False)
    

    And for httplib2 module:

    import httplib2
    http = httplib2.Http(disable_ssl_certificate_validation=True)
    http.request(url)
    
  2. Add self-signed certificate file to bundle:

    For requests module, the file cacert.pem is located in:

    .../PythonXX/lib/site-packages/requests/cacert.pem

    And for httplib2 module is in:

    .../PythonXX/lib/site-packages/httplib2/cacerts.txt

    For each of them, you can copy it to inside of your project (or just address to it),

    And config setup.py for including it:

    setup(console=['temp.py'],
        # for `requests` module
        data_files=['cacert.pem'] ) # or data_files=['cacerts.txt'] ) for `httplib2`
    

    And change your code to using that, for request module:

    import os 
    import requests 
    url = 'https://google.com/' 
    cert ='cacert.pem'
    # or os.environ['REQUESTS_CA_BUNDLE'] = cert 
    os.environ['REQUESTS_CA_BUNDLE'] = os.path.join(os.getcwd(), cert)
    requests.get(url)
    

    And for httplib2 module:

    import httplib2 
    cert = 'cacerts.txt' 
    http = httplib2.Http(ca_certs=cert) 
    http.request(url)
    

    Or if your httplib2 version is 0.8, you can create a file that should named ca_certs_locater.py, and define a get function, that return path of ca_certs file.

    def get():
        return 'cacerts.txt'
    

Ok, now for your error, and for twilio module, it use httplib2, and cacert.pem of it is in:

.../twilio/conf/cacert.pem

So you need to add this file to setup.py as described above.

But twilio itself has a function with name get_cert_file that pass ca_cert file to httplib2.

I think if you using ca_certs_locater.py that described above, it also will works for that, but if not, you have yet an ugly option, so you can monkey patch get_cert_file function of twilio:

from twilio.rest.resources.base import get_cert_file
get_cert_file = lambda: 'cacert.pem'

Note that this may an issue for twilio or even for py2exe or PyInstaller .

like image 148
Omid Raha Avatar answered Oct 20 '22 17:10

Omid Raha


I had the same problem with twilio and pyinstaller, and was able to fix it by modifying the base.py module in twilio\rest\resources:

   def get_cert_file():
 """ Get the cert file location or bail """
# XXX - this currently fails test coverage because we don't actually go
# over the network anywhere. Might be good to have a test that stands up a
# local server and authenticates against it.
  try:
    # Apparently __file__ is not available in all places so wrapping this
    # in a try/catch
    current_path = os.path.realpath(__file__)
    #ca_cert_path = os.path.join(current_path, "..", "..", "..",    (old path)
     #                           "conf", "cacert.pem")
    ca_cert_path = os.getcwd() + '\Config\cacert.pem' (my new path)

    return os.path.abspath(ca_cert_path)

(I am storing my cacert.pem file in a Config folder off my main script directory)

like image 41
Graham Avatar answered Oct 20 '22 16:10

Graham