FAQ
I'm writing a class that derives it's functionality from mix-ins.
Here's the code:

def boilerplate(what): # This used to be a decorator, but all of
the
##what = f.__name__ # function bodies turned out to be
'pass'.
'Validate the user, then call the appropriate plug-in.'
def template(self, which, username, password, *args):
if not self.security.isAuthorised(username, password,
which, what):
raise Exception('Unauthorised access')
return getattr(self.blog, what)(which, *args)
template.__name__ = what
template.__doc__ = getattr(self.blog, what).__doc__
return template

class MetaWeblog(object):
def __init__(self,
securityHandler=SimpleSecurityHandler,
blogHandler=SimpleBlogHandler):
self.security = securityHandler()
self.blog = blogHandler()
newPost = boilerplate('newPost')
editPost = boilerplate('editPost')
getPost = boilerplate('getPost')
# etc, etc, etc

I'd like to replace the method definitions with a loop:
for what in attr_list:
setattr(klass, what, boilerplate(what))

That begs the question of where I define 'klass' and 'attr_list'.
Should I use a class decorator, or a metaclass? In favor of
decorators is that I can see how to do it; in favor of a metaclass is
that I get to learn how to use them. ;-) What are the other pros and
cons for each choice?

Search Discussions

  • Sam Denton at May 11, 2009 at 9:44 pm

    On May 11, 1:16?pm, samwyse wrote:
    I'm writing a class that derives it's functionality from mix-ins.
    While waiting, I gave a try at using class decorators. Here's what I
    came up with:

    def add_methods(*m_list, **kwds):
    def wrapper(klass):
    for m_name in m_list:
    def template(self, which, username, password, *args):
    if not self.security.isAuthorised(username, password,
    which, m_name):
    raise Exception('Unauthorised access')
    return getattr(self.blog, m_name)(which, *args)
    dotted_name = kwds.get('prefix', '') + m_name
    template.__name__ = dotted_name
    template.__doc__ = dotted_name
    setattr(klass, dotted_name, template)
    return klass
    return wrapper

    @add_methods('newPost', 'editPost', 'getPost', 'newMediaObject',
    'getCategories', 'getRecentPosts',
    prefix='metaWeblog.')
    class MetaWeblog(object):
    def __init__(self,
    securityHandler=SimpleSecurityHandler,
    blogHandler=SimpleBlogHandler):
    self.security = securityHandler()
    self.blog = blogHandler()

    if __name__ == '__main__':
    server = SimpleXMLRPCServer(("localhost", 8080))
    server.register_instance(MetaWeblog())
    server.register_introspection_functions()
    server.serve_forever()

    The problem that I'm having is that when I call newPost,
    SimpleBlogHandler.getRecentPosts gets invoked. Apparently add_methods
    isn't generating unique templates. I think I need to move 'template'
    into another function, but I'm starting to wonder if metaclasses might
    work better. Any ideas?
  • Carl Banks at May 12, 2009 at 2:01 am

    On May 11, 11:16?am, samwyse wrote:
    I'm writing a class that derives it's functionality from mix-ins.
    Here's the code:

    ? ? def boilerplate(what): ? # This used to be a decorator, but all of
    the
    ? ? ? ? ##what = f.__name__ ?# function bodies turned out to be
    'pass'.
    ? ? ? ? 'Validate the user, then call the appropriate plug-in.'
    ? ? ? ? def template(self, which, username, password, *args):
    ? ? ? ? ? ? if not self.security.isAuthorised(username, password,
    which, what):
    ? ? ? ? ? ? ? ? raise Exception('Unauthorised access')
    ? ? ? ? ? ? return getattr(self.blog, what)(which, *args)
    ? ? ? ? template.__name__ = what
    ? ? ? ? template.__doc__ = getattr(self.blog, what).__doc__
    ? ? ? ? return template

    ? ? class MetaWeblog(object):
    ? ? ? ? def __init__(self,
    ? ? ? ? ? ? ? ? ? ? ?securityHandler=SimpleSecurityHandler,
    ? ? ? ? ? ? ? ? ? ? ?blogHandler=SimpleBlogHandler):
    ? ? ? ? ? ? self.security = securityHandler()
    ? ? ? ? ? ? self.blog = blogHandler()
    ? ? ? ? newPost = boilerplate('newPost')
    ? ? ? ? editPost = boilerplate('editPost')
    ? ? ? ? getPost = boilerplate('getPost')
    ? ? ? ? # etc, etc, etc

    I'd like to replace the method definitions with a loop:
    ? ? ? ? for what in attr_list:
    ? ? ? ? ? ? setattr(klass, what, boilerplate(what))

    That begs the question of where I define 'klass' and 'attr_list'.
    Should I use a class decorator, or a metaclass?
    Here's the thing: unless you have advance knowledge of the methods
    defined by self.blog, you can't get the attr_list at class definition
    time, which means neither the metaclass nor the decorator would be a
    good approach. If that's the case, you should define newPost,
    editPost, and whatever other methods of self.blog as ordinary
    attributes of self, within the init function. boilerplate would be
    the same except you would pass self to it and allow template to use it
    from its nested scope (it would no longer be an instance method since
    it's an ordinary attribute).

    If you do know what the methods of self.blog will be, then that's
    where you get attr_list from. So, for instance, if blogHandler always
    returns an object of type Blog, then you could inspect Blog's type
    dict to see what methods are defined in it; in fact you probably want
    to check the whole MRO for Blog, like this (untested):

    attr_list = []
    for cls in Blog.__mro__:
    for value in cls.__dict__:
    if is_wrapped_method(value):
    attr_list.append(value)


    A metaclass is probably overkill to assign the wrapped blog methods.
    I probably wouldn't even bother with the decorator, and just write the
    loop after the class definition. Then you can use MetaBlog directly
    for klass.

    class MetaBlog(object):
    ...

    for what in attr_list:
    setattr(MetaBlog, what, boilerplate(what))


    If it were the kind of thing I found myself doing often I'd refactor
    into a decorator.


    Carl Banks
  • Sam Denton at May 12, 2009 at 11:45 am

    On May 11, 9:01?pm, Carl Banks wrote:
    On May 11, 11:16?am, samwyse wrote:

    Should I use a class decorator, or a metaclass?
    Here's the thing: unless you have advance knowledge of the methods
    defined by self.blog, you can't get the attr_list at class definition
    time, which means neither the metaclass nor the decorator would be a
    good approach. ?If that's the case, you should define newPost,
    editPost, and whatever other methods of self.blog as ordinary
    attributes of self, within the init function. ?boilerplate would be
    the same except you would pass self to it and allow template to use it
    from its nested scope (it would no longer be an instance method since
    it's an ordinary attribute).

    If you do know what the methods of self.blog will be, then that's
    where you get attr_list from. ?So, for instance, if blogHandler always
    returns an object of type Blog, then you could inspect Blog's type
    dict to see what methods are defined in it; in fact you probably want
    to check the whole MRO for Blog, like this (untested):

    attr_list = []
    for cls in Blog.__mro__:
    ? ? for value in cls.__dict__:
    ? ? ? ? if is_wrapped_method(value):
    ? ? ? ? ? ? attr_list.append(value)

    A metaclass is probably overkill to assign the wrapped blog methods.
    I probably wouldn't even bother with the decorator, and just write the
    loop after the class definition. ?Then you can use MetaBlog directly
    for klass.

    class MetaBlog(object):
    ? ? ...

    for what in attr_list:
    ? ? setattr(MetaBlog, what, boilerplate(what))

    If it were the kind of thing I found myself doing often I'd refactor
    into a decorator.
    Unfortunately, 'boilerplate()' uses the handlers that I provide when
    MetaBlog is instantiated. I tried the following, but it didn't work
    (for reasons that were obvious in retrospect).

    def instantiate_template(m_name, instance):
    isAuthorised = instance.security.isAuthorised
    method_to_wrap = getattr(instance.blog, m_name)
    def template(self, which, username, password, *args):
    if not isAuthorised(username, password, which, m_name):
    raise Exception('Unauthorised access')
    return method_to_wrap(which, *args)
    template.__name__ = method_to_wrap.__name__
    template.__doc__ = method_to_wrap.__doc__
    return template

    class MetaWeblog(object):
    def __init__(self,
    securityHandler=SimpleSecurityHandler,
    blogHandler=SimpleBlogHandler):
    self.security = securityHandler()
    self.blog = blogHandler()
    # from http://www.xmlrpc.com/metaWeblogApi
    m_prefix = 'metaWeblog.'
    m_list = ('newPost', 'editPost', 'getPost', 'newMediaObject',
    'getCategories', 'getRecentPosts', )

    # Here's where things fell apart
    for m_name in m_list:
    dotted_name = m_prefix + m_name
    method = instantiate_template(m_name, self)
    setattr(self, dotted_name, method)

    So, I replaced that last for-loop with this:

    # Since we're about to monkey-patch the class, we should
    # make certain that all instances use the same handlers.
    handlers = (self.security, self.blog)
    try:
    assert getattr(self.__class__, '_handlers') == handlers
    except AttributeError:
    for m_name in m_list:
    dotted_name = m_prefix + m_name
    method = instantiate_template(m_name, self)
    setattr(self.__class__, dotted_name, method)
    setattr(self.__class__, '_handlers', handlers)

    This is good enough for now, since I can't conceive of a reason why
    MetaBlog would be instantiated more than once. If it were, on the
    other hand, it would probably be because you wanted to use different
    handlers. In that case, I think I'd want to use a class factory,
    something like this:
    server.register_instance(
    MetaWeblogFactory(securityHandler, blogHandler)()
    )

    Anyway, thanks for getting me over a conceptual hump.
  • Carl Banks at May 12, 2009 at 10:38 pm

    On May 12, 4:45?am, samwyse wrote:
    Unfortunately, 'boilerplate()' uses the handlers that I provide when
    MetaBlog is instantiated.
    In that case, the handler functions should be attributes of the
    instance, not of the class. Do something like this:

    class MetaBlog(object):
    def __init__(self):
    self.blog = blogHander()
    for name in self.blog.get_methods():
    setattr(self,name,self.boilerplate(name))

    def boilerplate(self,name):
    method = getattr(self.blog,name)
    def template(*args):
    self.do_verification_here()
    method(*args)
    return template


    Carl Banks

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedMay 11, '09 at 6:16p
activeMay 12, '09 at 10:38p
posts5
users2
websitepython.org

2 users in discussion

Sam Denton: 3 posts Carl Banks: 2 posts

People

Translate

site design / logo © 2022 Grokbase