summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Piercy <web@stevepiercy.com>2015-11-14 02:44:27 -0800
committerSteve Piercy <web@stevepiercy.com>2015-11-14 02:44:27 -0800
commit0d403e8236cc9c1c5dc8d79f1131f95363189495 (patch)
treeb11bf63e1f4eecf14354b6386c77a970fe8c75a5
parent63163f7bd71fba313bdab162201cb00a8dcc7c9b (diff)
parent031b5b69f4ae3e2187e3b9f1f7e582d71fac45b1 (diff)
downloadpyramid-0d403e8236cc9c1c5dc8d79f1131f95363189495.tar.gz
pyramid-0d403e8236cc9c1c5dc8d79f1131f95363189495.tar.bz2
pyramid-0d403e8236cc9c1c5dc8d79f1131f95363189495.zip
Merge remote-tracking branch 'upstream/master'
-rw-r--r--CHANGES.txt3
-rw-r--r--pyramid/scripts/pserve.py61
2 files changed, 57 insertions, 7 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 739eb870d..9ff26420b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -155,6 +155,9 @@ Features
docstrings instead of the default ``str(obj)`` when possible.
See https://github.com/Pylons/pyramid/pull/1929
+- ``pserve --reload`` will no longer crash on syntax errors!!!
+ See https://github.com/Pylons/pyramid/pull/2044
+
Bug Fixes
---------
diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py
index 01d6383d6..efad0cc68 100644
--- a/pyramid/scripts/pserve.py
+++ b/pyramid/scripts/pserve.py
@@ -14,9 +14,11 @@ import errno
import logging
import optparse
import os
+import py_compile
import re
import subprocess
import sys
+import tempfile
import textwrap
import threading
import time
@@ -227,7 +229,10 @@ class PServeCommand(object):
cmd = None
if self.options.reload:
- if self.options.daemon or cmd in ('start', 'stop', 'restart'):
+ if (
+ getattr(self.options, 'daemon', False) or
+ cmd in ('start', 'stop', 'restart')
+ ):
self.out(
'Error: Cannot use reloading while running as a dameon.')
return 2
@@ -843,7 +848,7 @@ class Monitor(object): # pragma: no cover
if %errorlevel% == 3 goto repeat
or run a monitoring process in Python (``pserve --reload`` does
- this).
+ this).
Use the ``watch_file(filename)`` function to cause a reload/restart for
other non-Python files (e.g., configuration files). If you have
@@ -866,9 +871,19 @@ class Monitor(object): # pragma: no cover
self.poll_interval = poll_interval
self.extra_files = list(self.global_extra_files)
self.instances.append(self)
+ self.syntax_error_files = set()
+ self.pending_reload = False
self.file_callbacks = list(self.global_file_callbacks)
+ temp_pyc_fp = tempfile.NamedTemporaryFile(delete=False)
+ self.temp_pyc = temp_pyc_fp.name
+ temp_pyc_fp.close()
def _exit(self):
+ try:
+ os.unlink(self.temp_pyc)
+ except IOError:
+ # not worried if the tempfile can't be removed
+ pass
# use os._exit() here and not sys.exit() since within a
# thread sys.exit() just closes the given thread and
# won't kill the process; note os._exit does not call
@@ -899,6 +914,7 @@ class Monitor(object): # pragma: no cover
continue
if filename is not None:
filenames.append(filename)
+ new_changes = False
for filename in filenames:
try:
stat = os.stat(filename)
@@ -910,11 +926,42 @@ class Monitor(object): # pragma: no cover
continue
if filename.endswith('.pyc') and os.path.exists(filename[:-1]):
mtime = max(os.stat(filename[:-1]).st_mtime, mtime)
- if filename not in self.module_mtimes:
- self.module_mtimes[filename] = mtime
- elif self.module_mtimes[filename] < mtime:
- print("%s changed; reloading..." % filename)
- return False
+ pyc = True
+ else:
+ pyc = False
+ old_mtime = self.module_mtimes.get(filename)
+ self.module_mtimes[filename] = mtime
+ if old_mtime is not None and old_mtime < mtime:
+ new_changes = True
+ if pyc:
+ filename = filename[:-1]
+ is_valid = True
+ if filename.endswith('.py'):
+ is_valid = self.check_syntax(filename)
+ if is_valid:
+ print("%s changed ..." % filename)
+ if new_changes:
+ self.pending_reload = True
+ if self.syntax_error_files:
+ for filename in sorted(self.syntax_error_files):
+ print("%s has a SyntaxError; NOT reloading." % filename)
+ if self.pending_reload and not self.syntax_error_files:
+ self.pending_reload = False
+ return False
+ return True
+
+ def check_syntax(self, filename):
+ # check if a file has syntax errors.
+ # If so, track it until it's fixed.
+ try:
+ py_compile.compile(filename, cfile=self.temp_pyc, doraise=True)
+ except py_compile.PyCompileError as ex:
+ print(ex.msg)
+ self.syntax_error_files.add(filename)
+ return False
+ else:
+ if filename in self.syntax_error_files:
+ self.syntax_error_files.remove(filename)
return True
def watch_file(self, cls, filename):