This file is indexed.

/usr/lib/python2.7/dist-packages/hupper/reloader.py is in python-hupper 1.0-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
from __future__ import print_function

from glob import glob
import signal
import sys
import threading
import time

from .compat import is_watchdog_supported
from .compat import queue
from .ipc import ProcessGroup
from .worker import (
    Worker,
    is_active,
    get_reloader,
)


class FileMonitorProxy(object):
    """
    Wrap an :class:`hupper.interfaces.IFileMonitor` into an object that
    exposes a thread-safe interface back to the reloader to detect
    when it should reload.

    """
    def __init__(self, monitor_factory, verbose=1):
        self.monitor = monitor_factory(self.file_changed)
        self.verbose = verbose
        self.change_event = threading.Event()
        self.changed_paths = set()

    def out(self, msg):
        if self.verbose > 0:
            print(msg)

    def add_path(self, path):
        # if the glob does not match any files then go ahead and pass
        # the pattern to the monitor anyway incase it is just a file that
        # is currently missing
        for p in glob(path) or [path]:
            self.monitor.add_path(p)

    def start(self):
        self.monitor.start()

    def stop(self):
        self.monitor.stop()
        self.monitor.join()

    def file_changed(self, path):
        if path not in self.changed_paths:
            self.changed_paths.add(path)
            self.out('%s changed; reloading ...' % (path,))
        self.set_changed()

    def is_changed(self):
        return self.change_event.is_set()

    def wait_for_change(self, timeout=None):
        return self.change_event.wait(timeout)

    def clear_changes(self):
        self.change_event.clear()
        self.changed_paths.clear()

    def set_changed(self):
        self.change_event.set()


class Reloader(object):
    """
    A wrapper class around a file monitor which will handle changes by
    restarting a new worker process.

    """
    def __init__(self,
                 worker_path,
                 monitor_factory,
                 reload_interval=1,
                 verbose=1,
                 worker_args=None,
                 worker_kwargs=None,
                 ):
        self.worker_path = worker_path
        self.worker_args = worker_args
        self.worker_kwargs = worker_kwargs
        self.monitor_factory = monitor_factory
        self.reload_interval = reload_interval
        self.verbose = verbose
        self.monitor = None
        self.worker = None
        self.group = ProcessGroup()

    def out(self, msg):
        if self.verbose > 0:
            print(msg)

    def run(self):
        """
        Execute the reloader forever, blocking the current thread.

        This will invoke ``sys.exit(1)`` if interrupted.

        """
        self._capture_signals()
        self._start_monitor()
        try:
            while True:
                if not self._run_worker():
                    self._wait_for_changes()
                time.sleep(self.reload_interval)
        except KeyboardInterrupt:
            pass
        finally:
            self._stop_monitor()
            self._restore_signals()
        sys.exit(1)

    def run_once(self):
        """
        Execute the worker once.

        This method will return after a file change is detected.

        """
        self._capture_signals()
        self._start_monitor()
        try:
            self._run_worker()
        except KeyboardInterrupt:
            return
        finally:
            self._stop_monitor()
            self._restore_signals()

    def _run_worker(self):
        self.worker = Worker(
            self.worker_path,
            args=self.worker_args,
            kwargs=self.worker_kwargs,
        )
        self.worker.start()

        try:
            # register the worker with the process group
            self.group.add_child(self.worker.pid)

            self.out("Starting monitor for PID %s." % self.worker.pid)
            self.monitor.clear_changes()

            while not self.monitor.is_changed() and self.worker.is_alive():
                try:
                    cmd = self.worker.pipe.recv(timeout=self.reload_interval)
                except queue.Empty:
                    continue

                if not cmd or cmd[0] == 'reload':
                    break

                if cmd[0] == 'watch':
                    for path in cmd[1]:
                        self.monitor.add_path(path)

                else:
                    raise RuntimeError('received unknown command')

        except KeyboardInterrupt:
            if self.worker.is_alive():
                self.out('Waiting for server to exit ...')
                time.sleep(self.reload_interval)
            raise

        finally:
            if self.worker.is_alive():
                self.out('Killing server with PID %s.' % self.worker.pid)
                self.worker.terminate()
                self.worker.join()

            else:
                self.worker.join()
                self.out('Server with PID %s exited with code %d.' %
                         (self.worker.pid, self.worker.exitcode))

        self.monitor.clear_changes()

        force_restart = self.worker.terminated
        self.worker = None
        return force_restart

    def _wait_for_changes(self):
        self.out('Waiting for changes before reloading.')
        while (
            not self.monitor.wait_for_change(self.reload_interval)
        ):  # pragma: nocover
            pass

        self.monitor.clear_changes()

    def _start_monitor(self):
        self.monitor = FileMonitorProxy(self.monitor_factory, self.verbose)
        self.monitor.start()

    def _stop_monitor(self):
        if self.monitor:
            self.monitor.stop()
            self.monitor = None

    def _capture_signals(self):
        # SIGHUP is not supported on windows
        if hasattr(signal, 'SIGHUP'):
            signal.signal(signal.SIGHUP, self._signal_sighup)

    def _signal_sighup(self, signum, frame):
        self.out('Received SIGHUP, triggering a reload.')
        self.monitor.set_changed()

    def _restore_signals(self):
        if hasattr(signal, 'SIGHUP'):
            signal.signal(signal.SIGHUP, signal.SIG_DFL)


def start_reloader(
    worker_path,
    reload_interval=1,
    verbose=1,
    monitor_factory=None,
    worker_args=None,
    worker_kwargs=None,
):
    """
    Start a monitor and then fork a worker process which starts by executing
    the importable function at ``worker_path``.

    If this function is called from a worker process that is already being
    monitored then it will return a reference to the current
    :class:`hupper.interfaces.IReloaderProxy` which can be used to
    communicate with the monitor.

    ``worker_path`` must be a dotted string pointing to a globally importable
    function that will be executed to start the worker. An example could be
    ``myapp.cli.main``. In most cases it will point at the same function that
    is invoking ``start_reloader`` in the first place.

    ``reload_interval`` is a value in seconds and will be used to throttle
    restarts.

    ``verbose`` controls the output. Set to ``0`` to turn off any logging
    of activity and turn up to ``2`` for extra output.

    ``monitor_factory`` is an instance of
    :class:`hupper.interfaces.IFileMonitorFactory`. If left unspecified, this
    will try to create a :class:`hupper.watchdog.WatchdogFileMonitor` if
    `watchdog <https://pypi.org/project/watchdog/>`_ is installed and will
    fallback to the less efficient
    :class:`hupper.polling.PollingFileMonitor` otherwise.

    """
    if is_active():
        return get_reloader()

    if monitor_factory is None:
        if is_watchdog_supported():
            from .watchdog import WatchdogFileMonitor

            def monitor_factory(callback):
                return WatchdogFileMonitor(callback)

            if verbose > 1:
                print('File monitor backend: watchdog')

        else:
            from .polling import PollingFileMonitor

            def monitor_factory(callback):
                return PollingFileMonitor(callback, reload_interval)

            if verbose > 1:
                print('File monitor backend: polling')

    reloader = Reloader(
        worker_path=worker_path,
        worker_args=worker_args,
        worker_kwargs=worker_kwargs,
        reload_interval=reload_interval,
        verbose=verbose,
        monitor_factory=monitor_factory,
    )
    return reloader.run()