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
                }
         }
 )
                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.
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)
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 .
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With