@@ -190,55 +190,18 @@ def handler(req):
190190 realm , user , passwd = process_auth (req , module )
191191
192192 # resolve the object ('traverse')
193- try :
194- object = resolve_object (req , module , func_path , realm , user , passwd )
195- except AttributeError :
196- raise apache .SERVER_RETURN , apache .HTTP_NOT_FOUND
197-
198- # not callable, a class or an unbound method
199- if (not callable (object ) or
200- type (object ) is ClassType or
201- (hasattr (object , 'im_self' ) and not object .im_self )):
202-
203- result = str (object )
204-
205- else :
206- # callable, (but not a class or unbound method)
207-
208- # process input, if any
209- req .form = util .FieldStorage (req , keep_blank_values = 1 )
210- result = util .apply_fs_data (object , req .form , req = req )
211-
212- # Now we'll send what the published object has returned
213- # TODO : I'm not sure we should always return apache.OK if something was sent
214- # or if there was an internal redirect.
215- if result or req .bytes_sent > 0 or req .next :
216-
217- if result is None :
218- result = ""
219- elif type (result ) == UnicodeType :
220- return result
221- else :
222- result = str (result )
193+ object = resolve_object (req , module , func_path , realm , user , passwd )
223194
224- # unless content_type was manually set, we will attempt
225- # to guess it
226- if not req ._content_type_set :
227- # make an attempt to guess content-type
228- if result [:100 ].strip ()[:6 ].lower () == '<html>' \
229- or result .find ('</' ) > 0 :
230- req .content_type = 'text/html'
231- else :
232- req .content_type = 'text/plain'
195+ # publish the object
196+ published = publish_object (req , object )
197+
198+ # we log a message if nothing was published, it helps with debugging
199+ if (not published ) and (req .bytes_sent == 0 ) and (req .next is None ):
200+ log = int (req .get_config ().get ("PythonDebug" , 0 ))
201+ if log :
202+ req .log_error ("mod_python.publisher: nothing to publish." )
233203
234- if req .method != "HEAD" :
235- req .write (result )
236- else :
237- req .write ("" )
238- return apache .OK
239- else :
240- req .log_error ("mod_python.publisher: %s returned nothing." % `object` )
241- return apache .HTTP_INTERNAL_SERVER_ERROR
204+ return apache .OK
242205
243206def process_auth (req , object , realm = "unknown" , user = None , passwd = None ):
244207
@@ -348,14 +311,16 @@ def process_auth(req, object, realm="unknown", user=None, passwd=None):
348311tp_rules .update ({
349312 # Those are not traversable nor publishable
350313 ModuleType : (False , False ),
314+ BuiltinFunctionType : (False , False ),
315+
316+ # This may change in the near future to (False, True)
351317 ClassType : (False , False ),
352318 TypeType : (False , False ),
353- BuiltinFunctionType : (False , False ),
354319
355- # XXX Generators should be publishable, see
356- # http://issues.apache.org/jira/browse/MODPYTHON-15
357- # Until they are, it is not interesting to publish them
358- GeneratorType : (False , False ),
320+ # Publishing a generator may not seem to makes sense, because
321+ # it can only be done once. However, we could get a brand new generator
322+ # each time a new-style class property is accessed.
323+ GeneratorType : (False , True ),
359324
360325 # Old-style instances are traversable
361326 InstanceType : (True , True ),
@@ -394,7 +359,10 @@ def resolve_object(req, obj, object_str, realm=None, user=None, passwd=None):
394359 # we know it's OK to call getattr
395360 # note that getattr can really call some code because
396361 # of property objects (or attribute with __get__ special methods)...
397- obj = getattr (obj , obj_str )
362+ try :
363+ obj = getattr (obj , obj_str )
364+ except AttributeError :
365+ raise apache .SERVER_RETURN , apache .HTTP_NOT_FOUND
398366
399367 # we process the authentication for the object
400368 realm , user , passwd = process_auth (req , obj , realm , user , passwd )
@@ -409,3 +377,55 @@ def resolve_object(req, obj, object_str, realm=None, user=None, passwd=None):
409377 raise apache .SERVER_RETURN , apache .HTTP_FORBIDDEN
410378
411379 return obj
380+
381+ # This regular expression is used to test for the presence of an HTML header
382+ # tag, written in upper or lower case.
383+ re_html = re .compile (r"</HTML\s*>\s*$" ,re .I )
384+ re_charset = re .compile (r"charset\s*=\s*([^\s;]+)" ,re .I );
385+
386+ def publish_object (req , object ):
387+ if callable (object ):
388+ req .form = util .FieldStorage (req , keep_blank_values = 1 )
389+ return publish_object (req ,util .apply_fs_data (object , req .form , req = req ))
390+ elif hasattr (object ,'__iter__' ):
391+ result = False
392+ for item in object :
393+ result |= publish_object (req ,item )
394+ return result
395+ else :
396+ if object is None :
397+ return False
398+ elif isinstance (object ,UnicodeType ):
399+ # We try to detect the character encoding
400+ # from the Content-Type header
401+ if req ._content_type_set :
402+ charset = re_charset .search (req .content_type )
403+ if charset :
404+ charset = charset .group (1 )
405+ else :
406+ charset = 'UTF8'
407+ req .content_type += '; charset=UTF8'
408+ else :
409+ charset = 'UTF8'
410+
411+ result = object .encode (charset )
412+ else :
413+ charset = None
414+ result = str (object )
415+
416+ if not req ._content_type_set :
417+ # make an attempt to guess content-type
418+ # we look for a </HTML in the last 100 characters.
419+ # re.search works OK with a negative start index (it starts from 0
420+ # in that case)
421+ if re_html .search (result ,len (result )- 100 ):
422+ req .content_type = 'text/html'
423+ else :
424+ req .content_type = 'text/plain'
425+ if charset is not None :
426+ req .content_type += '; charset=UTF8'
427+
428+ if req .method != 'HEAD' :
429+ req .write (result )
430+
431+ return True
0 commit comments