Skip to content

Instantly share code, notes, and snippets.

@tonyseek
Created September 3, 2012 09:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tonyseek/3608222 to your computer and use it in GitHub Desktop.
Save tonyseek/3608222 to your computer and use it in GitHub Desktop.
A traversal style dispatcher which is alike Quixote.
#!/usr/bin/env python
#-*- coding:utf-8 -*-
def dispatch(entry_object, path, spliter="/"):
"""Starting with an entry object and dispatching the request by url path.
>>> class GroupList(object):
... exports = frozenset(["create"])
...
... def _q_index(self):
... return "groups"
...
... def _q_lookup(self, group_id):
... return Group(group_id)
...
... def create(self):
... return "apply-create"
>>>
>>> class Group(object):
... exports = frozenset(["members", "overview"])
...
... def __init__(self, group_id):
... self.group_id = group_id
...
... def _q_index(self):
... return self.overview()
...
... @property
... def members(self):
... return MemberList(self.group_id)
...
... def overview(self):
... return "group-%s-overview" % self.group_id
>>>
>>> class MemberList(object):
... def __init__(self, group_id):
... self.group_id = group_id
...
... def _q_access(self):
... if not self.group_id.isdigit():
... raise ValueError("WTF")
...
... def _q_index(self):
... return "members-of-group-%s" % self.group_id
...
>>> class MainDispatcher(object):
... exports = ["groups"]
... groups = GroupList()
>>>
>>> entry_object = MainDispatcher()
>>>
>>> dispatch(entry_object, "/")
Traceback (most recent call last):
...
TraversalError: Not found public attribute or dynamic finder
>>>
>>> dispatch(entry_object, "/groups")()
'groups'
>>> dispatch(entry_object, "/groups/create")()
'apply-create'
>>> dispatch(entry_object, "/groups/100001")()
'group-100001-overview'
>>> dispatch(entry_object, "/groups/100001/overview")()
'group-100001-overview'
>>> dispatch(entry_object, "/groups/100001/members")()
'members-of-group-100001'
>>> dispatch(entry_object, "/groups/powerby/members")()
Traceback (most recent call last):
...
ValueError: WTF
"""
current_object = entry_object
path_segments = path.lstrip(spliter).split(spliter)
for segment in path_segments:
#: check access
_check_access(current_object)
if segment in current_object.exports:
#: transform into found public attribute
current_object = getattr(current_object, segment)
elif hasattr(current_object, "_q_lookup"):
#: transform into found dynamic finder
current_object = current_object._q_lookup(segment)
else:
#: failure
raise TraversalError("Not found public attribute or dynamic finder")
#: check access
_check_access(current_object)
if callable(current_object):
#: final node is function or custom callable object
return current_object
if hasattr(current_object, "_q_index"):
#: final node is module which has an index function
return current_object._q_index
raise TraversalError("Invalid final handler.")
def _check_access(current_object):
"""Check the access permission of current object."""
if hasattr(current_object, "_q_access"):
current_object._q_access()
class TraversalError(Exception):
"""Failed to dispatch request by traversal."""
if __name__ == "__main__":
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment