学以致用

focus on Python , C++, and some interest in Go and R

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

在使用cx_freeze将python程序转换成exe的时候碰到如下错误:

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"], 
                        "include-files": ["security/cert.pem", "security/privatekey.pem"],
                        # "create_shared_zip": True,
                        # "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 = "test",
        version = "0.1",
        description = "My GUI application!",
        options = {"build_exe": build_exe_options},
        executables = [Executable("test.py", base=base)])

对于error: error in setup script: command 'build_exe' has no such option 'include-files'的错误觉得很是奇怪,因为在使用help查看的时候发现include-files是有效的:


仔细看了看源码才发现,原来是cx_freeze同distutils配合的问题。
cx_freeze最终还是使用distutils来实现打包分发,
cx_freeze将python转换为exe时主要是调用了distutils.dist.py中def run_command(self, command)和def _set_command_options(self, command_obj, option_dict=None):方法

def _set_command_options(self, command_obj, option_dict=None):
        """Set the options for 'command_obj' from 'option_dict'.  Basically
        this means copying elements of a dictionary ('option_dict') to
        attributes of an instance ('command').

        'command_obj' must be a Command instance.  If 'option_dict' is not
        supplied, uses the standard option dictionary for this command
        (from 'self.command_options').
        """
        command_name = command_obj.get_command_name()
        if option_dict is None:
            option_dict = self.get_option_dict(command_name)

        if DEBUG:
            self.announce("  setting options for '%s' command:" % command_name)
        for (option, (source, value)) in option_dict.items():
            # print 'option= ', option
            # print '(source, value) = ', (source, value)
            if DEBUG:
                self.announce("    %s = %s (from %s)" % (option, value,
                                                         source))
            try:
                bool_opts = map(translate_longopt, command_obj.boolean_options)
            except AttributeError:
                bool_opts = []
            try:
                neg_opt = command_obj.negative_opt
            except AttributeError:
                neg_opt = {}

            try:
                is_string = isinstance(value, str)
                if option in neg_opt and is_string:
                    setattr(command_obj, neg_opt[option], not strtobool(value))
                elif option in bool_opts and is_string:
                    setattr(command_obj, option, strtobool(value))
                elif hasattr(command_obj, option):
                    setattr(command_obj, option, value)
                else:
                    raise DistutilsOptionError, \
                          ("error in %s: command '%s' has no such option '%s'"
                           % (source, command_name, option))
            except ValueError, msg:
                raise DistutilsOptionError, msg

def run_command(self, command):
        """Do whatever it takes to run a command (including nothing at all,
        if the command has already been run).  Specifically: if we have
        already created and run the command named by 'command', return
        silently without doing anything.  If the command named by 'command'
        doesn't even have a command object yet, create one.  Then invoke
        'run()' on that command object (or an existing one).
        """
        # Already been here, done that? then return silently.
        if self.have_run.get(command):
            return

        log.info("running %s", command)
        cmd_obj = self.get_command_obj(command)
        cmd_obj.ensure_finalized()
        # print "begin to run"
        cmd_obj.run()
        self.have_run[command] = 1

通过_set_command_options源码可以看出,error: error in setup script: command 'build_exe' has no such option 'include-files'的错误是出在hasattr(command_obj, option)部分,也就是说distutils.dist在执行命令时先去检测Command是否有相应的属性,我们来看一下cx_freeze的class build_exe(distutils.core.Command)定义:

def initialize_options(self):
        self.optimize = 0
        self.build_exe = None
        self.excludes = []
        self.includes = []
        self.packages = []
        self.namespace_packages = []
        self.replace_paths = []
        self.compressed = None
        self.copy_dependent_files = None
        self.init_script = None
        self.base = None
        self.path = None
        self.create_shared_zip = None
        self.append_script_to_exe = None
        self.include_in_shared_zip = None
        self.include_msvcr = None
        self.icon = None
        self.constants = []
        self.include_files = []
        self.zip_includes = []
        self.bin_excludes = []
        self.bin_includes = []
        self.bin_path_includes = []
        self.bin_path_excludes = []
        self.silent = None

通过代码可以看出,build_exe并没有include-files,通过追溯可以发现build_exe有的是include_files
对于其他的参数,也会存在这样的问题,比如在--help中看到的create-shared-zip,实际上在setup.py中应该是create_shared_zip,也就是说需要将-转换成_,这点需要特别注意
就其原因是因为python的命名规范,连字符(-)不允许出现在python的变量定义中,但下划线(_)可以



posted on 2013-03-21 16:26  Jerry.Kwan  阅读(2229)  评论(0编辑  收藏  举报