# HG changeset patch # User Tero Marttila # Date 1234151052 -7200 # Node ID a1da82870a6b988608b5b54457c217234570fb0c # Parent e4fbf480fbeee7cff1f69d76634a6a4b88fa367a implement better handling of generated URLs with default values, and let default values in urls be overriden by query arguments diff -r e4fbf480fbee -r a1da82870a6b urltree.py --- a/urltree.py Mon Feb 09 04:38:23 2009 +0200 +++ b/urltree.py Mon Feb 09 05:44:12 2009 +0200 @@ -19,16 +19,17 @@ Represents the value of a ValueLabel... love these names """ - def __init__ (self, label, value) : + def __init__ (self, label, value, is_default) : """ Just store """ self.label = label self.value = value + self.is_default = is_default def __str__ (self) : - return "%s=%r" % (self.label.key, self.value) + return "%s%s" % (self.label.key, "=%r" % (self.value, ) if not self.is_default else '') def __repr__ (self) : return "<%s>" % self @@ -100,6 +101,14 @@ abstract + def build_default (self, value_dict) : + """ + Return an (is_default, value) tuple + """ + + abstract + + class EmptyLabel (Label) : """ An empty label, i.e. just a slash in the URL @@ -126,7 +135,10 @@ return True def build (self, values) : - return str(self) + return '' + + def build_default (self, values) : + return (False, '') def __str__ (self) : return '' @@ -166,7 +178,10 @@ return True def build (self, values) : - return str(self) + return self.name + + def build_default (self, values) : + return (False, self.name) def __str__ (self) : return self.name @@ -201,7 +216,7 @@ # lookup the value obj to use value = values.get(self.key) - if not value and self.default : + if not value and self.default is not None : value = self.default elif not value : @@ -212,6 +227,33 @@ return value + def build_default (self, values) : + """ + Check if we have a value in values, and return based on that + + XXX: copy-paste from build() + """ + + # state + is_default = False + + # lookup the value obj to use + value = values.get(self.key) + + if not value and self.default is not None : + is_default = True + value = self.default + + elif not value : + raise URLError("No value given for label %r" % (self.key, )) + + # convert value back to str + value = self.type.build(value) + + # return + return (is_default, value) + + class SimpleValueLabel (ValueLabel) : """ A label that has a name and a simple string value @@ -241,7 +283,7 @@ # default? if value is None and self.default is not None : - return LabelValue(self, self.default) + return LabelValue(self, self.default, True) # only non-empty values! elif value : @@ -252,7 +294,7 @@ # convert with type value = self.type.parse(value) - return LabelValue(self, value) + return LabelValue(self, value, False) def __str__ (self) : return '{%s%s%s}' % ( @@ -457,6 +499,9 @@ # query string self.query_args = dict() + + # remove prepending root / + url_mask = url_mask.lstrip('/') # parse any query string # XXX: conflicts with regexp syntax @@ -511,23 +556,31 @@ # start with the defaults kwargs = self.defaults.copy() + # ...dict of those label values which are set to defaults + default_labels = {} + # then add all the values for label_value in label_values : kwargs[label_value.label.key] = label_value.value + + # add key to default_values? + if label_value.is_default : + default_labels[label_value.label.key] = label_value.label # then parse all query args for key, value in request.get_args() : - # unknown key? - if key not in self.query_args : - # ignore? - if self.config.ignore_extra_args : - continue - - else : - raise URLError("Unrecognized query argument: %r" % (key, )) - - # lookup spec - type, default = self.query_args[key] + # lookup in our defined query args + if key in self.query_args : + # lookup spec + type, default = self.query_args[key] + + # override URL params if they were not given + elif key in default_labels : + type, default = default_labels[key].type, None + + # be strict about extraneous query args? + elif self.config.ignore_extra_args : + raise URLError("Unrecognized query argument: %r" % (key, )) # normalize empty value to None if not value : @@ -575,11 +628,25 @@ def build (self, request, **values) : """ - Build an absolute URL pointing to this target, with the given values + Build an absolute URL pointing to this target, with the given values. Default values are left off if they + are at the end of the URL. """ + + # collect segments as a list of (is_default, segment) values + segments = [(False, request.page_prefix)] + [label.build_default(values) for label in self.label_path] + + # trim default items off the end + for is_default, segment in segments[::-1] : + if is_default : + segments.pop(-1) + + else : + break - # build URL from request page prefix and our labels - return request.page_prefix + '/'.join(label.build(values) for label in self.label_path) + assert segments + + # join + return '/'.join(segment for is_default, segment in segments) def __str__ (self) : return '/'.join(str(label) for label in self.label_path) @@ -785,13 +852,10 @@ """ Adds the given URL to the tree. The URL must begin with a root slash. """ + # get url's label path path = url.get_label_path() - # should begin with root - root_label = path.pop(0) - assert root_label == self.root.label, "URL must begin with root" - # add to root self.root.add_url(url, path)