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

时间:2017-11-06 13:51:05

标签: python python-3.x

背景

我有一个名为server.py的Python 3脚本,它使用内置的http.server模块。该脚本归结为以下内容:

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通过“动态”导入模块来处理GET和POST请求,具体取决于用于请求的URI。

上述工作正常,但是,在创建该脚本之后,需要能够远程更新脚本的整个“包”(即服务器脚本,以及所有“模块脚本”) “即时”加载并位于子文件夹中。

为此,我在Python 3中编写了另一个服务器脚本(称为updater.py),在指示时,它将检索一个zip文件,然后解压缩以覆盖原始server.py脚本以及所有其他相关的脚本和子文件夹。

问题

这一切都很好,但我现在已经撞墙了。我认为,最好的方法是让updater.py脚本控制server.py的运行。它可以关闭server.py,以及在覆盖它之前链接到它的所有内容,然后在覆盖它之后给它一个干净的开始。

在此基础上,我走下去的道路是使用subprocess.Popen来启动服务器,相信我可以在覆盖server.py之前杀死Python进程,但是,这是不按预期工作。这是我用来测试理论的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()

这似乎启动并终止了一个Python进程,但是当这个脚本运行时服务器没有运行,所以我不确定它是什么启动和杀死!键入“start”然后“退出”(从而退出trial.py脚本)允许服务器运行,但我无法理解为什么,因为我认为subprocess.Popen应该导致生成的进程运行独立于父进程?

编辑:感谢@HåkenLid在下面的敏锐观察,我注意到我所做的只是打破了while循环,退出脚本。这让我相信while循环以某种方式阻止子进程运行(因为一旦退出循环,服务器就会启动)。

2 个答案:

答案 0 :(得分:1)

根据我们的讨论,我建议使用某种方法从“server.py”清空stdio缓冲区。如果您还希望能够提供用户输入,则在等待主线程上的用户输入时,您将需要一个线程来执行打印(或者只是将缓冲区清空到黑洞中)。这是我如何做到这一点的粗略想法..

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
# ...

答案 1 :(得分:0)

好的,所以我想我现在已经解决了这个问题。问题是我在/.../myProg -s -i "/.../file with spaces" 循环内调用了input()来阻止子进程执行。在收到一些输入之前,脚本什么都不做。我将while作为循环的一个组成部分,并将调用放在此循环中subprocess.poll()。如果我点击input()几次,服务器将启动,我可以使用它。如果我然后键入<return>,服务器将按预期被杀死。

我不确定这在我的kill脚本的上下文中是如何工作的,但我在“在发布到StackOverflow之前更深入思考”中学到了一个宝贵的教训!

updater.py