简体   繁体   English

.exe中的Python子进程

[英]Python subprocess in .exe

I'm creating a python script that will copy files and folder over the network. 我正在创建一个python脚本,它将通过网络复制文件和文件夹。 it's cross-platform so I make an .exe file using cx_freeze 它是跨平台的所以我使用cx_freeze创建一个.exe文件

I used Popen method of the subprocess module 我使用了子进程模块的Popen方法

if I run .py file it is running as expected but when i create .exe subprocess is not created in the system 如果我运行.py文件它正在按预期运行但是当我创建.exe子进程没有在系统中创建

I've gone through all documentation of subprocess module but I didn't find any solution 我已经浏览了子进程模块的所有文档,但我没有找到任何解决方案

everything else (I am using Tkinter that also works fine) is working in the .exe accept subprocess. 其他一切(我使用Tkinter也工作正常)正在.exe接受子进程中工作。

any idea how can I call subprocess in .exe.file ?? 任何想法如何在.exe.file中调用子进程?

This file is calling another .py file 此文件正在调用另一个.py文件

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()

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 : 例如,如果要查找source_path ,最好使用带break / elsefor循环:

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

Some advice: 一些忠告:

  • 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). 如果你想要可移植代码:避免系统调用(Windows上没有pgrep )。

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. 由于您的应用程序打包在virtualenv中(我假设cx_freeze执行此类操作),因此您无法访问系统范围的Python。 You even don't have that on Windows. 你甚至没有在Windows上拥有它。 So you need to use the packaged Python (this is a best practice anyway). 因此,您需要使用打包的Python(无论如何这是最佳实践)。

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. 如果要像子scheduler.py一样调用Python脚本,这意味着您有两个打包的应用程序:您需要为主应用程序 scheduler.py脚本创建一个exe But, that's not easy to communicate with it. 但是,与它沟通并不容易。

Another solution is to use multiprocessing to spawn a new Python process. 另一种解决方案是使用multiprocessing来生成新的Python进程。 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. multiprocessing模块中介绍了执行此操作的方法。

Basically: 基本上:

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. 当然,我们需要根据您的问题进行调整。

Here is how I would do that (I removed UI-related code for clarity): 以下是我将如何做到这一点(为清楚起见,我删除了与UI相关的代码):

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)

To check if your process is still running, I recommend you to use psutil . 要检查您的进程是否仍在运行,我建议您使用psutil It's really a great tool! 这真是一个很棒的工具!

You can define your scheduler.py script like that: 您可以像这样定义scheduler.py脚本:

def scheduler_copy(source_path):
    ...

Multiprocessing vs Threading Python 多处理与线程Python

Quoting this answer: https://stackoverflow.com/a/3044626/1513933 引用这个答案: https//stackoverflow.com/a/3044626/1513933

The threading module uses threads, the multiprocessing module uses processes. threading模块使用线程, multiprocessing模块使用进程。 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. 这就是全局解释器锁的用途。

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. 你可能需要psutil。

This is not an exact solution you are looking for, but following suggestion should be preferred for two reasons. 这不是您正在寻找的确切解决方案,但出于两个原因,应优先考虑以下建议。

  1. These are more pythonic way 这些都是更加蟒蛇的方式
  2. subprocess is slightly expensive subprocess略贵

Suggestions you can consider 建议你可以考虑

  1. Don't use subprocess for fetching system path. 不要使用子进程来获取系统路径。 Try check os.getenv('PATH') to get env variable & try to find if python is in the path. 尝试检查os.getenv('PATH')以获取env变量并尝试查找python是否在路径中。 For windows, one has to manually add Python path or else you can directly check in Program Files I guess 对于Windows,必须手动添加Python路径,否则您可以直接签入Program Files

  2. For checking process ID's you can try psutils . 要检查进程ID,您可以尝试psutils A wonderful answer is provided here at how do I get the process list in Python? 这里提供了一个很好的答案, 我如何在Python中获取进程列表?

  3. Calling another script from a python script. 从python脚本调用另一个脚本。 This does not look cool. 这看起来不酷。 Not bad, but I would not prefer this at all. 不错,但我根本不喜欢这个。

  4. In above code, line - if sys.platform == "win32": has same value in if and else condition ==> you dont need a conditional statement here. 在上面的代码中,line - if sys.platform == "win32": ifelse condition ==>中的值相同,这里不需要条件语句。

You wrote pretty fine working code to tell you. 你写了很好的工作代码来告诉你。 Keep Coding! 保持编码!

If you want to run a subprocess in an exe file, then you can use 如果要在exe文件中运行子进程,则可以使用

import subprocess

program=('example')
arguments=('/command')
subprocess.call([program, arguments])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM