473,390 Members | 1,232 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,390 software developers and data experts.

mod_python exception catching, other repetitious per-page actions

I'm in the middle of refactoring a small mod_python Web application,
which uses the Publisher handler. This application is currently a
single main Python file (which loads several other files as modules)
with several function entry points for the different pages in the
application.

(The application is a tool to allow staff members here to request
firewall port openings for their servers. Thus there is a page (and
function) for "view my servers", "add a server", "add a service to a
server", and so forth. Each of these is a function in the published
file.)

One thing I would like to do is factor all of the top-level database
connecting, top-level exception handling, and error-page generation into
a single place. Whenever an exception occurs not trapped locally --
including an error connecting to the database -- I want to generate an
HTML error page instead of throwing the stacktrace at the user.

Currently this means that every function is one big try/except block
where the except block calls a TypesetErrorPage() function. Also
repeated are the function calls to set up the database backend
connection and to set up some per-user globals:

def mainpage(apache_request, some_cgi_arg):
try:
_connect_to_database()
# other repetitious stuff here
_do_user_specific_stuff(apache_request)
#
# specific code here
#
except Exception, err:
return _TypesetErrorPage(err)

def addserverpage(apache_request, some_cgi_arg, another_arg):
try:
_connect_to_database()
# other repetitious stuff here
_do_user_specific_stuff(apache_request)
#
# specific code here
#
except Exception, err:
return _TypesetErrorPage(err)

I'd like to factor this repetition out. However there does not seem to
be a straightforward way to do it -- the connect_to_database() could be
done at the module level (i.e. outside of any function), but this might
have problems if the Apache instance persists long enough between
requests for the database to time out.

One thing I have considered is to handle _all_ the pages through a
single function, which would look up the specific pages by name:

PAGES = { "mainpage" : mainpagefunc, "addserverpage" : addserverfunc }

def page(apache_request, which_page, *args):
try:
connect_to_database()
# other repetitious stuff here
do_user_specific_stuff(apache_request)
return PAGES[which_page](req, *args) # call the real page
except Exception, err:
return TypesetErrorPage(err)

.... but this has the problem that it sacrifices the argument name
matching which is a major useful feature of mod_python Publisher. I'm
not sure if keyword args are exactly the Right Thing either. It also
would make the URLs even longer than they already are, though I can
kludge that with Apache URL rewriting rules. (Bleah.)

I feel like I want a Lisp macro, but that's a topic I shouldn't even
invoke. Forget I said that. :)

Any thoughts?

--
Karl A. Krueger <kk******@example.edu>
Woods Hole Oceanographic Institution
Email address is spamtrapped. s/example/whoi/
"Outlook not so good." -- Magic 8-Ball Software Reviews
Jul 18 '05 #1
6 2083
You could use a wrapper for each function, which at least keeps you
from needing the which_page argument..

def page_wrapper(callable):
def _wrapper(apache_request, *args):
try:
_connect_to_database()
_do_user_specific_stuff(apache_request)
return callable(*args)
except Exception, err:
return _TypesetErrorPage(err)
return _wrapper

def mainpage(apache_request, some_cgi_arg):
# specific code ehre
mainpage = page_wrapper(mainpage)
page_wrapper() could do extra magic with exec if you want it to have the
same signature as callable.. something like
def page_wrapper(callable):
d = {}
name = callable.__name__
argspec = getargspecstr(callable) # based on inspect.getargspec()
callspec = ... # argspec with default values removed
exec """def %s(%s): ... return callable(%s) ...""" \
% (name, argspec, callspec) in d
return d[name]
.... barf, eh?

Jeff
PS one of the currently discussed PEPs may let you write
def mainpage(apache_request, some_cgi_arg)[page_wrapper]: ...
instead of writing mainpage = page_wrapper(mainpage) after the function
body.

Jul 18 '05 #2

"Karl A. Krueger" wrote:
I'm in the middle of refactoring a small mod_python Web application,
which uses the Publisher handler.
I have never used Publisher handler myself. Yes, I've read about it,
but I've decided to not use it after seeing that I will have to
explicitly tell which modules/functions I DON'T want external user to
be able to run - completely broken idea from security POV.
One thing I would like to do is factor all of the top-level database
connecting, top-level exception handling, and error-page generation into
a single place. Whenever an exception occurs not trapped locally --
including an error connecting to the database -- I want to generate an
HTML error page instead of throwing the stacktrace at the user. [...] One thing I have considered is to handle _all_ the pages through a
single function, which would look up the specific pages by name:
If you do this then you may as well drop Publisher handler completely.
One handle() function will work for you.
... but this has the problem that it sacrifices the argument name
matching which is a major useful feature of mod_python Publisher.
Not so big, as you need to validate arguments format anyway. With calls
like this:
pos = self.GetFsInt( 'posId' )
lastDate = self.GetFsDate( 'lastDate' )
you can get arguments from FieldStorage, ensure that arguments are
present and convert them to usable format (e.g. string=>datetime.date).
Some examples:

def RaiseMangled( self ):
raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST

def RaiseNotFound( self ):
raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND

def GetFsString( self, key, default=None ):
if(default != None):
if self.fs.has_key(key):
value = self.fs[key]
else:
value = default
else:
value = self.fs[key]
if not isinstance( value, str ):
self.RaiseMangled()
return value

def GetFsInt( self, key ):
try:
return int( self.GetFsString(key,'') )
except ValueError:
self.RaiseMangled()

def GetFsDate( self, key ):
try:
return Url2Date( self.GetFsString(key,'') )
except ValueError:
self.RaiseMangled()

I've used one method in class to handle db connections, dispatch
requests and handle errors:

def handler( req ):
h = Handler()
return h.Handle( req )

class Handler:
[...]
def Handle(req):
try:
db = DbAccess.ConnectDb( dbName, host, user )
self.fs = util.FieldStorage( self.req, True )
[...]
self.req.add_common_vars()
env = req.subprocess_env
self.script = env['SCRIPT_NAME']
self.cmd = self.script[ rfind(self.script,'/')+1: ]

if self.cmd == '':
self.HandleMenu( db )
elif self.cmd == 'PosDailyFeed':
self.HandlePosDailyFeed( db )
elif self.cmd == 'PosDailySubmit':
self.HandlePosDailySubmit( db )
[...]
else:
self.RaiseNotFound()
[...]
except:
[...]

# handler for urls like this:
# /PosDailyFeed?posId=3&date=2004-03-22
def HandlePosDailyFeed( self, db ):
pos = self.GetFsInt( 'posId' )
date = self.GetFsDate( 'date' )
pb = self.CreatePageBuilder( db )
valueDict = db.GetPosWorkerData( date, pos )
BuildDailyFeedPage( pb, db, date, pos, valueDict,
String2Html(db.GetPosName(pos)) )
self.SendPage( pb )

I'm not sure if keyword args are exactly the Right Thing either. It also
would make the URLs even longer than they already are, though I can
kludge that with Apache URL rewriting rules. (Bleah.)


If you use straight handler() function instead of Publisher handler then
you have complete freedom how your URLs will look like.

Best regards,
Jacek.
Jul 18 '05 #3
One more idea -- I don't know mod_python, but is the Publisher class
something you could subclass, to add your own behavior there instead of
in each page function?

Jeff

Jul 18 '05 #4
Jeff Epler <je****@unpythonic.net> wrote:
page_wrapper() could do extra magic with exec if you want it to have the
same signature as callable.. something like
def page_wrapper(callable):
d = {}
name = callable.__name__
argspec = getargspecstr(callable) # based on inspect.getargspec()
callspec = ... # argspec with default values removed
exec """def %s(%s): ... return callable(%s) ...""" \
% (name, argspec, callspec) in d
return d[name]
... barf, eh?


Barf? I -did- say I kind of wanted a Lisp macro; exec is about as close
as Python can get, I suppose.

Anyway, this has given me useful thoughts. Of course, I may just end up
back with keyword arguments. :-/

(But probably -not- reimplementing my whole application in Lisp. I'm
not that good in Lisp yet.)

--
Karl A. Krueger <kk******@example.edu>
Woods Hole Oceanographic Institution
Email address is spamtrapped. s/example/whoi/
"Outlook not so good." -- Magic 8-Ball Software Reviews
Jul 18 '05 #5
Jacek Trzmiel <sc***@hot.pl> wrote:
"Karl A. Krueger" wrote:
I'm in the middle of refactoring a small mod_python Web application,
which uses the Publisher handler.


I have never used Publisher handler myself. Yes, I've read about it,
but I've decided to not use it after seeing that I will have to
explicitly tell which modules/functions I DON'T want external user to
be able to run - completely broken idea from security POV.


Actually, Publisher never exposes modules imported into yours. It
traverses your module for names and types before exposing anything -- it
won't expose any object whose name begins with an underscore, or any
module.

(Oddly enough, it does expose exceptions, but just their string
representation.)

If anyone reading this is concerned about Publisher security, these
audit functions might help:

def AuditModuleForPublisher(module):
import types
for name in module.__dict__:
object = module.__dict__[name]
if name.startswith("_"):
# name starts with an underscore, not exposed
pass
elif type(object) == types.ModuleType:
# object is a module, not exposed
pass
elif type(object) == types.FunctionType:
print ":: Exposed function:", name
elif isinstance(object, str) or isinstance(object, unicode):
if "passw" in name or "PASSW" in name:
print "!! Your %s password is %s." % (name, object)
else:
print ":: Exposed string:", name
else:
print ":: Exposed variable:", name

A nicer one:

def SymbolsExposedToPublisher(module):
return [ sym for sym in module.__dict__
if (not sym.startswith("_"))
and (type(module.__dict__[sym]) != type(module)) ]

By enumerating the list of functions and variables you *intend* to
expose, a unit test should not be too hard to derive from this.

One thing I have considered is to handle _all_ the pages through a
single function, which would look up the specific pages by name:


If you do this then you may as well drop Publisher handler completely.
One handle() function will work for you.


I agree. However, since I want the functionality of Publisher's
argument name mapping, that is not what I want to do.

--
Karl A. Krueger <kk******@example.edu>
Woods Hole Oceanographic Institution
Email address is spamtrapped. s/example/whoi/
"Outlook not so good." -- Magic 8-Ball Software Reviews
Jul 18 '05 #6
In article <ma************************************@python.org >, Jeff Epler wrote:
One more idea -- I don't know mod_python, but is the Publisher class
something you could subclass, to add your own behavior there instead of
in each page function?


I suppose I should be embarrassed to say this, but I just made a copy and
modified it to suit my own needs. I don't think its design is particularly
extensible, but it's a small script that's easy to understand.

--
..:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
: please talk to your son or daughter about parametric polymorphism. :
Jul 18 '05 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: wolf | last post by:
i would like to briefly share my experiences with installing mod_python on a w2000 box. i must say that i believe the installation process to be unnecessarily complicated by the simple fact that...
6
by: Piet | last post by:
Hi there, I cannot install mod_python v3.1.3 on either Win2k/ActivePython 2.3.2 or WinMe/Python 2.3.4. When I run the Windows installer, I get the following error message: Traceback (most recent...
3
by: JWA | last post by:
Hi All, How can you capture unhandled exceptions thrown by an MDI child form separate from the entire app? I have an application-wide exception handler to catch crashes by doing the following...
11
by: Master of C++ | last post by:
Hi, I am writing a simulation package in C++, and so far I've written about 8000 lines of code and have about 30 classes. I haven't used C++ exceptions so far (for various reasons). The only two...
1
by: treelife | last post by:
I'm getting and internal server error when | run the following mod_python script. I am actually trying to run Django. Script: from mod_python import apache def handler(req):...
3
by: Charles | last post by:
Hello, I'm think about using mod_python for a project but I need to make sure: Does mod_python time out after a minute ? (I hope not). If I leave an HTTP connection open so that the content keeps...
5
by: Fernando | last post by:
I would like to know if this is OK or if it is better to just let the exception go instead of re-throwing it: I have: try { } catch(OracleException ex) {
5
by: m.banaouas | last post by:
Hi, bonjour, witch versions are suitable to use for apache & mod_python ? Can i install and use "Apache 2.2.3" & "mod_python 3.2.10" (most recent versions) without facing any known major...
1
TMS
by: TMS | last post by:
I installed mod_python 3.3.1 for Apache 2.0.58 and Windows XP. Per instructions I added this line to the httpd.conf file: LoadModule mod_python modules/mod_python.so And I get this error: ...
4
by: chris.monsanto | last post by:
Recently I've had to move my site to a new dedicated server running FreeBSD 6.1. After installing apache 2.0.59, python 2.4.4 and mod_python 3.3.1, I decided to bench a script in PHP vs one in...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.