Issue Details (XML | Word | Printable)

Key: MODPYTHON-73
Type: Improvement Improvement
Status: Closed Closed
Resolution: Fixed
Priority: Minor Minor
Assignee: Unassigned
Reporter: Graham Dumpleton
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
mod_python

Using objects to create an explicit hierarchy.

Created: 13/Aug/05 10:43 AM   Updated: 05/Mar/06 02:20 PM
Return to search
Component/s: publisher
Affects Version/s: 3.2.7
Fix Version/s: 3.2.7

Time Tracking:
Not Specified

File Attachments:
  Size
Text File Licensed for inclusion in ASF works util.diff.txt 2005-08-13 10:44 AM Graham Dumpleton 2 kB

Resolution Date: 01/Sep/05 06:05 PM


 Description  « Hide
Cut and paste of idea presented on mailing list. See:

  http://www.mail-archive.com/python-dev@httpd.apache.org/msg00294.html

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


 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Graham Dumpleton added a comment - 13/Aug/05 10:44 AM
Attached path for this change. Why can't you attache patches when issue created. :-(

Graham Dumpleton made changes - 13/Aug/05 10:44 AM
Field Original Value New Value
Attachment util.diff.txt [ 12311731 ]
Repository Revision Date User Message
ASF #265674 Thu Sep 01 08:54:47 UTC 2005 nlehuen Graham's patch for MODPYTHON-73
Files Changed
MODIFY /httpd/mod_python/trunk/lib/python/mod_python/util.py

Repository Revision Date User Message
ASF #265677 Thu Sep 01 09:05:17 UTC 2005 nlehuen Added unit tests for MODPYTHON-73
Files Changed
MODIFY /httpd/mod_python/trunk/test/htdocs/tests.py
MODIFY /httpd/mod_python/trunk/test/test.py
MODIFY /httpd/mod_python/trunk/src/include/mpversion.h

Repository Revision Date User Message
ASF #265736 Thu Sep 01 14:05:43 UTC 2005 nlehuen Added a new unit test to match Graham's test in MODPYTHON-73.
Files Changed
MODIFY /httpd/mod_python/trunk/test/htdocs/tests.py
MODIFY /httpd/mod_python/trunk/test/test.py

Nicolas Lehuen added a comment - 01/Sep/05 06:05 PM
Checked in Graham's patch and wrote a unit test for it. Everything is OK.

Nicolas Lehuen made changes - 01/Sep/05 06:05 PM
Fix Version/s 3.2.0 [ 11060 ]
Status Open [ 1 ] Resolved [ 5 ]
Resolution Fixed [ 1 ]
Nicolas Lehuen added a comment - 01/Sep/05 06:13 PM
BTW Graham, after I wrote the unit test, I remembered that your patch should not be needed. With the new publisher, the hierarchy traversal is taken care of at the publisher level, and apply_fs_data don't have to worry about it. Indeed, I've reverted your patch on my working copy and ran the new unit test, and mod_python behaved as you want without changing anything to apply_fs_data. Unless I missed something in your enhancement request ?

Graham Dumpleton added a comment - 01/Sep/05 08:36 PM
Note that util.apply_fs_data() can be called directly independent of mod_python.publisher.
Thus it may be redundant within the context of mod_python.publisher but still useful as a
separate change when the function is used directly, which is what I had in mind for it.

Graham Dumpleton added a comment - 01/Sep/05 08:52 PM
I checked again, you mustn't have it quite right as it definitely fails for me. Eg:

def index():
  return "<html></body><p>XOX</p></body></html>"

class _Dummy:
  pass
  
dummy = _Dummy()
dummy.__call__ = index

When I use URL /~grahamd/mp32/page/dummy I get:

Mod_python error: "PythonHandler mod_python.publisher"

Traceback (most recent call last):

  File "/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages/mod_python/apache.py", line 299, in HandlerDispatch
    result = object(req)

  File "/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages/mod_python/publisher.py", line 213, in handler
    published = publish_object(req, object)

  File "/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages/mod_python/publisher.py", line 410, in publish_object
    return publish_object(req,util.apply_fs_data(object, req.form, req=req))

  File "/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages/mod_python/util.py", line 398, in apply_fs_data
    fc = object.__call__.im_func.func_code

AttributeError: 'function' object has no attribute 'im_func'

With the fix I suggested, it works.

If I import the file into Python command line, can call it okay:

>>> import page
>>> page.dummy()
'<html></body><p>XOX</p></body></html>'

So it is valid to do in Python.

Nicolas Lehuen added a comment - 01/Sep/05 11:07 PM
OK, the unit test I performed was not exactly what you meant :

class Mapping(object):
    def __init__(self,name):
        self.name = name

    def __call__(self,req):
        return "Called %s"%self.name
hierarchy_root = Mapping("root");
hierarchy_root.page1 = Mapping("page1")
hierarchy_root.page1.subpage1 = Mapping("subpage1")
hierarchy_root.page2 = Mapping("page2")

This worked before your patch and still works after, but that wasn't what you meant. I've added a new unit test to match your needs :

class Mapping2:
    pass
hierarchy_root_2 = Mapping2()
hierarchy_root_2.__call__ = index
hierarchy_root_2.page1 = index
hierarchy_root_2.page2 = index

And indeed, your patch is required for this to work.

Graham Dumpleton made changes - 05/Mar/06 02:20 PM
Status Resolved [ 5 ] Closed [ 6 ]