[Twisted-Python] Refactoring GetChildWithDefault (removal is ideal)
Clark C. Evans
cce at clarkevans.com
Fri Feb 28 11:37:29 EST 2003
First, before I get started, let me just say that I think that
the resource delegation mechanism in this library is just
brilliant in its simplicity and operation.
However, after much musing, I've decided that getChildWithDefault
isn't very useful and kinda mucks up the waters:
# public interface
def getChild(self, path, request):
return error.NoResource("No such child resource.")
# private interface
def getChildWithDefault(self, path, request):
if self.children.has_key(path):
return self.children[path]
return self.getChild(path, request)
def getChildForRequest(self, request):
res = self
while request.postpath and not res.isLeaf:
pathElement = request.postpath.pop(0)
request.acqpath.append(pathElement)
request.prepath.append(pathElement)
res = res.getChildWithDefault(pathElement, request)
return res
Suggested refactor:
# module variables
resourceNotFound = error.NoResource('No such child resource.')
# public interface
def getChild(self, path, request):
if self.children.has_key(path):
return self.children[path]
return None
# private interface (called on root only)
def getChildForRequest(self, request):
res = self
while request.postpath and not res.isLeaf:
pathElement = request.postpath.pop(0)
request.acqpath.append(pathElement)
request.prepath.append(pathElement)
res = res.getChild(pathElement, request)
if res is None: return resourceNotFound
return res
Rationale:
1. It is very useful to have a *public* interface function
which is _always_ called for every request. In this manner,
an application can implement request modifiers/filters.
Currently the function that satisfies this need,
getChildWithDefault is private.
2. Unless you break the public interface, the current mechanism
always searches children first without a hook for the
application. This isn't always desireable.
For example, a 'security' FilterResource may want to check
user access before descending down a given resource sub-tree.
Yes, you could implement this security as part of each
resource (by inheriting); but I feel that this is inferior
to haveing a more "component" based solution where the
security filter is injected into the resource tree.
3. From a object-oriented perspective, getChildWithDefault
actually does the 'default' behavior that people may want
to inherit and discard, and thus this default searching
code should go into getChild instead; the user can then
decide how to best use this default behavior.
4. getChild's current interface, always returning a resource,
albeit a not-very-useful resource limits possible innovative
combinations of intra-resource delegation and cooperation.
It should intead return a None value which can be tested for...
Impact on change:
Anyone who wrote a previous resource who dependend on the
set of children being searched *before* getChild is called
would break. I think that this is probably a pretty
rare event; but it is a clean break, and the fix is simple...
class MyResource(Resource):
def getChild(self,path,request):
res = Resource.getChild(self,path,request)
if res is None:
// try to create a dynamic resource
return None
Alternatively, if they wanted to search the dynamic
resources first, they could code it this way:
class MyResource(Resource):
def getChild(self,path,resource):
res = None
// try to create dynamic resource
if res is not None: return res
return Resource.getChild(self,path,request)
Perhaps a few examples would have to be changed, but most
likely the above impact is in only a few select resources.
Alternative refactor:
The simplest alternative is to add getChildWithDefault to the
public interface and document the mechanism. It think that this,
in the long run is not as good as the proposed refactor since
it adds extra complexity for the "search children first or last"
behavior choice. It's just clunky the way it is, IMHO.
In any case, the Resource finding mechanism in Twisted is very
clever, and I'm using my PathArgs *alot* so I'd like a solution
so that my requirements don't require a breaking the public
interface. Ohh, and to answer:
On Thu, Feb 27, 2003 at 05:51:02PM +0100, Mario Ruggier wrote:
| A source of confusion for me is knowing which,
| and when, specific methods are called automatically.
| Particularly, it would be nice to have a clarification (in the
| API docs) of when the methods getChildForRequest() and
| getChildWithDefault() are called -- they seem not be called in
| a non-siteroot resource. Things worked well with PathArgs, it
| being set as root resource, but for an arbitrary resource,
| like the example I previoulsy included, the game seems to
| change .
getChildWithDefault is infact called on non-siteroot resources,
so the current code for PathArgs will work at any level... albeit
a violation of the private/public encapsulation. But yes, the
overall mechanism (by having a 3rd wheel) is less than ideal.
Best,
Clark
More information about the Twisted-Python
mailing list