.exe中的Python子进程 [英] Python subprocess in .exe

查看:91
本文介绍了.exe中的Python子进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个python脚本,该脚本将通过网络复制文件和文件夹.它是跨平台的,所以我使用 cx_freeze

I'm creating a python script that will copy files and folder over the network. it's cross-platform so I make an .exe file using cx_freeze

我使用了子流程模块的Popen方法

I used Popen method of the subprocess module

如果我运行 .py 文件,该文件将按预期运行,但是当我创建 .exe 子进程时没有在系统中创建

if I run .py file it is running as expected but when i create .exe subprocess is not created in the system

我已经浏览了子流程模块的所有文档,但是没有找到任何解决方法

I've gone through all documentation of subprocess module but I didn't find any solution

.exe接受子进程中的所有其他功能(我正在使用Tkinter的同时也可以正常工作).

everything else (I am using Tkinter that also works fine) is working in the .exe accept subprocess.

任何想法如何在.exe.file中调用子进程??

any idea how can I call subprocess in .exe.file ??

此文件正在调用另一个.py文件

This file is calling another .py file

def start_scheduler_action(self, scheduler_id, scheduler_name, list_index):
       scheduler_detail=db.get_scheduler_detail_using_id(scheduler_id)
        for detail in scheduler_detail:
            source_path=detail[2]
        if not os.path.exists(source_path):
            showerror("Invalid Path","Please select valid path", parent=self.new_frame)
            return

        self.forms.new_scheduler.start_scheduler_button.destroy()

        #Create stop scheduler button
        if getattr(self.forms.new_scheduler, "stop_scheduler_button", None)==None:

            self.forms.new_scheduler.stop_scheduler_button = tk.Button(self.new_frame, text='Stop scheduler', width=10, command=lambda:self.stop_scheduler_action(scheduler_id, scheduler_name, list_index))
            self.forms.new_scheduler.stop_scheduler_button.grid(row=11, column=1, sticky=E, pady=10, padx=1)

        scheduler_id=str(scheduler_id)

        # Get python paths
        if sys.platform == "win32":
            proc = subprocess.Popen(['where', "python"], env=None, stdout=subprocess.PIPE)

        else:
            proc = subprocess.Popen(['which', "python"], env=None,stdout=subprocess.PIPE)

        out, err = proc.communicate()

        if err or not out:
            showerror("", "Python not found", parent=self.new_frame)

        else:

            try:
                paths = out.split(os.pathsep)

                # Create python path
                python_path = (paths[len(paths) - 1]).split('\n')[0]

                cmd = os.path.realpath('scheduler.py')
                #cmd='scheduler.py'

                if sys.platform == "win32":
                    python_path=python_path.splitlines()

                else:
                    python_path=python_path

                # Run the scheduler file using scheduler id

                proc = subprocess.Popen([python_path, cmd, scheduler_id], env=None, stdout=subprocess.PIPE)


                message="Started the scheduler : %s" %(scheduler_name)
                showinfo("", message, parent=self.new_frame)

                #Add process id to scheduler table
                process_id=proc.pid
                #showinfo("pid", process_id, parent=self.new_frame)
                def get_process_id(name):
                    child = subprocess.Popen(['pgrep', '-f', name], stdout=subprocess.PIPE, shell=False)
                    response = child.communicate()[0]
                    return [int(pid) for pid in response.split()]

                print(get_process_id(scheduler_name))

                # Add the process id in database
                self.db.add_process_id(scheduler_id, process_id)

                # Add the is_running status in database
                self.db.add_status(scheduler_id)

            except Exception as e:

                showerror("", e)

该文件名为:

And this file is called:

def scheduler_copy():

    date= strftime("%m-%d-%Y %H %M %S", localtime())
    logFile = scheduler_name + "_"+scheduler_id+"_"+ date+".log"
    #file_obj=open(logFile, 'w')

    # Call __init__ method of xcopy file 
    xcopy=XCopy(connection_ip, username , password, client_name, server_name, domain_name)
    check=xcopy.connect()

    # Cretae a log file for scheduler
    file_obj=open(logFile, 'w')

    if check is False:

        file_obj.write("Problem in connection..Please check connection..!!")
        return

    scheduler_next_run=schedule.next_run()
    scheduler_next_run="Next run at: " +str(scheduler_next_run)

    # If checkbox_value selected copy all the file to new directory
    if checkbox_value==1:
        new_destination_path=xcopy.create_backup_directory(share_folder, destination_path, date)
    else:
        new_destination_path=destination_path

    # Call backup method for coping data from source to destination
    try:
        xcopy.backup(share_folder, source_path, new_destination_path, file_obj, exclude)
        file_obj.write("Scheduler completed successfully..\n")

    except Exception as e:

        # Write the error message of the scheduler to log file
        file_obj.write("Scheduler failed to copy all data..\nProblem in connection..Please check connection..!!\n")
        # #file_obj.write("Error while scheduling")
        # return

    # Write the details of scheduler to log file
    file_obj.write("Total skipped unmodified file:")
    file_obj.write(str(xcopy.skipped_unmodified_count))
    file_obj.write("\n")
    file_obj.write("Total skipped file:")
    file_obj.write(str(xcopy.skipped_file))
    file_obj.write("\n")
    file_obj.write("Total copied file:")
    file_obj.write(str(xcopy.copy_count))
    file_obj.write("\n")
    file_obj.write("Total skipped folder:")
    file_obj.write(str(xcopy.skipped_folder))
    file_obj.write("\n")
    # file_obj.write(scheduler_next_run)
    file_obj.close()

推荐答案

您的源代码中有些笨拙,但我不会花时间在上面.例如,如果要查找 source_path ,最好将for循环与break/else一起使用:

There is some awkwardness in your source code, but I won't spend time on that. For instance, if you want to find the source_path, it's better to use a for loop with break/else:

for detail in scheduler_detail:
    source_path = detail[2]
    break  # found
else:
    # not found: raise an exception
    ...

一些建议:

  • 尝试将用户界面代码和子处理分开,避免两者混用.
  • 使用异常和异常处理程序.
  • 如果要使用可移植的代码:避免系统调用(在Windows中没有pgrep).
  • Try to separate the user interface code and the sub-processing, avoid mixing the two.
  • Use exceptions and exception handlers.
  • If you want portable code: avoid system call (there are no pgrep on Windows).

由于您的应用程序打包在virtualenv中(我假设cx_freeze做这种事情),因此您无权访问系统范围的Python.您甚至在Windows上都没有.因此,您需要使用打包的Python(无论如何这都是最佳做法).

Since your application is packaged in a virtualenv (I make the assumption cx_freeze does this kind of thing), you have no access to the system-wide Python. You even don't have that on Windows. So you need to use the packaged Python (this is a best practice anyway).

如果要像子进程一样调用Python脚本,则意味着您有两个打包的应用程序:您需要为主应用程序创建exe,为scheduler.py脚本创建.但是,要与之交流并不容易.

If you want to call a Python script like a subprocess, that means you have two packaged applications: you need to create an exe for the main application and for the scheduler.py script. But, that's not easy to communicate with it.

另一种解决方案是使用multiprocessing生成新的Python进程.由于您不想等待处理结束(可能很长),因此需要创建守护进程. multiprocessing 模块.

Another solution is to use multiprocessing to spawn a new Python process. Since you don't want to wait for the end of processing (which may be long), you need to create daemon processes. The way to do that is explained in the multiprocessing module.

基本上:

import time
from multiprocessing import Process

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.daemon = True
    p.start()

    # let it live and die, don't call: `p.join()`
    time.sleep(1)

当然,我们需要适应您的问题.

Of course, we need to adapt that with your problem.

这就是我要怎么做(为了清楚起见,我删除了与UI相关的代码):

Here is how I would do that (I removed UI-related code for clarity):

import scheduler


class SchedulerError(Exception):
    pass


class YourClass(object):
    def start_scheduler_action(self, scheduler_id, scheduler_name, list_index):
        scheduler_detail = db.get_scheduler_detail_using_id(scheduler_id)
        for detail in scheduler_detail:
            source_path = detail[2]
            break
        else:
            raise SchedulerError("Invalid Path", "Missing source path", parent=self.new_frame)

        if not os.path.exists(source_path):
            raise SchedulerError("Invalid Path", "Please select valid path", parent=self.new_frame)

        p = Process(target=scheduler.scheduler_copy, args=('source_path',))
        p.daemon = True
        p.start()

        self.db.add_process_id(scheduler_id, p.pid)

要检查进程是否仍在运行,建议您使用 psutil .这真的是一个很棒的工具!

To check if your process is still running, I recommend you to use psutil. It's really a great tool!

您可以这样定义scheduler.py脚本:

def scheduler_copy(source_path):
    ...


多处理与线程Python

引用此答案: https://stackoverflow.com/a/3044626/1513933

threading模块使用线程,multiprocessing模块使用进程.区别在于线程在相同的内存空间中运行,而进程具有单独的内存.这使得在具有多处理的进程之间共享对象更加困难.由于线程使用相同的内存,因此必须采取预防措施,否则两个线程将同时写入同一内​​存.这就是全局解释器锁的作用.

The threading module uses threads, the multiprocessing module uses processes. The difference is that threads run in the same memory space, while processes have separate memory. This makes it a bit harder to share objects between processes with multiprocessing. Since threads use the same memory, precautions have to be taken or two threads will write to the same memory at the same time. This is what the global interpreter lock is for.

在这里,与多线程相比,多处理的优势在于您可以杀死(或终止)一个进程.你不能杀死线程.您可能需要psutil.

Here, the advantage of multiprocessing over multithreading is that you can kill (or terminate) a process; you can't kill a thread. You may need psutil for that.

这篇关于.exe中的Python子进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆