gunicorn/gunicorn/reloader.py
Berker Peksag da5e847b63 Fix RuntimeError in gunicorn.reloader.
Here's the reproducer in Python 3.3:

    $ gunicorn --paste paste.ini --reload

Then I got the following exception:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/local/lib/python3.3/threading.py", line 901, in _bootstrap_inner
    self.run()
  File "/home/berker/hacking/mediagoblin/venv3/lib/python3.3/site-packages/gunicorn/reloader.py", line 41, in run
    for filename in self.get_files():
  File "/home/berker/hacking/mediagoblin/venv3/lib/python3.3/site-packages/gunicorn/reloader.py", line 29, in get_files
    for module in sys.modules.values()
  File "/home/berker/hacking/mediagoblin/venv3/lib/python3.3/site-packages/gunicorn/reloader.py", line 28, in <listcomp>
    re.sub('py[co]$', 'py', module.__file__)
RuntimeError: dictionary changed size during iteration
2014-06-28 07:03:31 +03:00

54 lines
1.5 KiB
Python

# -*- coding: utf-8 -
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
import os
import re
import sys
import time
import threading
class Reloader(threading.Thread):
def __init__(self, extra_files=None, interval=1, callback=None):
super(Reloader, self).__init__()
self.setDaemon(True)
self._extra_files = set(extra_files or ())
self._extra_files_lock = threading.RLock()
self._interval = interval
self._callback = callback
def add_extra_file(self, filename):
with self._extra_files_lock:
self._extra_files.add(filename)
def get_files(self):
fnames = [
re.sub('py[co]$', 'py', module.__file__)
for module in list(sys.modules.values())
if hasattr(module, '__file__')
]
with self._extra_files_lock:
fnames.extend(self._extra_files)
return fnames
def run(self):
mtimes = {}
while True:
for filename in self.get_files():
try:
mtime = os.stat(filename).st_mtime
except OSError:
continue
old_time = mtimes.get(filename)
if old_time is None:
mtimes[filename] = mtime
continue
elif mtime > old_time:
if self._callback:
self._callback(filename)
time.sleep(self._interval)