Commits

Jason R. Coombs committed 98426c8 Merge

Merge with 3.2.x

Comments (0)

Files changed (5)

cherrypy/__init__.py

 engine.signal_handler = process.plugins.SignalHandler(engine)
 
 
+class _HandleSignalsPlugin(object):
+    """Handle signals from other processes based on the configured
+    platform handlers above."""
+
+    def __init__(self, bus):
+        self.bus = bus
+
+    def subscribe(self):
+        """Add the handlers based on the platform"""
+        if hasattr(self.bus, "signal_handler"):
+            self.bus.signal_handler.subscribe()
+        if hasattr(self.bus, "console_control_handler"):
+            self.bus.console_control_handler.subscribe()
+
+engine.signals = _HandleSignalsPlugin(engine)
+
+
 from cherrypy import _cpserver
 server = _cpserver.Server()
 server.subscribe()
 
     tree.mount(root, script_name, config)
 
-    if hasattr(engine, "signal_handler"):
-        engine.signal_handler.subscribe()
-    if hasattr(engine, "console_control_handler"):
-        engine.console_control_handler.subscribe()
-
+    engine.signals.subscribe()
     engine.start()
     engine.block()
 

cherrypy/wsgiserver/wsgiserver2.py

     finally:
         etype = value = tb = None
 
+import operator
 
 from urllib import unquote
-from urlparse import urlparse
 import warnings
 
 if sys.version_info >= (3, 0):
 
     def grow(self, amount):
         """Spawn new worker threads (not above self.max)."""
-        for i in range(amount):
-            if self.max > 0 and len(self._threads) >= self.max:
-                break
-            worker = WorkerThread(self.server)
-            worker.setName("CP Server " + worker.getName())
-            self._threads.append(worker)
-            worker.start()
+        if self.max > 0:
+            budget = max(self.max - len(self._threads), 0)
+        else:
+            # self.max <= 0 indicates no maximum
+            budget = float('inf')
+
+        n_new = min(amount, budget)
+
+        workers = [self._spawn_worker() for i in range(n_new)]
+        while not self._all(operator.attrgetter('ready'), workers):
+            time.sleep(.1)
+        self._threads.extend(workers)
+
+    def _spawn_worker(self):
+        worker = WorkerThread(self.server)
+        worker.setName("CP Server " + worker.getName())
+        worker.start()
+        return worker
+
+    def _all(func, items):
+        results = [func(item) for item in items]
+        return reduce(operator.and_, results, True)
+    _all = staticmethod(_all)
 
     def shrink(self, amount):
         """Kill off worker threads (not below self.min)."""
                 self._threads.remove(t)
                 amount -= 1
 
-        if amount > 0:
-            for i in range(min(amount, len(self._threads) - self.min)):
-                # Put a number of shutdown requests on the queue equal
-                # to 'amount'. Once each of those is processed by a worker,
-                # that worker will terminate and be culled from our list
-                # in self.put.
-                self._queue.put(_SHUTDOWNREQUEST)
+        # calculate the number of threads above the minimum
+        n_extra = max(len(self._threads) - self.min, 0)
+
+        # don't remove more than amount
+        n_to_remove = min(amount, n_extra)
+
+        # put shutdown requests on the queue equal to the number of threads
+        # to remove. As each request is processed by a worker, that worker
+        # will terminate and be culled from the list.
+        for n in range(n_to_remove):
+            self._queue.put(_SHUTDOWNREQUEST)
 
     def stop(self, timeout=5):
         # Must shut down threads here so the code that calls

cherrypy/wsgiserver/wsgiserver3.py

 import threading
 import time
 from traceback import format_exc
-from urllib.parse import unquote
-from urllib.parse import urlparse
-from urllib.parse import scheme_chars
-import warnings
 
 if sys.version_info >= (3, 0):
     bytestr = bytes
             self._check_length()
             res.append(data)
             # See https://bitbucket.org/cherrypy/cherrypy/issue/421
-            if len(data) < 256 or data[-1:] == LF:
+            if len(data) < 256 or data[-1:].decode() == LF:
                 return EMPTY.join(res)
 
     def readlines(self, sizehint=0):
 
     def grow(self, amount):
         """Spawn new worker threads (not above self.max)."""
-        for i in range(amount):
-            if self.max > 0 and len(self._threads) >= self.max:
-                break
-            worker = WorkerThread(self.server)
-            worker.setName("CP Server " + worker.getName())
-            self._threads.append(worker)
-            worker.start()
+        if self.max > 0:
+            budget = max(self.max - len(self._threads), 0)
+        else:
+            # self.max <= 0 indicates no maximum
+            budget = float('inf')
+
+        n_new = min(amount, budget)
+
+        workers = [self._spawn_worker() for i in range(n_new)]
+        while not all(worker.ready for worker in workers):
+            time.sleep(.1)
+        self._threads.extend(workers)
+
+    def _spawn_worker(self):
+        worker = WorkerThread(self.server)
+        worker.setName("CP Server " + worker.getName())
+        worker.start()
+        return worker
 
     def shrink(self, amount):
         """Kill off worker threads (not below self.min)."""
                 self._threads.remove(t)
                 amount -= 1
 
-        if amount > 0:
-            for i in range(min(amount, len(self._threads) - self.min)):
-                # Put a number of shutdown requests on the queue equal
-                # to 'amount'. Once each of those is processed by a worker,
-                # that worker will terminate and be culled from our list
-                # in self.put.
-                self._queue.put(_SHUTDOWNREQUEST)
+        # calculate the number of threads above the minimum
+        n_extra = max(len(self._threads) - self.min, 0)
+
+        # don't remove more than amount
+        n_to_remove = min(amount, n_extra)
+
+        # put shutdown requests on the queue equal to the number of threads
+        # to remove. As each request is processed by a worker, that worker
+        # will terminate and be culled from the list.
+        for n in range(n_to_remove):
+            self._queue.put(_SHUTDOWNREQUEST)
 
     def stop(self, timeout=5):
         # Must shut down threads here so the code that calls
 
 VERSION='3.2.3'
 
+if sys.version_info < (3,):
+	input = raw_input
+
 def get_next_version():
-	digits = map(int, VERSION.split('.'))
-	digits[-1] += 1
-	return '.'.join(map(str, digits))
+	print("The last release on this branch was {version}".format(
+		version=VERSION))
+	return input("What version are you releasing? ")
 
 NEXT_VERSION = get_next_version()
 
 	"""
 	print("You're about to release CherryPy {NEXT_VERSION}".format(
 		**globals()))
-	res = raw_input('Have you run the tests with `nosetests -s ./` on '
+	res = input('Have you run the tests with `nosetests -s ./` on '
 		'Windows, Linux, and Mac on at least Python 2.4, 2.5, 2.7, and 3.2? '
 		.format(**globals()))
 	if not res.lower().startswith('y'):
 	"""
 	subprocess.check_call(['hg', 'tag', NEXT_VERSION])
 
+dist_commands = [
+	[sys.executable, 'setup.py', 'sdist'],
+	[sys.executable, 'setup.py', 'sdist', '--format=gztar'],
+	[sys.executable, 'setup.py', 'bdist_wininst'],
+]
+
 def build():
 	if os.path.isfile('MANIFEST'):
 		os.remove('MANIFEST')
 	if os.path.isdir('dist'):
 		shutil.rmtree('dist')
-	subprocess.check_call([sys.executable, 'setup.py', 'sdist'])
-	subprocess.check_call([sys.executable, 'setup.py', 'sdist',
-		'--format=gztar'])
-	subprocess.check_call([sys.executable, 'setup.py', 'bdist_wininst'])
+	list(map(subprocess.check_call, dist_commands))
 
 def push():
 	"The build went well, so let's push the SCM changesets"
 	subprocess.check_call(['hg', 'push'])
 
 def publish():
-	scp_command = 'pscp' if platform.system() == 'Windows' else 'scp'
+	"""
+	Publish the dists on PyPI
+	"""
 	try:
-		subprocess.check_call([scp_command, 'dist/*',
-			'/home/fumanchu/webapps/downloads/cherrypy/{NEXT_VERSION}/'
-			.format(**globals())])
+		upload_dist_commands = [cmd + ['register', 'upload']
+			for cmd in dist_commands]
+		list(map(subprocess.check_call, upload_dist_commands))
 	except:
 		print("Unable to upload the dist files. Ask in IRC for help access "
 			"or assistance.")
 		raise SystemExit(4)
-	res = raw_input('Have you asked in IRC for others to help you test '
-		'CherryPy {NEXT_VERSION}? '
+	print('Distributions have been uploaded.')
+	print('Please ask in IRC for others to help you test '
+		'CherryPy {NEXT_VERSION}.'
 		.format(**globals()))
-	if not res.lower().startswith('y'):
-		print("Please do that")
-		raise SystemExit(2)
-	subprocess.check_call([sys.executable, 'setup.py', 'register'])
-	res = raw_input("Have you confirmed that the distro installs properly "
-		"with `easy_install CherryPy=={NEXT_VERSION}`? ".format(**globals()))
-	if not res.lower().startswith('y'):
-		print("Please do that")
-		raise SystemExit(3)
+	print("Please confirm that the distro installs properly "
+		"with `easy_install CherryPy=={NEXT_VERSION}`.".format(**globals()))
 
 def announce():
 	print("Please change the Wiki: Home page (news), CherryPyDownload")
 from distutils.command.install import INSTALL_SCHEMES
 from distutils.command.build_py import build_py
 import sys
-import os
 import re
 
 class cherrypy_build_py(build_py):
-    "Custom version of build_py that selects Python-specific modules"
+    "Custom version of build_py that excludes Python-specific modules"
     def build_module(self, module, module_file, package):
         python3 = sys.version_info >= (3,)
         if python3:
-            exclude_pattern = re.compile('wsgiserver2|ssl_pyopenssl|_cpcompat_subprocess')
+            exclude_pattern = re.compile('wsgiserver2|ssl_pyopenssl|'
+                '_cpcompat_subprocess')
         else:
             exclude_pattern = re.compile('wsgiserver3')
         if exclude_pattern.match(module):