Search code examples
pythonmysqlmysql-pythoncx-freeze

Python: Can't run Python executable file created by cx_freeze (with text file)


I have written a Python program to take the backup of all objects of MySQL db individually. The program works fine when run on my machine, but when I use cx_freeze to create an executable file, it fails to run on the other machine. I found the cause of the error to be the text file for configuration. When I don't write a configuration file, the program runs fine, else it gives an error.

My code for extraction: mysql_extractor.py. Please have a look at the filter_parsing function, the probable source of error.

[mysql_extractor.py]

#!/usr/bin/env python3

"""
__title__        = "mysql_extractor.py"
__description__  = "Script to extract objects of MySQL database individually"

"""

from __future__ import print_function
import argparse
import os
import sys
import pymysql
import encodings.idna

class MySQLExtractor:

############################################
#
#    PUBLIC METHODS
#
############################################

   directory = ""
   dumpcmd   = ""

   def parse_argument(self):
      self.parser = argparse.ArgumentParser(description="MySQL dump process")
      args_conn = self.parser.add_argument_group(title="Database Connection")
      args_conn.add_argument('--host', dest="hostname", default="127.0.0.1", help="Host name or IP Address")
      args_conn.add_argument('-u', '--username', dest="username", default="root", help="Database user name.")
      args_conn.add_argument('--password', dest="password", default="", help="Password for the given user.")
      args_conn.add_argument('-d', '--db', dest="db", default="test", help="Database name to connect to.")

      args_filter = self.parser.add_argument_group(title="Filters", description="All object names given in any filter MUST be fully schema qualified.")
      args_filter.add_argument('--gettables', dest="gettables", action="store_true", help="Get all tables only")

      self.args = self.parser.parse_args()
   # end _parse_arguments()

   def gettables(self):
      try:
         print ("Dumping Tables..")
         conn = pymysql.connect(host=self.args.hostname, user=self.args.username, passwd=self.args.password, database=self.args.db)
         cursor = conn.cursor()
         if not os.path.exists("tables"):
            os.makedirs("tables")
            os.chdir("tables")
            query = "show full tables where Table_Type != 'VIEW'"
            cursor.execute(query)
            for row in cursor:
               dumpcmd = self.dumpcmd
               dumpcmd +=  " " + row[0] + " --no-data > " + row[0] + ".sql"
               os.system(dumpcmd)
            os.chdir(self.directory)
         cursor.close()
         conn.close()
      except pymysql.Error as e:
         print("Error lin connecting to the DB: " + str(e) )
         sys.exit(2)
   # end gettables

   def filter_parsing(self):
      """
      Some more code here, but not relevant to the question
      """
      self.directory = self.args.db
      os.makedirs(self.directory)
      os.chdir(self.directory)

      # Create config file <Probable source of error>
      filename = "my_config"
      f = open(filename, 'w')
      f.write("[mysqldump]\n")
      f.write("host='"+ self.args.hostname + "'\n")
      f.write("user='"+ self.args.username + "'\n")
      f.write("password='"+ self.args.password + "'\n")
      f.close()

      self.directory = os.getcwd()
      self.dumpcmd = "mysqldump --defaults-file=" + self.directory + "/my_config" +" --skip-dump-date " + self.args.db

      # If the following command is used and config file is not created, it gives no error.
      # self.dumpcmd = "mysqldump -u " + self.args.username + " -h " + self.args.hostname + " --password=" + self.args.password + "  --skip-dump-date " + self.args.db 

      #gettables is selected
      if self.args.gettables==True:
         self.gettables()
         return
   # end filter_parsing

#end class MySQLExtractor


p = MySQLExtractor()
p.parse_argument()
try:
   p.filter_parsing()
finally:
   print ("Done")      

This code works fine when run on my machine. But when I create an executable file and run it on other machine, it gives error(s). Here is my setup.py file

[setup.py]

import sys
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {"packages": ["os","MySQLdb"], "excludes": ["tkinter"]}

# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(  name = "extractor",
        version = "0.1",
        description = "My GUI application!",
        options = {"build_exe": build_exe_options},
        executables = [Executable("mysql_extractor.py", base=base)])

The error thrown is:

Traceback (most recent call last):
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1191, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1161, in _load_backward_compatible
  File "/usr/lib64/python3.4/site-packages/cx_Freeze-5.0-py3.4-linux-x86_64.egg/cx_Freeze/initscripts/__startup__.py", line 12, in <module>
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1191, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1161, in _load_backward_compatible
  File "/usr/lib64/python3.4/site-packages/cx_Freeze-5.0-py3.4-linux-x86_64.egg/cx_Freeze/initscripts/Console.py", line 21, in <module>
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1191, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1161, in _load_backward_compatible
  File "/home/AD/abhishek.bhola/Documents/tmp/tmp_test.py", line 97, in <module>
  File "/home/AD/abhishek.bhola/Documents/tmp/tmp_test.py", line 74, in filter_parsing
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2222, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 2164, in _find_spec
  File "<frozen importlib._bootstrap>", line 1940, in find_spec
  File "<frozen importlib._bootstrap>", line 1916, in _get_spec
  File "<frozen importlib._bootstrap>", line 1897, in _legacy_get_spec
  File "<frozen importlib._bootstrap>", line 863, in spec_from_loader
  File "<frozen importlib._bootstrap>", line 904, in spec_from_file_location
OSError: zipimport: can not open file ./lib64/python34.zip

Solution

  • This problem has been detected and is already solved in the source repository. The problem is the os.chdir() call that is made when using relative paths to start up the executable. You have three choices for now:

    • use an absolute path for starting the executable
    • get the updated source and build it (then you can use relative paths)
    • wait for the next release of cx_Freeze to be made (hopefully soon!)