简体   繁体   English

如何使用Python脚本启动和停止包含“ http.server.serveforever”的Python脚本

[英]How to use a Python script to start and stop a Python script containing “http.server.serveforever”

Background 背景

I have a Python 3 script called server.py that uses the built-in http.server module. 我有一个名为server.py的Python 3脚本,它使用内置的http.server模块。 The script boils down to the following: 该脚本可归结为以下内容:

from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler

class MyRequestHandler (BaseHTTPRequestHandler):
    def do_POST(self):
        # Code omitted for brevity

    def do_GET(self):
        # Code omitted for brevity

def start_server():

    # Begin serving
    # -------------
    server = HTTPServer(('', port), MyRequestHandler)
    print("server now running on port {0} ...".format(port))

    server.serve_forever()

# Start the Server
# ----------------
if __name__ == '__main__':
    start_server()

MyRequestHandler handles GET and POST requests by importing modules "on-the-fly" depending on the URI that was used for the request. MyRequestHandler通过“即时”导入模块来处理GET和POST请求,具体取决于用于请求的URI。

The above works fine, however, subsequent to the creation of that script, there has been a requirement to be able to remotely update the whole "package" of scripts (ie the server script, plus all of the "module scripts" that are loaded up "on-the-fly" and are located in sub-folders). 上面的方法工作正常,但是,在创建该脚本之后,需要能够远程更新脚本的整个“包”(即服务器脚本以及已加载的所有“模块脚本”即时”,位于子文件夹中)。

For this, I have written another server script in Python 3 (called updater.py ) which, when instructed, will retrieve a zip file, which it then unzips to overwrite the original server.py script along with all the other associated scripts and sub-folders. 为此,我在Python 3中编写了另一个服务器脚本(称为updater.py ),该脚本将在得到指示时检索一个zip文件,然后将其解压缩以覆盖原始的server.py脚本以及所有其他相关的脚本和子脚本。 -文件夹。

Question

This all works great, but I have now hit a wall. 这一切都很好,但是我现在碰壁了。 The best approach to this, I thought, would be to have the updater.py script control the running of server.py . 我认为,最好的方法是让updater.py脚本控制server.py的运行。 It could shut down the server.py , and everything linked to it before overwriting it all and then give it a clean start after it is overwritten. 它可以关闭server.py以及与其链接的所有内容,然后再覆盖所有内容,然后在覆盖后重新启动它。

On that basis, the road that I have gone down is to use subprocess.Popen to start the server, believing that I could just kill the Python process before overwriting the server.py stuff, however, this is not working as hoped. 在此基础上,我走过的路是使用subprocess.Popen启动服务器,认为我可以在覆盖server.py之前杀死Python进程,但是,这并没有达到预期的效果。 Here is my trial.py script that I have written to test the theory: 这是我为测试该理论而编写的trial.py脚本:

import sys
import subprocess

def main():
    def start_process():
        proc = subprocess.Popen([sys.executable, 'server.py'])
        print("Started process:")
        print(proc.pid)
        return proc

    def kill_process(the_process):
        print("Killing process:")
        print(the_process.pid)
        the_process.kill()

    process = None

    while True:
        user_input = input("Type something: ")

        if user_input == 'start':
            process = start_process()
        if user_input == 'kill':
            kill_process(process)
        if user_input == 'exit':
            break

if __name__ == '__main__':
    main()

This does appear to start and kill a Python process, but the server is not running while this script is running, so I am not sure what it is starting and killing! 这似乎确实可以启动并杀死一个Python进程,但是此脚本在运行时服务器未在运行,因此我不确定它正在启动并终止什么! Typing "start" and then "exit" (and thus quitting the trial.py script) allows the server to run, though I can't understand why, since I thought subprocess.Popen should cause the spawned process to run independently of the parent process? 键入“开始”然后“退出”(从而退出trial.py脚本)将允许服务器运行,尽管我不明白为什么,因为我认为subprocess.Popen应该导致生成的进程独立于父进程运行处理?

edit: Thanks to @Håken Lid's astute observation below, I noticed that all I was doing is breaking out of the while loop, not exiting the script. 编辑:由于下面@HåkenLid的敏锐观察,我注意到我所做的一切只是打破了while循环, 而不是退出脚本。 This leads me to believe that the while loop is somehow blocking the sub-process from running (since once the loop is exited, the server will start). 这使我相信while循环以某种方式阻止了子进程的运行(因为一旦退出循环,服务器将启动)。

per our discussion, I'd recommend some way to empty the stdio buffers from "server.py". 根据我们的讨论,我建议使用某种方法从“ server.py”中清空stdio缓冲区。 If you want to also be able to give user input, you'll need a thread to do the printing (or just to empty the buffers into a black hole) while you wait for user input on the main thread. 如果还希望能够提供用户输入,则在等待用户在主线程上输入时,需要一个线程来进行打印(或只是将缓冲区清空到黑洞中)。 Here's a rough idea of how I might do it.. 这是我可能如何做的一个大概想法。

import sys
import subprocess
from threading import Thread 
#This could probably be solved with async, but I still
#haven't learned async as well as I know threads

def main():

    def start_process():
        proc = subprocess.Popen([sys.executable, 'server.py'], 
                                stdin=subprocess.PIPE, 
                                stdout=subprocess.PIPE)
        print("Started process:")

        def buf_readerd(proc, inbuf, outbuf):
            while proc.poll() is None:
                outbuf.write(inbuf.readline()) #may need to add a newline.. I'm not sure if readline ends in a \n

        stdoutd = Thread(target=buf_readerd, args=(proc, proc.stdout, sys.stdout), daemon=True)
        stderrd = Thread(target=buf_readerd, args=(proc, proc.stderr, sys.stderr), daemon=True)
        stdoutd.start()
        stderrd.start()
        print("started pipe reader daemons")

        print(proc.pid)
        return proc
# ...

Okay, so I think I have fixed this myself now. 好的,我想我已经自己修复此问题。 The problem was that I was blocking the sub-process execution with the call to input() inside the while loop. 问题是我在while循环内通过调用input()阻止了子流程的执行。 The script does nothing until it receives some input. 在收到一些输入之前,脚本不执行任何操作。 I made subprocess.poll() an integral part of the loop and put the call to input() inside this loop. 我使subprocess.poll()成为循环的组成部分,并将对input()的调用置于该循环内。 If I hit <return> a few times, the server will start and I can use it. 如果几次击<return> ,服务器将启动并且可以使用它。 If I then type kill , the server will be killed, as expected. 如果再键入kill ,则服务器将按预期终止。

I am not sure how this will work in the context of my updater.py script, but I have learnt a valuable lesson in "thinking more deeply before posting to StackOverflow"! 我不确定在我的updater.py脚本的上下文中这将如何工作,但是我已经学到了一个有价值的课程,即“在发布到StackOverflow之前要更深入地思考”!

import sys
import subprocess

def main():
    def start_process():
        proc = subprocess.Popen([sys.executable, 'server.py'])
        print("Started process:")
        print(proc.pid)
        return proc

    def kill_process(the_process):
        print("Killing process:")
        print(the_process.pid)
        the_process.kill()

    user_input = input("Type something: ")

    if user_input == 'start':
        process = start_process()

        while process.poll() is None:
            user_input = input()
            if user_input == 'kill' or user_input == 'exit':
                kill_process(process)

if __name__ == '__main__':
    main()

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

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