# HG changeset patch # User Tero Marttila # Date 1234031647 -7200 # Node ID 1f13c384508e701ea12b01ad46d658dfa2dd0dd6 # Parent 02d4040d594691794df8cca53927695c9e7ee117 URLTree works, apart from trailing default values diff -r 02d4040d5946 -r 1f13c384508e sites/irclogs.qmsk.net/urls.py --- a/sites/irclogs.qmsk.net/urls.py Sat Feb 07 20:05:57 2009 +0200 +++ b/sites/irclogs.qmsk.net/urls.py Sat Feb 07 20:34:07 2009 +0200 @@ -4,6 +4,7 @@ """ import re +import os.path # our own handlers import handlers @@ -18,6 +19,25 @@ pass +class LabelValue (object) : + """ + Represents the value of a ValueLabel... + """ + + def __init__ (self, label, value) : + """ + Just store + """ + + self.label = label + self.value = value + + def __str__ (self) : + return "%s=%s" % (self.label.key, self.value) + + def __repr__ (self) : + return "<%s>" % self + class Label (object) : """ Base class for URL labels (i.e. the segments of the URL between /s) @@ -54,7 +74,14 @@ # invalid raise URLError("Invalid label: %r" % (mask, )) + + def match (self, value) : + """ + Match this label against the given value, returning either True to match without a value, a LabelValue + object, or boolean false to not match + """ + abstract class EmptyLabel (Label) : """ @@ -68,6 +95,14 @@ return isinstance(other, EmptyLabel) + def match (self, value) : + """ + Match empty string -> True + """ + + if value == '' : + return True + def __str__ (self) : return '' @@ -92,12 +127,22 @@ return isinstance(other, StaticLabel) and self.name == other.name + def match (self, value) : + """ + Match exactly -> True + """ + + if value == self.name : + return True + def __str__ (self) : return self.name class ValueLabel (Label) : """ A label with a key and a value + + XXX: do we even need this? """ def __init__ (self, key, default) : @@ -129,6 +174,15 @@ super(SimpleValueLabel, self).__init__(key, default) + def match (self, value) : + """ + Match -> LabelValue + + XXX: empty string? + """ + + return LabelValue(self, value) + def __str__ (self) : if self.default : return '{%s=%s}' % (self.key, self.default) @@ -162,13 +216,21 @@ # copy self.label_path return list(self.label_path) - def execute (self, request, xxx) : + def execute (self, request, label_values) : """ - Invoke the handler with the correct parameters + Invoke the handler, using the given label values """ + + # start with the defaults + kwargs = self.defaults() - xxx - + # then add all the values + for label_value in label_values : + kwargs[label_value.label.key] = label_value.value + + # execute the handler + return self.handler(request, **kwargs) + def __str__ (self) : return '/'.join(str(label) for label in self.label_path) @@ -250,60 +312,60 @@ # recurse to handle the rest of the label_path child.add_url(url, label_path) - def match_label (self, label) : + def match (self, label_path) : """ - Match our label mask against the given label value. - - Returns either False (no match), True (matched, no value) or a (key, value) pair. - """ - - # simple mask - if self.label.beginswith('{') and self.label.endswith('}') : - value_name = self.label.strip('{}') + Locate the URL object corresponding to the given label_path value under this node. - return (value_name, label) - - # literal mask - elif self.label == label : - return True - - else : - return False - - def match (self, path, label_path) : - """ - Locate the URL object corresponding to the given label_path value under this node, with the given path - containing the previously matched label values + Returns a (url, label_values) tuple """ # empty label_path? - if not label_path or not label_path[0] : - # we wanted this node + if not label_path or label_path[0] == '' : + # the search ends at this node if self.url : # this URL is the best match - return [self] - - elif self.children : - # continue testing our children - label = None + return (self.url, []) else : # incomplete URL - raise URLError(url, "no URL handler defined for this Node") + raise URLError("no URL handler defined for this Node") else : - # pop our child label from the label path + # pop the next label from the label path label = label_path.pop(0) - # build a list of matching children - matches = [] + # return one match... + match = value = None # recurse through our children for child in self.children : - matches.append(child.match(label_path)) + # match value + value = child.label.match(label) - # return the list of matching children - return matches + # skip those that don't match at all + if not value : + continue; + + # already found a match? :/ + if match : + raise URLError("Ambiguous URL") + + # ok, but continue looking to make sure there's no ambiguous URLs + match = child + + # found something? + if not match : + raise URLError("No child found for label") + + # ok, recurse into the match + url, label_value = match.match(label_path) + + # add our value? + if isinstance(value, LabelValue) : + label_value.append(value) + + # return the match + return url, label_value def dump (self, indent=0) : """ @@ -355,17 +417,23 @@ def match (self, url) : """ - Returns the URL object best corresponding to the given url + Find the URL object best corresponding to the given url, matching any ValueLabels. + + Returns an (URL, [LabelValue]) tuple. """ # normalize the URL url = os.path.normpath(url) # split it into labels - label_path = url.split('/') + path = url.split('/') + + # ensure that it starts with a / + root_label = path.pop(0) + assert self.root.label.match(root_label), "URL must begin with root" # just match starting at root - return self.root.match(label_path) + return self.root.match(path) def handle_request (self, request) : """ @@ -373,12 +441,10 @@ """ # get the request's URL path - url = self.match(request.get_page_name()) - - # XXX: figure out the argument values... + url, label_values = self.match(request.get_page_name()) # let the URL handle it - url.execute(request, xxx) + url.execute(request, label_values) # urls index = URL( '/', handlers.index )