You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-commits@quetz.apache.org by nl...@apache.org on 2005/05/01 11:36:55 UTC
svn commit: r165474 - in /httpd/mod_python/trunk:
lib/python/mod_python/publisher.py test/htdocs/tests.py test/test.py
Author: nlehuen
Date: Sun May 1 02:36:54 2005
New Revision: 165474
URL: http://svn.apache.org/viewcvs?rev=165474&view=rev
Log:
New version of the publisher with support for old-style & new-style classes, iterators and generators.
Modified:
httpd/mod_python/trunk/lib/python/mod_python/publisher.py
httpd/mod_python/trunk/test/htdocs/tests.py
httpd/mod_python/trunk/test/test.py
Modified: httpd/mod_python/trunk/lib/python/mod_python/publisher.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/trunk/lib/python/mod_python/publisher.py?rev=165474&r1=165473&r2=165474&view=diff
==============================================================================
--- httpd/mod_python/trunk/lib/python/mod_python/publisher.py (original)
+++ httpd/mod_python/trunk/lib/python/mod_python/publisher.py Sun May 1 02:36:54 2005
@@ -154,55 +154,18 @@
realm, user, passwd = process_auth(req, module)
# resolve the object ('traverse')
- try:
- object = resolve_object(req, module, func_path, realm, user, passwd)
- except AttributeError:
- raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
-
- # not callable, a class or an unbound method
- if (not callable(object) or
- type(object) is ClassType or
- (hasattr(object, 'im_self') and not object.im_self)):
-
- result = str(object)
-
- else:
- # callable, (but not a class or unbound method)
-
- # process input, if any
- req.form = util.FieldStorage(req, keep_blank_values=1)
- result = util.apply_fs_data(object, req.form, req=req)
+ object = resolve_object(req, module, func_path, realm, user, passwd)
- # Now we'll send what the published object has returned
- # TODO : I'm not sure we should always return apache.OK if something was sent
- # or if there was an internal redirect.
- if result or req.bytes_sent > 0 or req.next:
-
- if result is None:
- result = ""
- elif type(result) == UnicodeType:
- return result
- else:
- result = str(result)
-
- # unless content_type was manually set, we will attempt
- # to guess it
- if not req._content_type_set:
- # make an attempt to guess content-type
- if result[:100].strip()[:6].lower() == '<html>' \
- or result.find('</') > 0:
- req.content_type = 'text/html'
- else:
- req.content_type = 'text/plain'
+ # publish the object
+ published = publish_object(req, object)
+
+ # we log a message if nothing was published, it helps with debugging
+ if (not published) and (req.bytes_sent==0) and (req.next is None):
+ log=int(req.get_config().get("PythonDebug", 0))
+ if log:
+ req.log_error("mod_python.publisher: nothing to publish.")
- if req.method != "HEAD":
- req.write(result)
- else:
- req.write("")
- return apache.OK
- else:
- req.log_error("mod_python.publisher: %s returned nothing." % `object`)
- return apache.HTTP_INTERNAL_SERVER_ERROR
+ return apache.OK
def process_auth(req, object, realm="unknown", user=None, passwd=None):
@@ -312,14 +275,16 @@
tp_rules.update({
# Those are not traversable nor publishable
ModuleType : (False, False),
+ BuiltinFunctionType : (False, False),
+
+ # This may change in the near future to (False, True)
ClassType : (False, False),
TypeType : (False, False),
- BuiltinFunctionType : (False, False),
- # XXX Generators should be publishable, see
- # http://issues.apache.org/jira/browse/MODPYTHON-15
- # Until they are, it is not interesting to publish them
- GeneratorType : (False, False),
+ # Publishing a generator may not seem to makes sense, because
+ # it can only be done once. However, we could get a brand new generator
+ # each time a new-style class property is accessed.
+ GeneratorType : (False, True),
# Old-style instances are traversable
InstanceType : (True, True),
@@ -358,7 +323,10 @@
# we know it's OK to call getattr
# note that getattr can really call some code because
# of property objects (or attribute with __get__ special methods)...
- obj = getattr(obj, obj_str)
+ try:
+ obj = getattr(obj, obj_str)
+ except AttributeError:
+ raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
# we process the authentication for the object
realm, user, passwd = process_auth(req, obj, realm, user, passwd)
@@ -373,3 +341,55 @@
raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN
return obj
+
+# This regular expression is used to test for the presence of an HTML header
+# tag, written in upper or lower case.
+re_html = re.compile(r"</HTML",re.I)
+re_charset = re.compile(r"charset\s*=\s*([^\s;]+)",re.I);
+
+def publish_object(req, object):
+ if callable(object):
+ req.form = util.FieldStorage(req, keep_blank_values=1)
+ return publish_object(req,util.apply_fs_data(object, req.form, req=req))
+ elif hasattr(object,'__iter__'):
+ result = False
+ for item in object:
+ result |= publish_object(req,item)
+ return result
+ else:
+ if object is None:
+ return False
+ elif isinstance(object,UnicodeType):
+ # We try to detect the character encoding
+ # from the Content-Type header
+ if req._content_type_set:
+ charset = re_charset.search(req.content_type)
+ if charset:
+ charset = charset.group(1)
+ else:
+ charset = 'UTF8'
+ req.content_type += '; charset=UTF8'
+ else:
+ charset = 'UTF8'
+
+ result = object.encode(charset)
+ else:
+ charset = None
+ result = str(object)
+
+ if not req._content_type_set:
+ # make an attempt to guess content-type
+ # we look for a </HTML in the last 100 characters.
+ # re.search works OK with a negative start index (it starts from 0
+ # in that case)
+ if re_html.search(result,len(result)-100):
+ req.content_type = 'text/html'
+ else:
+ req.content_type = 'text/plain'
+ if charset is not None:
+ req.content_type += '; charset=UTF8'
+
+ if req.method!='HEAD':
+ req.write(result)
+
+ return True
Modified: httpd/mod_python/trunk/test/htdocs/tests.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/trunk/test/htdocs/tests.py?rev=165474&r1=165473&r2=165474&view=diff
==============================================================================
--- httpd/mod_python/trunk/test/htdocs/tests.py (original)
+++ httpd/mod_python/trunk/test/htdocs/tests.py Sun May 1 02:36:54 2005
@@ -835,6 +835,8 @@
return "test ok, interpreter=%s" % req.interpreter
class OldStyleClassTest:
+ def __init__(self):
+ pass
def __call__(self, req):
return "test callable old-style instance ok"
def traverse(self, req):
@@ -843,6 +845,15 @@
test_dict = {1:1, 2:2, 3:3}
test_dict_keys = test_dict.keys
+
+def test_dict_iteration(req):
+ return test_dict_keys()
+
+def test_generator(req):
+ c = 0
+ while c < 10:
+ yield c
+ c += 1
class InstanceTest(object):
def __call__(self, req):
Modified: httpd/mod_python/trunk/test/test.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/trunk/test/test.py?rev=165474&r1=165473&r2=165474&view=diff
==============================================================================
--- httpd/mod_python/trunk/test/test.py (original)
+++ httpd/mod_python/trunk/test/test.py Sun May 1 02:36:54 2005
@@ -1224,6 +1224,27 @@
if status != 403:
self.fail('Vulnerability : built-in type publishing (%i)\n%s' % (status, response))
+ def test_publisher_iterator(self):
+ c = VirtualHost("*",
+ ServerName("test_publisher"),
+ DocumentRoot(DOCUMENT_ROOT),
+ Directory(DOCUMENT_ROOT,
+ SetHandler("mod_python"),
+ PythonHandler("mod_python.publisher"),
+ PythonDebug("On")))
+ return str(c)
+
+ def test_publisher_iterator(self):
+ print "\n * Testing mod_python.publisher iterators"
+
+ rsp = self.vhost_get("test_publisher", path="/tests.py/test_dict_iteration")
+ if (rsp != "123"):
+ self.fail(`rsp`)
+
+ rsp = self.vhost_get("test_publisher", path="/tests.py/test_generator")
+ if (rsp != "0123456789"):
+ self.fail(`rsp`)
+
def test_publisher_old_style_instance_conf(self):
c = VirtualHost("*",
ServerName("test_publisher"),
@@ -1363,6 +1384,7 @@
perRequestSuite.addTest(PerRequestTestCase("test_publisher_old_style_instance"))
perRequestSuite.addTest(PerRequestTestCase("test_publisher_instance"))
perRequestSuite.addTest(PerRequestTestCase("test_publisher_security"))
+ perRequestSuite.addTest(PerRequestTestCase("test_publisher_iterator"))
# this must be last so its error_log is not overwritten
perRequestSuite.addTest(PerRequestTestCase("test_internal"))