[Zope-Checkins] CVS: Zope3/lib/python/Zope/Publisher/HTTP - HTTPCharsets.py:1.2 HTTPRequest.py:1.4 HTTPResponse.py:1.4 IHTTPResponse.py:1.3

Stephan Richter srichter@cbu.edu
Fri, 14 Jun 2002 12:50:52 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/Publisher/HTTP
In directory cvs.zope.org:/tmp/cvs-serv18258/lib/python/Zope/Publisher/HTTP

Modified Files:
	HTTPRequest.py HTTPResponse.py IHTTPResponse.py 
Added Files:
	HTTPCharsets.py 
Log Message:
Finished Zope 3 Unicode support. Zope 3 should now be able to handle all 
unicode formats. Note that ebcoding and decoding unicode strings is based
on the parameters we get from the browser in HTTP_ACCEPT_CHARSET.

All "text strings" (strings that represent human language text) internally 
in Zope 3 should be unicode strings from now on! I have not checked all of 
Zope 3, so if you see a non-unicode text string somewhere, please convert
it simply by putting 'u' before the string literal.

Note that binary data, such as images are not to be encoded. 

The encoding happens on the HTTPRequest/HTTPResponse abstraction level. 
That means that currenty FTP does not profit from this new code; however
FTP is always data anyhow.


=== Zope3/lib/python/Zope/Publisher/HTTP/HTTPCharsets.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+# 
+##############################################################################
+"""Retrieval of browser character set information.
+
+$Id$
+"""
+
+from Zope.I18n.IUserPreferredCharsets import IUserPreferredCharsets
+
+
+def sort_charsets(x, y):
+    if y[1] == 'UTF-8':
+        return 1
+    if x[1] == 'UTF-8':
+        return -1    
+    return cmp(y, x)
+
+
+class HTTPCharsets:
+
+    __implements__ =  IUserPreferredCharsets
+
+    def __init__(self, request):
+        self.request = request
+
+    ############################################################
+    # Implementation methods for interface
+    # Zope.I18n.IUserPreferredCharsets.
+
+    def getPreferredCharsets(self):
+        '''See interface IUserPreferredCharsets'''
+        charsets = []
+        for charset in self.request.get('HTTP_ACCEPT_CHARSET', '').split(','):
+            charset = charset.strip()
+            if charset:
+                if ';' in charset:
+                    charset, quality = charset.split(';')
+                    quality = float(quality[2:])
+                else:
+                    quality = 1.0
+                charsets.append((quality, charset))
+        # UTF-8 is **always** preferred over anything else
+        charsets.sort(sort_charsets)
+        return map(lambda c: c[1], charsets)
+
+    #
+    ############################################################


=== Zope3/lib/python/Zope/Publisher/HTTP/HTTPRequest.py 1.3 => 1.4 ===
         self.__setupURLBase()
 
+        self.getResponse().setCharsetUsingRequest(self)
+
+
     def __setupURLBase(self):
 
         get_env = self._environ.get


=== Zope3/lib/python/Zope/Publisher/HTTP/HTTPResponse.py 1.3 => 1.4 ===
 
 import sys, re
-from types import StringType, ClassType
+from types import StringTypes, UnicodeType, ClassType
 from cgi import escape
 
+from Zope.ComponentArchitecture import getAdapter
+
 from Zope.Publisher.BaseResponse import BaseResponse
 from Zope.Publisher.Exceptions import Redirect
 from IHTTPResponse import IHTTPResponse
 from IHTTPApplicationResponse import IHTTPApplicationResponse
 from Zope.Exceptions.ExceptionFormatter import format_exception
+from Zope.I18n.IUserPreferredCharsets import IUserPreferredCharsets
 
 # Possible HTTP status responses
 status_reasons = {
@@ -110,6 +113,7 @@
         '_status',              # The response status (usually an integer)
         '_reason',              # The reason that goes with the status
         '_status_set',          # Boolean: status explicitly set
+        '_charset',             # String: character set for the output
         )
 
 
@@ -125,7 +129,8 @@
         self._status = 599
         self._reason = 'No status set'
         self._status_set = 0
-
+        self._charset = None
+        
 
     def setHeaderOutput(self, header_output):
         self._header_output = header_output
@@ -140,7 +145,7 @@
         if status is None:
             status = 200
         else:
-            if isinstance(status, StringType):
+            if type(status) in StringTypes:
                 status = status.lower()
             if status in status_codes:
                 status = status_codes[status]
@@ -263,6 +268,21 @@
 
         cookie['value']=value
 
+
+    def setCharset(self, charset=None):
+        'See Zope.Publisher.HTTP.IHTTPResponse.IHTTPResponse'
+        self._charset = charset
+
+
+    def setCharsetUsingRequest(self, request):
+        'See Zope.Publisher.HTTP.IHTTPResponse.IHTTPResponse'
+        envadaptor = getAdapter(request, IUserPreferredCharsets)
+        try:
+            charset = envadaptor.getPreferredCharsets()[0]
+        except:
+            charset = 'UTF-8'
+        self.setCharset(charset)
+
     #
     ############################################################
 
@@ -274,7 +294,7 @@
     # from: Zope.Publisher.IPublisherResponse.IPublisherResponse
 
     def setBody(self, body):
-        self._body = str(body)
+        self._body = unicode(body)
         if not self._status_set:
             self.setStatus(200)
 
@@ -289,7 +309,7 @@
                 self.redirect(v.getLocation())
                 return
         else:
-            title = tname = str(t)
+            title = tname = unicode(t)
 
         # Throwing non-protocol-specific exceptions is a good way
         # for apps to control the status code.
@@ -302,16 +322,16 @@
 
     def internalError(self):
         'See Zope.Publisher.IPublisherResponse.IPublisherResponse'
-        self.setStatus(500, "The engines can't take any more, Jim!")
+        self.setStatus(500, u"The engines can't take any more, Jim!")
 
 
     def _html(self, title, content):
         t = escape(title)
         return (
-            "<html><head><title>%s</title></head>\n"
-            "<body><h2>%s</h2>\n"
-            "%s\n"
-            "</body></html>\n" %
+            u"<html><head><title>%s</title></head>\n"
+            u"<body><h2>%s</h2>\n"
+            u"%s\n"
+            u"</body></html>\n" %
             (t, t, content)
             )
 
@@ -424,8 +444,15 @@
         if not self._wrote_headers:
             self.outputHeaders()
             self._wrote_headers = 1
+
+        if self.getHeader('content-type', '').startswith('text') and \
+               self._charset is not None and \
+               type(data) is UnicodeType:
+            data = data.encode(self._charset)
+
         self._outstream.write(data)
 
+
     def outputBody(self):
         """
         Outputs the response body.
@@ -459,7 +486,7 @@
 
             try:
                 result.append('    (Info: %s)' %
-                               str(locals['__traceback_info__']))
+                               unicode(locals['__traceback_info__']))
             except: pass
             tb = tb.tb_next
             n = n+1


=== Zope3/lib/python/Zope/Publisher/HTTP/IHTTPResponse.py 1.2 => 1.3 ===
 
         """
+
+    def setCharset(charset=None):
+        """Set the character set into which the response body should be
+           encoded. If None is passed in then no encoding will be done to
+           the output body.
+
+           The default character set is None.
+        """
+
+    def setCharsetUsingRequest(request):
+        """This convinience function determines the character set based on the
+           HTTP header information.
+        """