Redundant code in multiple methods

R

Rob Conner

No you don't need to know Zope to help me. The whole reason I'd even
want to do this is because of Zope though. I made a Zope product, and
now want to perfect it.

some simple example code...

<code>
class User:

def View(self):
# play with data here
myHtmlDoc = "pretend this is a uper profile"
return myHtmlDoc
index_html = View

def Edit(self):
# play with data here
myHtmlDoc = "editing the user"
return myHtmlDoc
</code>

So when visiting "website.com/User" zope will call User.index_html() or
when you visit "website.com/User/View" zope will call User.View() In
all of the testing/learning I've done on Zope I'm pretty sure that last
item (index_html or View) must be a method, but possible it only needs
to have an __doc__ attribute (otherwise Zope will Error)

The problem comes when I want to have code put into every method.
Perhaps a counter I want to increment on every visit to a User page.
I can do this..

<snippet>
def View(self):
incrementCounter()
# play with data here
myHtmlDoc = "pretend this is a uper profile"
return myHtmlDoc
index_html = View

def Edit(self):
incrementCounter()
# play with data here
myHtmlDoc = "editing the user"
return myHtmlDoc
</snippet>

.... but in reality in my real code that one counter increment line ends
up being 20 lines long. An al lot of that "counter code" is actaully
setting up variables I'd like to access within the scope of the given
method. So if you follow me so far, I was wondering how I might change
things to only have one place where I have to maintain the "setup my
method" code, which is pretty much a lot of the same code typed over
and over into all of the methods.
(for a more real life example of things) -

<snippet>
def View(self):
REQUEST = self.REQUEST
SESSION = REQUEST.SESSION
dbConnection = self.getDBConnection()
logger = self.getLogger()
trackStatsHere()
# set up some local variables here
# change some global variables here
try:
myHtmlDoc = """make the htmldocument here using all
of the previous variables"""
# play with data here
return myHtmlDoc
except:
raise "handle the error here"
finally:
dbConnection.close()
index_html = View

def Edit(self):
REQUEST = self.REQUEST
SESSION = REQUEST.SESSION
dbConnection = self.getDBConnection()
logger = self.getLogger()
trackStatsHere()
# set up some local variables here
# change some global variables here
try:
myHtmlDoc = """make the htmldocument here using all
of the previous variables"""
# play with data here
return myHtmlDoc
except:
raise "handle the error here"
finally:
dbConnection.close()
</snippet>

I would ideally like to do something such as this this, or something
where I don't have all of that redundant code.

<snippet>
def __allmethods__(self, methodname):
"gets called when all methods are called"
REQUEST = self.REQUEST
SESSION = REQUEST.SESSION
dbConnection = self.getDBConnection()
logger = self.getLogger()
trackStatsHere()
# set up some local variables here
# change some global variables here
try:
methodname(localvariables)
except:
raise "handle the error here"
finally:
dbConnection.close()

def View(self, localvariables):
myHtmlDoc = """make the htmldocument here using all
of the previous variables"""
# play with data here
return myHtmlDoc
index_html = View

def Edit(self):
myHtmlDoc = """make the htmldocument here using all
of the previous variables"""
# play with data here
return myHtmlDoc
</snippet>

__getattr__ almost does the trick but not quite. So any suggestions on
how to streamline my code here and make it slightly more maintainable.
 
S

Sam Pointon

How about using a class, with __call__, as a wrapper instead of the
function itself?


class FunctionWrapper(object):
def __init__(self, cls, function):
self._function = function
self._cls = cls

def __call__(self, *args, **kwargs):
REQUEST = self.REQUEST
SESSION = REQUEST.SESSION
dbConnection = self._cls.getDBConnection()
logger = self._cls.getLogger()
trackStatsHere()
# set up some local variables here
# change some global variables here
try:
return self._function(self._cls, *args, **kwargs)
except:
raise "handle the error here"
finally:
dbConnection.close()

class User(object):
def __init__(self):
def View(cls, self, localvariables): #Needs the cls argument
before self to take the FunctionWrapper first argument
myHtmlDoc = """make the htmldocument here using all
of the previous variables"""
# play with data here
return myHtmlDoc
self.View = FunctionWrapper(self, View)

def Edit(cls, self): #Ditto
myHtmlDoc = """make the htmldocument here using all
of the previous variables"""
# play with data here
return myHtmlDoc
self.Edit = FunctionWrapper(self, Edit)

#the rest of the class
 
B

bruno modulix

Rob said:
No you don't need to know Zope to help me. The whole reason I'd even
want to do this is because of Zope though. I made a Zope product, and
now want to perfect it.

some simple example code...

<code>
class User:

def View(self):
# play with data here
myHtmlDoc = "pretend this is a uper profile"
return myHtmlDoc
index_html = View

def Edit(self):
# play with data here
myHtmlDoc = "editing the user"
return myHtmlDoc
</code>

So when visiting "website.com/User" zope will call User.index_html() or
when you visit "website.com/User/View" zope will call User.View() In
all of the testing/learning I've done on Zope I'm pretty sure that last
item (index_html or View) must be a method,

Anything that returns HTML is ok. Can be a method, a data attribute, a
class attribute pointing to a ZPT or DTML file (like you do for ZMI
forms), and even any aquired component ! (the view and index_html
attributes doesnt need to live in the class...)
The problem comes when I want to have code put into every method.
Perhaps a counter I want to increment on every visit to a User page.
I can do this..

<snippet>
def View(self):
incrementCounter()
# play with data here
myHtmlDoc = "pretend this is a uper profile"
return myHtmlDoc
index_html = View

def Edit(self):
incrementCounter()
# play with data here
myHtmlDoc = "editing the user"
return myHtmlDoc
</snippet>

... but in reality in my real code that one counter increment line ends
up being 20 lines long.

This is typically what AOP is all about - and Python offers a quite
usable support for "ad hoc" aspects, via the function wrapper idiom. The
simplest way is to use closures:

def wrapper(fun, wargs):
def wrapped(fargs):
do_something_before_funcall(wargs, fargs)
res = fun(fargs)
do_something_after_funcall(wargs, fargs)
return res
return wrapped

class Machin(object):
def to_be_wrapped(self, args):
# code here...
# Zope 2.x uses Python 2.3.x, so no @decorator syntax
to_be_wrapped = wrapper(to_be_wrapped, wargs)

If you need something more complex, see Sam's example with a callable
object.

(snip longer code exemple)
 
R

Rob Conner

Genius!
Thanks guys that was exactly the help I was looking for. I'll be
implementing this later today. I don't forsee any problems, so if I
don't post anything else, thank you so much for the help.
 
B

bruno modulix

Rob said:

Nope. Just common Python idioms...
Thanks guys that was exactly the help I was looking for. I'll be
implementing this later today. I don't forsee any problems, so if I
don't post anything else, thank you so much for the help.

You're welcome.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top