Jason R. Coombs avatar Jason R. Coombs committed 55be1e7 Merge

Merge with cherrypy-3.2.x

Comments (0)

Files changed (7)

cherrypy/_cpcompat.py

 
 if sys.version_info >= (3,3):
     Timer = threading.Timer
+    Event = threading.Event
 else:
     # Python 3.2 and earlier
     Timer = threading._Timer
+    Event = threading._Event

cherrypy/lib/caching.py

 
 import cherrypy
 from cherrypy.lib import cptools, httputil
-from cherrypy._cpcompat import copyitems, ntob, set_daemon, sorted
+from cherrypy._cpcompat import copyitems, ntob, set_daemon, sorted, Event
 
 
 class Cache(object):
         If timeout is None, no waiting is performed nor sentinels used.
         """
         value = self.get(key)
-        if isinstance(value, threading._Event):
+        if isinstance(value, Event):
             if timeout is None:
                 # Ignore the other thread and recalc it ourselves.
                 if debug:
         """Set the cached value for the given key."""
         existing = self.get(key)
         dict.__setitem__(self, key, value)
-        if isinstance(existing, threading._Event):
+        if isinstance(existing, Event):
             # Set Event.result so other threads waiting on it have
             # immediate access without needing to poll the cache again.
             existing.result = value

cherrypy/lib/httputil.py

 
     def output(self):
         """Transform self into a list of (name, value) tuples."""
-        header_list = []
-        for k, v in self.items():
+        return list(self.encode_header_items(self.items()))
+
+    def encode_header_items(cls, header_items):
+        """
+        Prepare the sequence of name, value tuples into a form suitable for
+        transmitting on the wire for HTTP.
+        """
+        for k, v in header_items:
             if isinstance(k, unicodestr):
-                k = self.encode(k)
+                k = cls.encode(k)
 
             if not isinstance(v, basestring):
                 v = str(v)
 
             if isinstance(v, unicodestr):
-                v = self.encode(v)
+                v = cls.encode(v)
 
             # See header_translate_* constants above.
             # Replace only if you really know what you're doing.
             k = k.translate(header_translate_table, header_translate_deletechars)
             v = v.translate(header_translate_table, header_translate_deletechars)
 
-            header_list.append((k, v))
-        return header_list
+            yield (k, v)
+    encode_header_items = classmethod(encode_header_items)
 
-    def encode(self, v):
+    def encode(cls, v):
         """Return the given header name or value, encoded for HTTP output."""
-        for enc in self.encodings:
+        for enc in cls.encodings:
             try:
                 return v.encode(enc)
             except UnicodeEncodeError:
                 continue
 
-        if self.protocol == (1, 1) and self.use_rfc_2047:
+        if cls.protocol == (1, 1) and cls.use_rfc_2047:
             # Encode RFC-2047 TEXT
             # (e.g. u"\u8200" -> "=?utf-8?b?6IiA?=").
             # We do our own here instead of using the email module
 
         raise ValueError("Could not encode header part %r using "
                          "any of the encodings %r." %
-                         (v, self.encodings))
-
+                         (v, cls.encodings))
+    encode = classmethod(encode)
 
 class Host(object):
     """An internet address.

cherrypy/test/test_compat.py

         """
         if compat.py3k:
             raise nose.SkipTest("Only useful on Python 2")
-        self.assertRaises(Exception, compat.ntob, u'fight')
+        self.assertRaises(Exception, compat.ntob, unicode('fight'))
+"""
+This script will walk a developer through the process of cutting a release.
+
+Based on 1
+
+To cut a release, simply invoke this script at the changeset to be released.
+"""
+
+from __future__ import print_function
+
+import subprocess
+import sys
+import os
+import platform
+import shutil
+
+VERSION='3.2.2'
+
+def get_next_version():
+	digits = map(int, VERSION.split('.'))
+	digits[-1] += 1
+	return '.'.join(map(str, digits))
+
+NEXT_VERSION = get_next_version()
+
+files_with_versions = ('release.py', 'setup.py', 'cherrypy/__init__.py',
+	'cherrypy/wsgiserver/wsgiserver2.py',
+	'cherrypy/wsgiserver/wsgiserver3.py',
+)
+
+def check_status():
+	"""
+	Make sure there aren't outstanding changesets that are unpushed, maybe
+	run the tests or ask the user if the tests are passing.
+	"""
+	print("You're about to release CherryPy {NEXT_VERSION}".format(
+		**globals()))
+	res = raw_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'):
+		print("Please do that")
+		raise SystemExit(1)
+
+def bump_versions():
+	"""
+	Bump the versions in each of the places where it appears and commit.
+	"""
+	list(map(bump_version, files_with_versions))
+
+	subprocess.check_call(['hg', 'ci', '-m',
+		'Bumped to {NEXT_VERSION} in preparation for next '
+		'release.'.format(**globals())])
+
+
+def bump_version(filename):
+	with open(filename, 'rb') as f:
+		lines = [line.replace(VERSION, NEXT_VERSION) for line in f]
+	with open(filename, 'wb') as f:
+		f.writelines(lines)
+
+def tag_release():
+	"""
+	Tag the release.
+	"""
+	subprocess.check_call(['hg', 'tag', NEXT_VERSION])
+
+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'])
+
+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'
+	try:
+		subprocess.check_call([scp_command, 'dist/*',
+			'/home/fumanchu/webapps/downloads/cherrypy/{NEXT_VERSION}/'
+			.format(**globals())])
+	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}? '
+		.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)
+
+def announce():
+	print("Please change the Wiki: Home page (news), CherryPyDownload")
+	print("Please announce the release on newsgroups, mailing lists, "
+		"and IRC /topic.")
+
+def main():
+	assert sys.version_info >= (2, 6), ("Release script requires Python 2.6 "
+		"or later.")
+	assert platform.system() == 'Windows', ('You must release on Windows '
+		'(to create Windows installers)')
+	check_status()
+	bump_versions()
+	tag_release()
+	build()
+	push()
+	publish()
+	announce()
+
+if __name__ == '__main__':
+	main()

sphinx/source/concepts/basics.rst

 when written using CherryPy::
 
     import cherrypy
-    
+
     class HelloWorld:
         def index(self):
             return "Hello world!"
         index.exposed = True
-    
+
     cherrypy.quickstart(HelloWorld())
 
 We assume that you already have :doc:`installed </intro/install>` CherryPy.
    mapping that is automatically generated and encoded by CherryPy; it can
    be used to store session-data in a persistent cookie. For it to work you
    have to enable the session functionality by setting 'tools.session.on' to
-   True in your config. 
+   True in your config.
  * :class:`cherrypy.response <cherrypy._cprequest.Response>` contains the
-   data that is used to build the HTTP response. 
+   data that is used to build the HTTP response.
  * :attr:`cherrypy.response.headers <cherrypy.lib.httputil.HeaderMap>`
    contains a mapping with the header options that will be returned by the
    server, before the contents get sent.
  * :attr:`cherrypy.response.body <cherrypy._cprequest.Response.body>` contains
    the actual contents of the webpage that will be sent as a response.
 
+CherryPy Response
+-----------------
+
+The `cherrypy.response` object is available to affect aspects of the response
+to a request. Like the request, the response object is a thread-local,
+meaning although it appears to be a global variable, its value is specific
+to the current thread, and thus the current request.
+
+One may store arbitrary data in the response object.
+
+HTTP Headers
+------------
+
+CherryPy exposes the request headers (as sent from the client), and response
+headers (to be returned in the response) in the `headers` attribute of
+`cherrypy.request` and `cherrypy.response`.
+
+For example, to find out what "host" to which the client intended to connect::
+
+    @cherrypy.expose
+    def index(self):
+        host = cherrypy.request.headers('Host')
+        return "You have successfully reached " + host
+
+Or to set headers on the response::
+
+    @cherrypy.expose
+    def index(self):
+        cherrypy.response.headers['Content-Type'] = 'application/jpeg'
+        return my_jpeg_data()

sphinx/source/progguide/customheaders.rst

     import cherrypy
 
     def multi_headers():
-        cherrypy.response.header_list.extend(cherrypy.response.multiheaders)
+        cherrypy.response.header_list.extend(
+            cherrypy.response.headers.encode_header_items(
+                cherrypy.response.multiheaders))
 
     cherrypy.tools.multiheaders = cherrypy.Tool('on_end_resource', multi_headers)
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.