ccplugin.py
author Christophe de Vienne <christophe@unlish.com>
Thu, 18 Sep 2014 22:33:04 +0200
changeset 11637 a9cde6a3394c
parent 11633 ffe4040cf4a2
child 11638 12de153c0d0e
permissions -rw-r--r--
Implements auto-reload and daemon mode. Heavily inspired by pyramid pserve, with pieces of code taken from it. auto-reload Start the server in a subprocess that auto-stops when a file is modified, and exit with a specific code. daemon mode Uses some code from pserve, but use the cw configuratione so the command is compatible with 'status' and 'stop' commands.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     1
"""
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     2
Provides a 'pyramid' command as a replacement to the 'start' command.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     3
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     4
The reloading strategy is heavily inspired by (and partially copied from)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     5
the pyramid script 'pserve'.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     6
"""
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     7
import atexit
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     8
import errno
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
     9
import os
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    10
import signal
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    11
import sys
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    12
import time
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    13
import threading
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    14
import subprocess
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    15
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    16
from cubicweb import BadCommandUsage, ExecutionError
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    17
from cubicweb.cwconfig import CubicWebConfiguration as cwcfg
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    18
from cubicweb.cwctl import CWCTL, InstanceCommand, init_cmdline_log_threshold
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    19
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    20
from pyramid_cubicweb import make_cubicweb_application
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    21
import waitress
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    22
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    23
MAXFD = 1024
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    24
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    25
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    26
class PyramidStartHandler(InstanceCommand):
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    27
    """Start an interactive pyramid server.
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    28
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    29
    This command requires http://hg.logilab.org/review/pyramid_cubicweb/
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    30
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    31
    <instance>
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    32
      identifier of the instance to configure.
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    33
    """
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    34
    name = 'pyramid'
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    35
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    36
    options = (
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    37
        ("debug",
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    38
         {'short': 'D', 'action': 'store_true',
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    39
          'help': 'start server in the foreground.'}),
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    40
        ('reload',
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    41
         {'action': 'store_true',
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    42
          'help': 'Restart the server if any source file is changed'}),
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    43
        ('reload-interval',
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    44
         {'type': 'int', 'default': 1,
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    45
          'help': 'Interval, in seconds, between file modifications checks'}),
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    46
        ('loglevel',
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    47
         {'short': 'l', 'type': 'choice', 'metavar': '<log level>',
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    48
          'default': None, 'choices': ('debug', 'info', 'warning', 'error'),
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    49
          'help': 'debug if -D is set, error otherwise',
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    50
          }),
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    51
    )
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    52
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    53
    _reloader_environ_key = 'CW_RELOADER_SHOULD_RUN'
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    54
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    55
    def debug(self, msg):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    56
        print('DEBUG - %s' % msg)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    57
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    58
    def info(self, msg):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    59
        print('INFO - %s' % msg)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    60
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    61
    def ordered_instances(self):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    62
        instances = super(PyramidStartHandler, self).ordered_instances()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    63
        if (self['debug'] or self['reload']) and len(instances) > 1:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    64
            raise BadCommandUsage(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    65
                '--debug and --reload can be used on a single instance only')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    66
        return instances
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    67
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    68
    def quote_first_command_arg(self, arg):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    69
        """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    70
        There's a bug in Windows when running an executable that's
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    71
        located inside a path with a space in it.  This method handles
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    72
        that case, or on non-Windows systems or an executable with no
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    73
        spaces, it just leaves well enough alone.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    74
        """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    75
        if (sys.platform != 'win32' or ' ' not in arg):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    76
            # Problem does not apply:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    77
            return arg
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    78
        try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    79
            import win32api
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    80
        except ImportError:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    81
            raise ValueError(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    82
                "The executable %r contains a space, and in order to "
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    83
                "handle this issue you must have the win32api module "
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    84
                "installed" % arg)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    85
        arg = win32api.GetShortPathName(arg)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    86
        return arg
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    87
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    88
    def _remove_pid_file(self, written_pid, filename):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    89
        current_pid = os.getpid()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    90
        if written_pid != current_pid:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    91
            # A forked process must be exiting, not the process that
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    92
            # wrote the PID file
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    93
            return
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    94
        if not os.path.exists(filename):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    95
            return
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    96
        with open(filename) as f:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    97
            content = f.read().strip()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    98
        try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
    99
            pid_in_file = int(content)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   100
        except ValueError:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   101
            pass
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   102
        else:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   103
            if pid_in_file != current_pid:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   104
                msg = "PID file %s contains %s, not expected PID %s"
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   105
                self.out(msg % (filename, pid_in_file, current_pid))
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   106
                return
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   107
        self.info("Removing PID file %s" % filename)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   108
        try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   109
            os.unlink(filename)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   110
            return
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   111
        except OSError as e:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   112
            # Record, but don't give traceback
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   113
            self.out("Cannot remove PID file: (%s)" % e)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   114
        # well, at least lets not leave the invalid PID around...
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   115
        try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   116
            with open(filename, 'w') as f:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   117
                f.write('')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   118
        except OSError as e:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   119
            self.out('Stale PID left in file: %s (%s)' % (filename, e))
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   120
        else:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   121
            self.out('Stale PID removed')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   122
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   123
    def record_pid(self, pid_file):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   124
        pid = os.getpid()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   125
        self.debug('Writing PID %s to %s' % (pid, pid_file))
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   126
        with open(pid_file, 'w') as f:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   127
            f.write(str(pid))
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   128
        atexit.register(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   129
            self._remove_pid_file, pid, pid_file)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   130
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   131
    def daemonize(self, pid_file):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   132
        pid = live_pidfile(pid_file)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   133
        if pid:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   134
            raise ExecutionError(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   135
                "Daemon is already running (PID: %s from PID file %s)"
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   136
                % (pid, pid_file))
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   137
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   138
        self.debug('Entering daemon mode')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   139
        pid = os.fork()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   140
        if pid:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   141
            # The forked process also has a handle on resources, so we
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   142
            # *don't* want proper termination of the process, we just
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   143
            # want to exit quick (which os._exit() does)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   144
            os._exit(0)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   145
        # Make this the session leader
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   146
        os.setsid()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   147
        # Fork again for good measure!
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   148
        pid = os.fork()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   149
        if pid:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   150
            os._exit(0)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   151
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   152
        # @@: Should we set the umask and cwd now?
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   153
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   154
        import resource  # Resource usage information.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   155
        maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   156
        if (maxfd == resource.RLIM_INFINITY):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   157
            maxfd = MAXFD
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   158
        # Iterate through and close all file descriptors.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   159
        for fd in range(0, maxfd):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   160
            try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   161
                os.close(fd)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   162
            except OSError:  # ERROR, fd wasn't open to begin with (ignored)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   163
                pass
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   164
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   165
        if (hasattr(os, "devnull")):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   166
            REDIRECT_TO = os.devnull
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   167
        else:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   168
            REDIRECT_TO = "/dev/null"
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   169
        os.open(REDIRECT_TO, os.O_RDWR)  # standard input (0)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   170
        # Duplicate standard input to standard output and standard error.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   171
        os.dup2(0, 1)  # standard output (1)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   172
        os.dup2(0, 2)  # standard error (2)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   173
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   174
    def restart_with_reloader(self):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   175
        self.debug('Starting subprocess with file monitor')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   176
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   177
        while True:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   178
            args = [self.quote_first_command_arg(sys.executable)] + sys.argv
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   179
            new_environ = os.environ.copy()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   180
            new_environ[self._reloader_environ_key] = 'true'
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   181
            proc = None
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   182
            try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   183
                try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   184
                    proc = subprocess.Popen(args, env=new_environ)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   185
                    exit_code = proc.wait()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   186
                    proc = None
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   187
                    print "Process exited with ", exit_code
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   188
                except KeyboardInterrupt:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   189
                    self.info('^C caught in monitor process')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   190
                    return 1
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   191
            finally:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   192
                if proc is not None:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   193
                    proc.terminate()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   194
                    self.info(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   195
                        'Waiting for the server to stop. Hit CTRL-C to exit')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   196
                    exit_code = proc.wait()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   197
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   198
            if exit_code != 3:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   199
                return exit_code
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   200
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   201
            self.info('%s %s %s' % ('-' * 20, 'Restarting', '-' * 20))
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   202
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   203
    def set_needreload(self):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   204
        self._needreload = True
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   205
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   206
    def install_reloader(self, poll_interval, extra_files):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   207
        mon = Monitor(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   208
            poll_interval=poll_interval, extra_files=extra_files,
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   209
            atexit=self.set_needreload)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   210
        mon_thread = threading.Thread(target=mon.periodic_reload)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   211
        mon_thread.daemon = True
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   212
        mon_thread.start()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   213
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   214
    def pyramid_instance(self, appid):
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   215
        self._needreload = False
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   216
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   217
        if self['reload'] and not os.environ.get(self._reloader_environ_key):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   218
            return self.restart_with_reloader()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   219
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   220
        cwconfig = cwcfg.config_for(appid, debugmode=self['debug'])
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   221
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   222
        if self['reload']:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   223
            _turn_sigterm_into_systemexit()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   224
            self.debug('Running reloading file monitor')
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   225
            extra_files = [sys.argv[0], cwconfig.main_config_file()]
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   226
            self.install_reloader(self['reload-interval'], extra_files)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   227
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   228
        if not self['reload'] and not self['debug']:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   229
            self.daemonize(cwconfig['pid-file'])
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   230
            self.record_pid(cwconfig['pid-file'])
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   231
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   232
        init_cmdline_log_threshold(cwconfig, self['loglevel'])
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   233
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   234
        host = cwconfig['interface']
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   235
        port = cwconfig['port'] or 8080
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   236
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   237
        pyramid_config = make_cubicweb_application(cwconfig)
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   238
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   239
        repo = cwconfig.repository()
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   240
        try:
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   241
            repo.start_looping_tasks()
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   242
            waitress.serve(
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   243
                pyramid_config.make_wsgi_app(), host=host, port=port)
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   244
        finally:
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   245
            repo.shutdown()
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   246
        if self._needreload:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   247
            return 3
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   248
        return 0
11633
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   249
ffe4040cf4a2 Implements the 'pyramid' command.
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   250
CWCTL.register(PyramidStartHandler)
11637
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   251
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   252
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   253
def live_pidfile(pidfile):  # pragma: no cover
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   254
    """(pidfile:str) -> int | None
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   255
    Returns an int found in the named file, if there is one,
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   256
    and if there is a running process with that process id.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   257
    Return None if no such process exists.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   258
    """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   259
    pid = read_pidfile(pidfile)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   260
    if pid:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   261
        try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   262
            os.kill(int(pid), 0)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   263
            return pid
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   264
        except OSError as e:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   265
            if e.errno == errno.EPERM:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   266
                return pid
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   267
    return None
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   268
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   269
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   270
def read_pidfile(filename):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   271
    if os.path.exists(filename):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   272
        try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   273
            with open(filename) as f:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   274
                content = f.read()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   275
            return int(content.strip())
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   276
        except (ValueError, IOError):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   277
            return None
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   278
    else:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   279
        return None
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   280
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   281
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   282
def _turn_sigterm_into_systemexit():
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   283
    """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   284
    Attempts to turn a SIGTERM exception into a SystemExit exception.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   285
    """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   286
    try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   287
        import signal
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   288
    except ImportError:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   289
        return
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   290
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   291
    def handle_term(signo, frame):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   292
        raise SystemExit
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   293
    signal.signal(signal.SIGTERM, handle_term)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   294
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   295
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   296
class Monitor(object):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   297
    """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   298
    A file monitor and server stopper.
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   299
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   300
    It is a simplified version of pyramid pserve.Monitor, with little changes:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   301
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   302
    -   The constructor takes extra_files and atexit
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   303
    -   The process is stopped by auto-kill with signal SIGTERM
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   304
    """
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   305
    def __init__(self, poll_interval, extra_files=[], atexit=None):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   306
        self.module_mtimes = {}
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   307
        self.keep_running = True
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   308
        self.poll_interval = poll_interval
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   309
        self.extra_files = extra_files
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   310
        self.atexit = atexit
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   311
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   312
    def _exit(self):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   313
        if self.atexit:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   314
            self.atexit()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   315
        os.kill(os.getpid(), signal.SIGTERM)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   316
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   317
    def periodic_reload(self):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   318
        while True:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   319
            if not self.check_reload():
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   320
                self._exit()
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   321
                break
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   322
            time.sleep(self.poll_interval)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   323
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   324
    def check_reload(self):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   325
        filenames = list(self.extra_files)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   326
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   327
        for module in list(sys.modules.values()):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   328
            try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   329
                filename = module.__file__
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   330
            except (AttributeError, ImportError):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   331
                continue
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   332
            if filename is not None:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   333
                filenames.append(filename)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   334
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   335
        for filename in filenames:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   336
            try:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   337
                stat = os.stat(filename)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   338
                if stat:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   339
                    mtime = stat.st_mtime
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   340
                else:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   341
                    mtime = 0
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   342
            except (OSError, IOError):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   343
                continue
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   344
            if filename.endswith('.pyc') and os.path.exists(filename[:-1]):
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   345
                mtime = max(os.stat(filename[:-1]).st_mtime, mtime)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   346
            if not filename in self.module_mtimes:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   347
                self.module_mtimes[filename] = mtime
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   348
            elif self.module_mtimes[filename] < mtime:
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   349
                print('%s changed; reloading...' % filename)
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   350
                return False
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   351
a9cde6a3394c Implements auto-reload and daemon mode.
Christophe de Vienne <christophe@unlish.com>
parents: 11633
diff changeset
   352
        return True