Have a strange patch here for consideration.

In CherryPy, one can manually construct the page hierarchy by writing:

  cpg.root.onepage = OnePage()
  cpg.root.otherpage = OtherPage()

  cpg.root.some = Page()
  cpg.root.some.page = Page()

The closest equivalent to this in mod_python is the publisher handler,
whereby a URL will be mapped to attributes and member functions of a
class. One generally though has to create an actual class to encapsulate
all the bits together.

One can to a degree with publisher create a mapping without creating a
proper class by using:

  class _Mapping:
    pass

  def _method1():
    return "_method1"

  def _method2():
    return "_method2"

  object = _Mapping()
  object.onepage = _method1
  object.otherpage = _method2

What isn't possible though without creating a real class is have a
normal function which is called when the dummy mapping object itself
is the target. Ie., following does not work:

  object.__call__ = _method1

This is because util.apply_fs_data() assumes that __call__() is always
an object method.

I know this is sort of an abuse of __call__(), but it does actually
work in Python itself, just not in mod_python when URLs are mapped to
object.

>>> class A:
...   pass
...
>>> def _method():
...   return "method"
...
>>> a=A()
>>> a.__call__ = _method
>>>
>>> a()
'method'

Anyway, I have attached a patch which would allow this sort of thing to
actually work within mod_python.

I feel it could be a useful way of quickly constructing special object
hierarchies from other functions, objects and attributes without having
to actually create real classes.

For example:

def _method():
  return "method"

class _Mapping:
  pass

_subdir1 = _Mapping()
_subdir1.__call__ = _method # .../root/subdir1
_subdir1.page1 = _method # .../root/subdir1/page1
_subdir1.page2 = _method # .../root/subdir1/page2

root = _Mapping()
root.__call__ = _method # .../root
root.page1 = _method # .../root/page1
root.subdir1 = _subdir1

Comments?

Index: lib/python/mod_python/util.py
===================================================================
--- lib/python/mod_python/util.py       (revision 231212)
+++ lib/python/mod_python/util.py       (working copy)
@@ -371,20 +371,6 @@
    then call the object, return the result.
    """
 
-   # add form data to args
-   for field in fs.list:
-       if field.filename:
-           val = field
-       else:
-           val = field.value
-       args.setdefault(field.name, []).append(val)
-
-   # replace lists with single values
-   for arg in args:
-       if ((type(args[arg]) is ListType) and
-           (len(args[arg]) == 1)):
-           args[arg] = args[arg][0]
-
    # we need to weed out unexpected keyword arguments
    # and for that we need to get a list of them. There
    # are a few options for callable objects here:
@@ -409,9 +395,27 @@
        expected = []
    elif hasattr(object, '__call__'):
        # callable object
-       fc = object.__call__.im_func.func_code
-       expected = fc.co_varnames[1:fc.co_argcount]
+       if type(object.__call__) is MethodType:
+           fc = object.__call__.im_func.func_code
+           expected = fc.co_varnames[1:fc.co_argcount]
+       else:
+           # abuse of objects to create hierarchy
+           return apply_fs_data(object.__call__, fs, **args)
 
+   # add form data to args
+   for field in fs.list:
+       if field.filename:
+           val = field
+       else:
+           val = field.value
+       args.setdefault(field.name, []).append(val)
+
+   # replace lists with single values
+   for arg in args:
+       if ((type(args[arg]) is ListType) and
+           (len(args[arg]) == 1)):
+           args[arg] = args[arg][0]
+
    # remove unexpected args unless co_flags & 0x08,
    # meaning function accepts **kw syntax
    if fc is None:


Graham

Reply via email to