summaryrefslogtreecommitdiff
path: root/sphinx/web/serve.py
blob: a1e7d78b486a8fa0fab980f88a40c606012fcd7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# -*- coding: utf-8 -*-
"""
    sphinx.web.serve
    ~~~~~~~~~~~~~~~~

    This module optionally wraps the `wsgiref` module so that it reloads code
    automatically. Works with any WSGI application but it won't help in non
    `wsgiref` environments. Use it only for development.

    :copyright: 2007-2008 by Armin Ronacher, Georg Brandl.
    :license: BSD.
"""
import os
import sys
import time
import thread


def reloader_loop(extra_files):
    """When this function is run from the main thread, it will force other
    threads to exit when any modules currently loaded change.

    :param extra_files: a list of additional files it should watch.
    """
    mtimes = {}
    while True:
        for filename in filter(None, [getattr(module, '__file__', None)
                                      for module in sys.modules.values()] +
                               extra_files):
            while not os.path.isfile(filename):
                filename = os.path.dirname(filename)
                if not filename:
                    break
            if not filename:
                continue

            if filename[-4:] in ('.pyc', '.pyo'):
                filename = filename[:-1]

            mtime = os.stat(filename).st_mtime
            if filename not in mtimes:
                mtimes[filename] = mtime
                continue
            if mtime > mtimes[filename]:
                sys.exit(3)
        time.sleep(1)


def restart_with_reloader():
    """Spawn a new Python interpreter with the same arguments as this one,
    but running the reloader thread."""
    while True:
        print '* Restarting with reloader...'
        args = [sys.executable] + sys.argv
        if sys.platform == 'win32':
            args = ['"%s"' % arg for arg in args]
        new_environ = os.environ.copy()
        new_environ['RUN_MAIN'] = 'true'
        exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)
        if exit_code != 3:
            return exit_code


def run_with_reloader(main_func, extra_watch):
    """
    Run the given function in an independent python interpreter.
    """
    if os.environ.get('RUN_MAIN') == 'true':
        thread.start_new_thread(main_func, ())
        try:
            reloader_loop(extra_watch)
        except KeyboardInterrupt:
            return
    try:
        sys.exit(restart_with_reloader())
    except KeyboardInterrupt:
        pass


def run_simple(hostname, port, make_app, use_reloader=False,
               extra_files=None):
    """
    Start an application using wsgiref and with an optional reloader.
    """
    from wsgiref.simple_server import make_server
    def inner():
        application = make_app()
        print '* Startup complete.'
        srv = make_server(hostname, port, application)
        try:
            srv.serve_forever()
        except KeyboardInterrupt:
            pass
    if os.environ.get('RUN_MAIN') != 'true':
        print '* Running on http://%s:%d/' % (hostname, port)
    if use_reloader:
        run_with_reloader(inner, extra_files or [])
    else:
        inner()