Back to index

moin  1.9.0~rc2
routing.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     werkzeug.routing
00004     ~~~~~~~~~~~~~~~~
00005 
00006     When it comes to combining multiple controller or view functions (however
00007     you want to call them) you need a dispatcher.  A simple way would be
00008     applying regular expression tests on the ``PATH_INFO`` and calling
00009     registered callback functions that return the value then.
00010 
00011     This module implements a much more powerful system than simple regular
00012     expression matching because it can also convert values in the URLs and
00013     build URLs.
00014 
00015     Here a simple example that creates an URL map for an application with
00016     two subdomains (www and kb) and some URL rules:
00017 
00018     >>> m = Map([
00019     ...     # Static URLs
00020     ...     Rule('/', endpoint='static/index'),
00021     ...     Rule('/about', endpoint='static/about'),
00022     ...     Rule('/help', endpoint='static/help'),
00023     ...     # Knowledge Base
00024     ...     Subdomain('kb', [
00025     ...         Rule('/', endpoint='kb/index'),
00026     ...         Rule('/browse/', endpoint='kb/browse'),
00027     ...         Rule('/browse/<int:id>/', endpoint='kb/browse'),
00028     ...         Rule('/browse/<int:id>/<int:page>', endpoint='kb/browse')
00029     ...     ])
00030     ... ], default_subdomain='www')
00031 
00032     If the application doesn't use subdomains it's perfectly fine to not set
00033     the default subdomain and use the `Subdomain` rule factory.  The endpoint
00034     in the rules can be anything, for example import paths or unique
00035     identifiers.  The WSGI application can use those endpoints to get the
00036     handler for that URL.  It doesn't have to be a string at all but it's
00037     recommended.
00038 
00039     Now it's possible to create a URL adapter for one of the subdomains and
00040     build URLs:
00041 
00042     >>> c = m.bind('example.com')
00043     >>> c.build("kb/browse", dict(id=42))
00044     'http://kb.example.com/browse/42/'
00045     >>> c.build("kb/browse", dict())
00046     'http://kb.example.com/browse/'
00047     >>> c.build("kb/browse", dict(id=42, page=3))
00048     'http://kb.example.com/browse/42/3'
00049     >>> c.build("static/about")
00050     '/about'
00051     >>> c.build("static/index", force_external=True)
00052     'http://www.example.com/'
00053 
00054     >>> c = m.bind('example.com', subdomain='kb')
00055     >>> c.build("static/about")
00056     'http://www.example.com/about'
00057 
00058     The first argument to bind is the server name *without* the subdomain.
00059     Per default it will assume that the script is mounted on the root, but
00060     often that's not the case so you can provide the real mount point as
00061     second argument:
00062 
00063     >>> c = m.bind('example.com', '/applications/example')
00064 
00065     The third argument can be the subdomain, if not given the default
00066     subdomain is used.  For more details about binding have a look at the
00067     documentation of the `MapAdapter`.
00068 
00069     And here is how you can match URLs:
00070 
00071     >>> c = m.bind('example.com')
00072     >>> c.match("/")
00073     ('static/index', {})
00074     >>> c.match("/about")
00075     ('static/about', {})
00076     >>> c = m.bind('example.com', '/', 'kb')
00077     >>> c.match("/")
00078     ('kb/index', {})
00079     >>> c.match("/browse/42/23")
00080     ('kb/browse', {'id': 42, 'page': 23})
00081 
00082     If matching fails you get a `NotFound` exception, if the rule thinks
00083     it's a good idea to redirect (for example because the URL was defined
00084     to have a slash at the end but the request was missing that slash) it
00085     will raise a `RequestRedirect` exception.  Both are subclasses of the
00086     `HTTPException` so you can use those errors as responses in the
00087     application.
00088 
00089     If matching succeeded but the URL rule was incompatible to the given
00090     method (for example there were only rules for `GET` and `HEAD` and
00091     routing system tried to match a `POST` request) a `MethodNotAllowed`
00092     method is raised.
00093 
00094 
00095     :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
00096                              Thomas Johansson.
00097     :license: BSD, see LICENSE for more details.
00098 """
00099 import re
00100 from urlparse import urljoin
00101 from itertools import izip
00102 
00103 from werkzeug.utils import url_encode, url_quote, redirect, format_string
00104 from werkzeug.exceptions import HTTPException, NotFound, MethodNotAllowed
00105 
00106 
00107 _rule_re = re.compile(r'''
00108     (?P<static>[^<]*)                           # static rule data
00109     <
00110     (?:
00111         (?P<converter>[a-zA-Z_][a-zA-Z0-9_]*)   # converter name
00112         (?:\((?P<args>.*?)\))?                  # converter arguments
00113         \:                                      # variable delimiter
00114     )?
00115     (?P<variable>[a-zA-Z][a-zA-Z0-9_]*)         # variable name
00116     >
00117 ''', re.VERBOSE)
00118 _simple_rule_re = re.compile(r'<([^>]+)>')
00119 
00120 
00121 def parse_rule(rule):
00122     """Parse a rule and return it as generator. Each iteration yields tuples
00123     in the form ``(converter, arguments, variable)``. If the converter is
00124     `None` it's a static url part, otherwise it's a dynamic one.
00125 
00126     :internal:
00127     """
00128     pos = 0
00129     end = len(rule)
00130     do_match = _rule_re.match
00131     used_names = set()
00132     while pos < end:
00133         m = do_match(rule, pos)
00134         if m is None:
00135             break
00136         data = m.groupdict()
00137         if data['static']:
00138             yield None, None, data['static']
00139         variable = data['variable']
00140         converter = data['converter'] or 'default'
00141         if variable in used_names:
00142             raise ValueError('variable name %r used twice.' % variable)
00143         used_names.add(variable)
00144         yield converter, data['args'] or None, variable
00145         pos = m.end()
00146     if pos < end:
00147         remaining = rule[pos:]
00148         if '>' in remaining or '<' in remaining:
00149             raise ValueError('malformed url rule: %r' % rule)
00150         yield None, None, remaining
00151 
00152 
00153 def get_converter(map, name, args):
00154     """Create a new converter for the given arguments or raise
00155     exception if the converter does not exist.
00156 
00157     :internal:
00158     """
00159     if not name in map.converters:
00160         raise LookupError('the converter %r does not exist' % name)
00161     if args:
00162         storage = type('_Storage', (), {'__getitem__': lambda s, x: x})()
00163         args, kwargs = eval(u'(lambda *a, **kw: (a, kw))(%s)' % args, {}, storage)
00164     else:
00165         args = ()
00166         kwargs = {}
00167     return map.converters[name](map, *args, **kwargs)
00168 
00169 
00170 class RoutingException(Exception):
00171     """Special exceptions that require the application to redirect, notifies
00172     him about missing urls etc.
00173 
00174     :internal:
00175     """
00176 
00177 
00178 class RequestRedirect(HTTPException, RoutingException):
00179     """Raise if the map requests a redirect. This is for example the case if
00180     `strict_slashes` are activated and an url that requires a leading slash.
00181 
00182     The attribute `new_url` contains the absolute destination url.
00183     """
00184     code = 301
00185 
00186     def __init__(self, new_url):
00187         RoutingException.__init__(self, new_url)
00188         self.new_url = new_url
00189 
00190     def get_response(self, environ):
00191         return redirect(self.new_url, 301)
00192 
00193 
00194 class RequestSlash(RoutingException):
00195     """Internal exception."""
00196 
00197 
00198 class BuildError(RoutingException, LookupError):
00199     """Raised if the build system cannot find a URL for an endpoint with the
00200     values provided.
00201     """
00202 
00203     def __init__(self, endpoint, values, method):
00204         LookupError.__init__(self, endpoint, values, method)
00205         self.endpoint = endpoint
00206         self.values = values
00207         self.method = method
00208 
00209 
00210 class ValidationError(ValueError):
00211     """Validation error.  If a rule converter raises this exception the rule
00212     does not match the current URL and the next URL is tried.
00213     """
00214 
00215 
00216 class RuleFactory(object):
00217     """As soon as you have more complex URL setups it's a good idea to use rule
00218     factories to avoid repetitive tasks.  Some of them are builtin, others can
00219     be added by subclassing `RuleFactory` and overriding `get_rules`.
00220     """
00221 
00222     def get_rules(self, map):
00223         """Subclasses of `RuleFactory` have to override this method and return
00224         an iterable of rules."""
00225         raise NotImplementedError()
00226 
00227 
00228 class Subdomain(RuleFactory):
00229     """All URLs provided by this factory have the subdomain set to a
00230     specific domain. For example if you want to use the subdomain for
00231     the current language this can be a good setup::
00232 
00233         url_map = Map([
00234             Rule('/', endpoint='#select_language'),
00235             Subdomain('<string(length=2):lang_code>', [
00236                 Rule('/', endpoint='index'),
00237                 Rule('/about', endpoint='about'),
00238                 Rule('/help', endpoint='help')
00239             ])
00240         ])
00241 
00242     All the rules except for the ``'#select_language'`` endpoint will now
00243     listen on a two letter long subdomain that helds the language code
00244     for the current request.
00245     """
00246 
00247     def __init__(self, subdomain, rules):
00248         self.subdomain = subdomain
00249         self.rules = rules
00250 
00251     def get_rules(self, map):
00252         for rulefactory in self.rules:
00253             for rule in rulefactory.get_rules(map):
00254                 rule.subdomain = self.subdomain
00255                 yield rule
00256 
00257 
00258 class Submount(RuleFactory):
00259     """Like `Subdomain` but prefixes the URL rule with a given string::
00260 
00261         url_map = Map([
00262             Rule('/', endpoint='index'),
00263             Submount('/blog', [
00264                 Rule('/', endpoint='blog/index'),
00265                 Rule('/entry/<entry_slug>', endpoint='blog/show')
00266             ])
00267         ])
00268 
00269     Now the rule ``'blog/show'`` matches ``/blog/entry/<entry_slug>``.
00270     """
00271 
00272     def __init__(self, path, rules):
00273         self.path = path.rstrip('/')
00274         self.rules = rules
00275 
00276     def get_rules(self, map):
00277         for rulefactory in self.rules:
00278             for rule in rulefactory.get_rules(map):
00279                 rule.rule = self.path + rule.rule
00280                 yield rule
00281 
00282 
00283 class EndpointPrefix(RuleFactory):
00284     """Prefixes all endpoints (which must be strings for this factory) with
00285     another string. This can be useful for sub applications::
00286 
00287         url_map = Map([
00288             Rule('/', endpoint='index'),
00289             EndpointPrefix('blog/', [Submount('/blog', [
00290                 Rule('/', endpoint='index'),
00291                 Rule('/entry/<entry_slug>', endpoint='show')
00292             ])])
00293         ])
00294     """
00295 
00296     def __init__(self, prefix, rules):
00297         self.prefix = prefix
00298         self.rules = rules
00299 
00300     def get_rules(self, map):
00301         for rulefactory in self.rules:
00302             for rule in rulefactory.get_rules(map):
00303                 rule.endpoint = self.prefix + rule.endpoint
00304                 yield rule
00305 
00306 
00307 class RuleTemplate(object):
00308     """Returns copies of the rules wrapped and expands string templates in
00309     the endpoint, rule, defaults or subdomain sections.
00310 
00311     Here a small example for such a rule template::
00312 
00313         from werkzeug.routing import Map, Rule, RuleTemplate
00314 
00315         resource = RuleTemplate([
00316             Rule('/$name/', endpoint='$name.list'),
00317             Rule('/$name/<int:id>', endpoint='$name.show')
00318         ])
00319 
00320         url_map = Map([resource(name='user'), resource(name='page')])
00321 
00322     When a rule template is called the keyword arguments are used to
00323     replace the placeholders in all the string parameters.
00324     """
00325 
00326     def __init__(self, rules):
00327         self.rules = list(rules)
00328 
00329     def __call__(self, *args, **kwargs):
00330         return RuleTemplateFactory(self.rules, dict(*args, **kwargs))
00331 
00332 
00333 class RuleTemplateFactory(RuleFactory):
00334     """A factory that fills in template variables into rules.  Used by
00335     `RuleTemplate` internally.
00336 
00337     :internal:
00338     """
00339 
00340     def __init__(self, rules, context):
00341         self.rules = rules
00342         self.context = context
00343 
00344     def get_rules(self, map):
00345         for rulefactory in self.rules:
00346             for rule in rulefactory.get_rules(map):
00347                 new_defaults = subdomain = None
00348                 if rule.defaults is not None:
00349                     new_defaults = {}
00350                     for key, value in rule.defaults.iteritems():
00351                         if isinstance(value, basestring):
00352                             value = format_string(value, self.context)
00353                         new_defaults[key] = value
00354                 if rule.subdomain is not None:
00355                     subdomain = format_string(rule.subdomain, self.context)
00356                 new_endpoint = rule.endpoint
00357                 if isinstance(new_endpoint, basestring):
00358                     new_endpoint = format_string(new_endpoint, self.context)
00359                 yield Rule(
00360                     format_string(rule.rule, self.context),
00361                     new_defaults,
00362                     subdomain,
00363                     rule.methods,
00364                     rule.build_only,
00365                     new_endpoint,
00366                     rule.strict_slashes
00367                 )
00368 
00369 
00370 class Rule(RuleFactory):
00371     """A Rule represents one URL pattern.  There are some options for `Rule`
00372     that change the way it behaves and are passed to the `Rule` constructor.
00373     Note that besides the rule-string all arguments *must* be keyword arguments
00374     in order to not break the application on Werkzeug upgrades.
00375 
00376     `string`
00377         Rule strings basically are just normal URL paths with placeholders in
00378         the format ``<converter(arguments):name>`` where the converter and the
00379         arguments are optional.  If no converter is defined the `default`
00380         converter is used which means `string` in the normal configuration.
00381 
00382         URL rules that end with a slash are branch URLs, others are leaves.
00383         If you have `strict_slashes` enabled (which is the default), all
00384         branch URLs that are matched without a trailing slash will trigger a
00385         redirect to the same URL with the missing slash appended.
00386 
00387         The converters are defined on the `Map`.
00388 
00389     `endpoint`
00390         The endpoint for this rule. This can be anything. A reference to a
00391         function, a string, a number etc.  The preferred way is using a string
00392         as because the endpoint is used for URL generation.
00393 
00394     `defaults`
00395         An optional dict with defaults for other rules with the same endpoint.
00396         This is a bit tricky but useful if you want to have unique URLs::
00397 
00398             url_map = Map([
00399                 Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
00400                 Rule('/all/page/<int:page>', endpoint='all_entries')
00401             ])
00402 
00403         If a user now visits ``http://example.com/all/page/1`` he will be
00404         redirected to ``http://example.com/all/``.  If `redirect_defaults` is
00405         disabled on the `Map` instance this will only affect the URL
00406         generation.
00407 
00408     `subdomain`
00409         The subdomain rule string for this rule. If not specified the rule
00410         only matches for the `default_subdomain` of the map.  If the map is
00411         not bound to a subdomain this feature is disabled.
00412 
00413         Can be useful if you want to have user profiles on different subdomains
00414         and all subdomains are forwarded to your application::
00415 
00416             url_map = Map([
00417                 Rule('/', subdomain='<username>', endpoint='user/homepage'),
00418                 Rule('/stats', subdomain='<username>', endpoint='user/stats')
00419             ])
00420 
00421     `methods`
00422         A sequence of http methods this rule applies to.  If not specified, all
00423         methods are allowed. For example this can be useful if you want different
00424         endpoints for `POST` and `GET`.  If methods are defined and the path
00425         matches but the method matched against is not in this list or in the
00426         list of another rule for that path the error raised is of the type
00427         `MethodNotAllowed` rather than `NotFound`.
00428 
00429     `strict_slashes`
00430         Override the `Map` setting for `strict_slashes` only for this rule. If
00431         not specified the `Map` setting is used.
00432 
00433     `build_only`
00434         Set this to True and the rule will never match but will create a URL
00435         that can be build. This is useful if you have resources on a subdomain
00436         or folder that are not handled by the WSGI application (like static data)
00437 
00438     `redirect_to`
00439         If given this must be either a string or callable.  In case of a
00440         callable it's called with the url adapter that triggered the match and
00441         the values of the URL as keyword arguments and has to return the target
00442         for the redirect, otherwise it has to be a string with placeholders in
00443         rule syntax::
00444 
00445             def foo_with_slug(adapter, id):
00446                 # ask the database for the slug for the old id.  this of
00447                 # course has nothing to do with werkzeug.
00448                 return 'foo/' + Foo.get_slug_for_id(id)
00449 
00450             url_map = Map([
00451                 Rule('/foo/<slug>', endpoint='foo'),
00452                 Rule('/some/old/url/<slug>', redirect_to='foo/<slug>'),
00453                 Rule('/other/old/url/<int:id>', redirect_to=foo_with_slug)
00454             ])
00455 
00456         When the rule is matched the routing system will raise a
00457         `RequestRedirect` exception with the target for the redirect.
00458 
00459         Keep in mind that the URL will be joined against the URL root of the
00460         script so don't use a leading slash on the target URL unless you
00461         really mean root of that domain.
00462     """
00463 
00464     def __init__(self, string, defaults=None, subdomain=None, methods=None,
00465                  build_only=False, endpoint=None, strict_slashes=None,
00466                  redirect_to=None):
00467         if not string.startswith('/'):
00468             raise ValueError('urls must start with a leading slash')
00469         self.rule = string
00470         self.is_leaf = not string.endswith('/')
00471 
00472         self.map = None
00473         self.strict_slashes = strict_slashes
00474         self.subdomain = subdomain
00475         self.defaults = defaults
00476         self.build_only = build_only
00477         if methods is None:
00478             self.methods = None
00479         else:
00480             self.methods = set([x.upper() for x in methods])
00481         self.endpoint = endpoint
00482         self.greediness = 0
00483         self.redirect_to = redirect_to
00484 
00485         self._trace = []
00486         if defaults is not None:
00487             self.arguments = set(map(str, defaults))
00488         else:
00489             self.arguments = set()
00490         self._converters = {}
00491         self._regex = None
00492         self._weights = []
00493 
00494     def empty(self):
00495         """Return an unbound copy of this rule.  This can be useful if you
00496         want to reuse an already bound URL for another map."""
00497         return Rule(self.rule, self.defaults, self.subdomain, self.methods,
00498                     self.build_only, self.endpoint, self.strict_slashes,
00499                     self.redirect_to)
00500 
00501     def get_rules(self, map):
00502         yield self
00503 
00504     def bind(self, map):
00505         """Bind the url to a map and create a regular expression based on
00506         the information from the rule itself and the defaults from the map.
00507 
00508         :internal:
00509         """
00510         if self.map is not None:
00511             raise RuntimeError('url rule %r already bound to map %r' %
00512                                (self, self.map))
00513         self.map = map
00514         if self.strict_slashes is None:
00515             self.strict_slashes = map.strict_slashes
00516         if self.subdomain is None:
00517             self.subdomain = map.default_subdomain
00518 
00519         rule = self.subdomain + '|' + (self.is_leaf and self.rule
00520                                        or self.rule.rstrip('/'))
00521 
00522         regex_parts = []
00523         for converter, arguments, variable in parse_rule(rule):
00524             if converter is None:
00525                 regex_parts.append(re.escape(variable))
00526                 self._trace.append((False, variable))
00527                 self._weights.append(len(variable))
00528             else:
00529                 convobj = get_converter(map, converter, arguments)
00530                 regex_parts.append('(?P<%s>%s)' % (variable, convobj.regex))
00531                 self._converters[variable] = convobj
00532                 self._trace.append((True, variable))
00533                 self._weights.append(convobj.weight)
00534                 self.arguments.add(str(variable))
00535                 if convobj.is_greedy:
00536                     self.greediness += 1
00537         if not self.is_leaf:
00538             self._trace.append((False, '/'))
00539 
00540         if not self.build_only:
00541             regex = r'^%s%s$' % (
00542                 u''.join(regex_parts),
00543                 (not self.is_leaf or not self.strict_slashes) and \
00544                     '(?<!/)(?P<__suffix__>/?)' or ''
00545             )
00546             self._regex = re.compile(regex, re.UNICODE)
00547 
00548     def match(self, path):
00549         """Check if the rule matches a given path. Path is a string in the
00550         form ``"subdomain|/path(method)"`` and is assembled by the map.
00551 
00552         If the rule matches a dict with the converted values is returned,
00553         otherwise the return value is `None`.
00554 
00555         :internal:
00556         """
00557         if not self.build_only:
00558             m = self._regex.search(path)
00559             if m is not None:
00560                 groups = m.groupdict()
00561                 # we have a folder like part of the url without a trailing
00562                 # slash and strict slashes enabled. raise an exception that
00563                 # tells the map to redirect to the same url but with a
00564                 # trailing slash
00565                 if self.strict_slashes and not self.is_leaf and \
00566                    not groups.pop('__suffix__'):
00567                     raise RequestSlash()
00568                 # if we are not in strict slashes mode we have to remove
00569                 # a __suffix__
00570                 elif not self.strict_slashes:
00571                     del groups['__suffix__']
00572 
00573                 result = {}
00574                 for name, value in groups.iteritems():
00575                     try:
00576                         value = self._converters[name].to_python(value)
00577                     except ValidationError:
00578                         return
00579                     result[str(name)] = value
00580                 if self.defaults is not None:
00581                     result.update(self.defaults)
00582                 return result
00583 
00584     def build(self, values):
00585         """Assembles the relative url for that rule and the subdomain.
00586         If building doesn't work for some reasons `None` is returned.
00587 
00588         :internal:
00589         """
00590         tmp = []
00591         add = tmp.append
00592         processed = set(self.arguments)
00593         for is_dynamic, data in self._trace:
00594             if is_dynamic:
00595                 try:
00596                     add(self._converters[data].to_url(values[data]))
00597                 except ValidationError:
00598                     return
00599                 processed.add(data)
00600             else:
00601                 add(data)
00602         subdomain, url = (u''.join(tmp)).split('|', 1)
00603 
00604         query_vars = {}
00605         for key in set(values) - processed:
00606             query_vars[key] = unicode(values[key])
00607         if query_vars:
00608             url += '?' + url_encode(query_vars, self.map.charset,
00609                                     sort=self.map.sort_parameters,
00610                                     key=self.map.sort_key)
00611 
00612         return subdomain, url
00613 
00614     def provides_defaults_for(self, rule):
00615         """Check if this rule has defaults for a given rule.
00616 
00617         :internal:
00618         """
00619         return not self.build_only and self.defaults is not None and \
00620                self.endpoint == rule.endpoint and self != rule and \
00621                self.arguments == rule.arguments
00622 
00623     def suitable_for(self, values, method):
00624         """Check if the dict of values has enough data for url generation.
00625 
00626         :internal:
00627         """
00628         if self.methods is not None and method not in self.methods:
00629             return False
00630 
00631         valueset = set(values)
00632 
00633         for key in self.arguments - set(self.defaults or ()):
00634             if key not in values:
00635                 return False
00636 
00637         if self.arguments.issubset(valueset):
00638             if self.defaults is None:
00639                 return True
00640             for key, value in self.defaults.iteritems():
00641                 if value != values[key]:
00642                     return False
00643 
00644         return True
00645 
00646     def match_compare(self, other):
00647         """Compare this object with another one for matching.
00648 
00649         :internal:
00650         """
00651         for sw, ow in izip(self._weights, other._weights):
00652             if sw > ow:
00653                 return -1
00654             elif sw < ow:
00655                 return 1
00656         if len(self._weights) > len(other._weights):
00657             return -1
00658         if len(self._weights) < len(other._weights):
00659             return 1
00660         if not other.arguments and self.arguments:
00661             return 1
00662         elif other.arguments and not self.arguments:
00663             return -1
00664         elif other.defaults is None and self.defaults is not None:
00665             return 1
00666         elif other.defaults is not None and self.defaults is None:
00667             return -1
00668         elif self.greediness > other.greediness:
00669             return -1
00670         elif self.greediness < other.greediness:
00671             return 1
00672         elif len(self.arguments) > len(other.arguments):
00673             return 1
00674         elif len(self.arguments) < len(other.arguments):
00675             return -1
00676         return 1
00677 
00678     def build_compare(self, other):
00679         """Compare this object with another one for building.
00680 
00681         :internal:
00682         """
00683         if not other.arguments and self.arguments:
00684             return -1
00685         elif other.arguments and not self.arguments:
00686             return 1
00687         elif other.defaults is None and self.defaults is not None:
00688             return -1
00689         elif other.defaults is not None and self.defaults is None:
00690             return 1
00691         elif self.provides_defaults_for(other):
00692             return -1
00693         elif other.provides_defaults_for(self):
00694             return 1
00695         elif self.greediness > other.greediness:
00696             return -1
00697         elif self.greediness < other.greediness:
00698             return 1
00699         elif len(self.arguments) > len(other.arguments):
00700             return -1
00701         elif len(self.arguments) < len(other.arguments):
00702             return 1
00703         return -1
00704 
00705     def __eq__(self, other):
00706         return self.__class__ is other.__class__ and \
00707                self._trace == other._trace
00708 
00709     def __ne__(self, other):
00710         return not self.__eq__(other)
00711 
00712     def __unicode__(self):
00713         return self.rule
00714 
00715     def __str__(self):
00716         charset = self.map is not None and self.map.charset or 'utf-8'
00717         return unicode(self).encode(charset)
00718 
00719     def __repr__(self):
00720         if self.map is None:
00721             return '<%s (unbound)>' % self.__class__.__name__
00722         charset = self.map is not None and self.map.charset or 'utf-8'
00723         tmp = []
00724         for is_dynamic, data in self._trace:
00725             if is_dynamic:
00726                 tmp.append('<%s>' % data)
00727             else:
00728                 tmp.append(data)
00729         return '<%s %r%s -> %s>' % (
00730             self.__class__.__name__,
00731             (u''.join(tmp).encode(charset)).lstrip('|'),
00732             self.methods is not None and ' (%s)' % \
00733                 ', '.join(self.methods) or '',
00734             self.endpoint
00735         )
00736 
00737 
00738 class BaseConverter(object):
00739     """Base class for all converters."""
00740     regex = '[^/]+'
00741     is_greedy = False
00742     weight = 100
00743 
00744     def __init__(self, map):
00745         self.map = map
00746 
00747     def to_python(self, value):
00748         return value
00749 
00750     def to_url(self, value):
00751         return url_quote(value, self.map.charset)
00752 
00753 
00754 class UnicodeConverter(BaseConverter):
00755     """This converter is the default converter and accepts any string but
00756     only one one path segment.  Thus the string can not include a slash.
00757 
00758     This is the default validator.
00759 
00760     Example::
00761 
00762         Rule('/pages/<page>'),
00763         Rule('/<string(length=2):lang_code>')
00764 
00765     :param map: the :class:`Map`.
00766     :param minlength: the minimum length of the string.  Must be greater
00767                       or equal 1.
00768     :param maxlength: the maximum length of the string.
00769     :param length: the exact length of the string.
00770     """
00771 
00772     def __init__(self, map, minlength=1, maxlength=None, length=None):
00773         BaseConverter.__init__(self, map)
00774         if length is not None:
00775             length = '{%d}' % int(length)
00776         else:
00777             if maxlength is None:
00778                 maxlength = ''
00779             else:
00780                 maxlength = int(maxlength)
00781             length = '{%s,%s}' % (
00782                 int(minlength),
00783                 maxlength
00784             )
00785         self.regex = '[^/]' + length
00786 
00787 
00788 class AnyConverter(BaseConverter):
00789     """Matches one of the items provided.  Items can either be Python
00790     identifiers or unicode strings::
00791 
00792         Rule('/<any(about, help, imprint, u"class"):page_name>')
00793 
00794     :param map: the :class:`Map`.
00795     :param items: this function accepts the possible items as positional
00796                   arguments.
00797     """
00798 
00799     def __init__(self, map, *items):
00800         BaseConverter.__init__(self, map)
00801         self.regex = '(?:%s)' % '|'.join([re.escape(x) for x in items])
00802 
00803 
00804 class PathConverter(BaseConverter):
00805     """Like the default :class:`UnicodeConverter`, but it also matches
00806     slashes.  This is useful for wikis and similar applications::
00807 
00808         Rule('/<path:wikipage>')
00809         Rule('/<path:wikipage>/edit')
00810 
00811     :param map: the :class:`Map`.
00812     :param minlength: the minimum length of the string.  Must be greater
00813                       or equal 1.
00814     :param maxlength: the maximum length of the string.
00815     :param length: the exact length of the string.
00816     """
00817     regex = '[^/].*?'
00818     is_greedy = True
00819     weight = 50
00820 
00821 
00822 class NumberConverter(BaseConverter):
00823     """Baseclass for `IntegerConverter` and `FloatConverter`.
00824 
00825     :internal:
00826     """
00827 
00828     def __init__(self, map, fixed_digits=0, min=None, max=None):
00829         BaseConverter.__init__(self, map)
00830         self.fixed_digits = fixed_digits
00831         self.min = min
00832         self.max = max
00833 
00834     def to_python(self, value):
00835         if (self.fixed_digits and len(value) != self.fixed_digits):
00836             raise ValidationError()
00837         value = self.num_convert(value)
00838         if (self.min is not None and value < self.min) or \
00839            (self.max is not None and value > self.max):
00840             raise ValidationError()
00841         return value
00842 
00843     def to_url(self, value):
00844         value = self.num_convert(value)
00845         if self.fixed_digits:
00846             value = ('%%0%sd' % self.fixed_digits) % value
00847         return str(value)
00848 
00849 
00850 class IntegerConverter(NumberConverter):
00851     """This converter only accepts integer values::
00852 
00853         Rule('/page/<int:page>')
00854 
00855     This converter does not support negative values.
00856 
00857     :param map: the :class:`Map`.
00858     :param fixed_digits: the number of fixed digits in the URL.  If you set
00859                          this to ``4`` for example, the application will
00860                          only match if the url looks like ``/0001/``.  The
00861                          default is variable length.
00862     :param min: the minimal value.
00863     :param max: the maximal value.
00864     """
00865     regex = r'\d+'
00866     num_convert = int
00867 
00868 
00869 class FloatConverter(NumberConverter):
00870     """This converter only accepts floating point values::
00871 
00872         Rule('/probability/<float:probability>')
00873 
00874     This converter does not support negative values.
00875 
00876     :param map: the :class:`Map`.
00877     :param min: the minimal value.
00878     :param max: the maximal value.
00879     """
00880     regex = r'\d+\.\d+'
00881     num_convert = float
00882 
00883     def __init__(self, map, min=None, max=None):
00884         NumberConverter.__init__(self, map, 0, min, max)
00885 
00886 
00887 class Map(object):
00888     """The map class stores all the URL rules and some configuration
00889     parameters.  Some of the configuration values are only stored on the
00890     `Map` instance since those affect all rules, others are just defaults
00891     and can be overridden for each rule.  Note that you have to specify all
00892     arguments besides the `rules` as keywords arguments!
00893 
00894     :param rules: sequence of url rules for this map.
00895     :param default_subdomain: The default subdomain for rules without a
00896                               subdomain defined.
00897     :param charset: charset of the url. defaults to ``"utf-8"``
00898     :param strict_slashes: Take care of trailing slashes.
00899     :param redirect_defaults: This will redirect to the default rule if it
00900                               wasn't visited that way. This helps creating
00901                               unique URLs.
00902     :param converters: A dict of converters that adds additional converters
00903                        to the list of converters. If you redefine one
00904                        converter this will override the original one.
00905     :param sort_parameters: If set to `True` the url parameters are sorted.
00906                             See `url_encode` for more details.
00907     :param sort_key: The sort key function for `url_encode`.
00908 
00909     .. versionadded:: 0.5
00910         `sort_parameters` and `sort_key` was added.
00911     """
00912 
00913     def __init__(self, rules=None, default_subdomain='', charset='utf-8',
00914                  strict_slashes=True, redirect_defaults=True,
00915                  converters=None, sort_parameters=False, sort_key=None):
00916         self._rules = []
00917         self._rules_by_endpoint = {}
00918         self._remap = True
00919 
00920         self.default_subdomain = default_subdomain
00921         self.charset = charset
00922         self.strict_slashes = strict_slashes
00923         self.redirect_defaults = redirect_defaults
00924 
00925         self.converters = DEFAULT_CONVERTERS.copy()
00926         if converters:
00927             self.converters.update(converters)
00928 
00929         self.sort_parameters = sort_parameters
00930         self.sort_key = sort_key
00931 
00932         for rulefactory in rules or ():
00933             self.add(rulefactory)
00934 
00935     def is_endpoint_expecting(self, endpoint, *arguments):
00936         """Iterate over all rules and check if the endpoint expects
00937         the arguments provided.  This is for example useful if you have
00938         some URLs that expect a language code and others that do not and
00939         you want to wrap the builder a bit so that the current language
00940         code is automatically added if not provided but endpoints expect
00941         it.
00942 
00943         :param endpoint: the endpoint to check.
00944         :param arguments: this function accepts one or more arguments
00945                           as positional arguments.  Each one of them is
00946                           checked.
00947         """
00948         self.update()
00949         arguments = set(arguments)
00950         for rule in self._rules_by_endpoint[endpoint]:
00951             if arguments.issubset(rule.arguments):
00952                 return True
00953         return False
00954 
00955     def iter_rules(self, endpoint=None):
00956         """Iterate over all rules or the rules of an endpoint.
00957 
00958         :param endpoint: if provided only the rules for that endpoint
00959                          are returned.
00960         :return: an iterator
00961         """
00962         if endpoint is not None:
00963             return iter(self._rules_by_endpoint[endpoint])
00964         return iter(self._rules)
00965 
00966     def add(self, rulefactory):
00967         """Add a new rule or factory to the map and bind it.  Requires that the
00968         rule is not bound to another map.
00969 
00970         :param rulefactory: a :class:`Rule` or :class:`RuleFactory`
00971         """
00972         for rule in rulefactory.get_rules(self):
00973             rule.bind(self)
00974             self._rules.append(rule)
00975             self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
00976         self._remap = True
00977 
00978     def bind(self, server_name, script_name=None, subdomain=None,
00979              url_scheme='http', default_method='GET', path_info=None):
00980         """Return a new :class:`MapAdapter` with the details specified to the
00981         call.  Note that `script_name` will default to ``'/'`` if not further
00982         specified or `None`.  The `server_name` at least is a requirement
00983         because the HTTP RFC requires absolute URLs for redirects and so all
00984         redirect exceptions raised by Werkzeug will contain the full canonical
00985         URL.
00986 
00987         If no path_info is passed to :meth:`match` it will use the default path
00988         info passed to bind.  While this doesn't really make sense for
00989         manual bind calls, it's useful if you bind a map to a WSGI
00990         environment which already contains the path info.
00991 
00992         `subdomain` will default to the `default_subdomain` for this map if
00993         no defined. If there is no `default_subdomain` you cannot use the
00994         subdomain feature.
00995         """
00996         if subdomain is None:
00997             subdomain = self.default_subdomain
00998         if script_name is None:
00999             script_name = '/'
01000         return MapAdapter(self, server_name, script_name, subdomain,
01001                           url_scheme, path_info, default_method)
01002 
01003     def bind_to_environ(self, environ, server_name=None, subdomain=None):
01004         """Like :meth:`bind` but you can pass it an WSGI environment and it
01005         will fetch the information from that directory.  Note that because of
01006         limitations in the protocol there is no way to get the current
01007         subdomain and real `server_name` from the environment.  If you don't
01008         provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or
01009         `HTTP_HOST` if provided) as used `server_name` with disabled subdomain
01010         feature.
01011 
01012         If `subdomain` is `None` but an environment and a server name is
01013         provided it will calculate the current subdomain automatically.
01014         Example: `server_name` is ``'example.com'`` and the `SERVER_NAME`
01015         in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated
01016         subdomain will be ``'staging.dev'``.
01017 
01018         If the object passed as environ has an environ attribute, the value of
01019         this attribute is used instead.  This allows you to pass request
01020         objects.  Additionally `PATH_INFO` added as a default ot the
01021         :class:`MapAdapter` so that you don't have to pass the path info to
01022         the match method.
01023 
01024         .. versionchanged:: 0.5
01025             previously this method accepted a bogus `calculate_subdomain`
01026             parameter that did not have any effect.  It was removed because
01027             of that.
01028 
01029         :param environ: a WSGI environment.
01030         :param server_name: an optional server name hint (see above).
01031         :param subdomain: optionally the current subdomain (see above).
01032         """
01033         if hasattr(environ, 'environ'):
01034             environ = environ.environ
01035         if server_name is None:
01036             if 'HTTP_HOST' in environ:
01037                 server_name = environ['HTTP_HOST']
01038             else:
01039                 server_name = environ['SERVER_NAME']
01040                 if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
01041                    in (('https', '443'), ('http', '80')):
01042                     server_name += ':' + environ['SERVER_PORT']
01043         elif subdomain is None:
01044             cur_server_name = environ.get('HTTP_HOST',
01045                 environ['SERVER_NAME']).split(':', 1)[0].split('.')
01046             real_server_name = server_name.split(':', 1)[0].split('.')
01047             offset = -len(real_server_name)
01048             if cur_server_name[offset:] != real_server_name:
01049                 raise ValueError('the server name provided (%r) does not '
01050                                  'match the server name from the WSGI '
01051                                  'environment (%r)' %
01052                                  (environ['SERVER_NAME'], server_name))
01053             subdomain = '.'.join(filter(None, cur_server_name[:offset]))
01054         return Map.bind(self, server_name, environ.get('SCRIPT_NAME'),
01055                         subdomain, environ['wsgi.url_scheme'],
01056                         environ['REQUEST_METHOD'], environ.get('PATH_INFO'))
01057 
01058     def update(self):
01059         """Called before matching and building to keep the compiled rules
01060         in the correct order after things changed.
01061         """
01062         if self._remap:
01063             self._rules.sort(lambda a, b: a.match_compare(b))
01064             for rules in self._rules_by_endpoint.itervalues():
01065                 rules.sort(lambda a, b: a.build_compare(b))
01066             self._remap = False
01067 
01068 
01069 class MapAdapter(object):
01070     """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does
01071     the URL matching and building based on runtime information.
01072     """
01073 
01074     def __init__(self, map, server_name, script_name, subdomain,
01075                  url_scheme, path_info, default_method):
01076         self.map = map
01077         self.server_name = server_name
01078         if not script_name.endswith('/'):
01079             script_name += '/'
01080         self.script_name = script_name
01081         self.subdomain = subdomain
01082         self.url_scheme = url_scheme
01083         self.path_info = path_info or u''
01084         self.default_method = default_method
01085 
01086     def dispatch(self, view_func, path_info=None, method=None,
01087                  catch_http_exceptions=False):
01088         """Does the complete dispatching process.  `view_func` is called with
01089         the endpoint and a dict with the values for the view.  It should
01090         look up the view function, call it, and return a response object
01091         or WSGI application.  http exceptions are not catched by default
01092         so that applications can display nicer error messages by just
01093         catching them by hand.  If you want to stick with the default
01094         error messages you can pass it ``catch_http_exceptions=True`` and
01095         it will catch the http exceptions.
01096 
01097         Here a small example for the dispatch usage::
01098 
01099             from werkzeug import Request, Response, responder
01100             from werkzeug.routing import Map, Rule
01101 
01102             def on_index(request):
01103                 return Response('Hello from the index')
01104 
01105             url_map = Map([Rule('/', endpoint='index')])
01106             views = {'index': on_index}
01107 
01108             @responder
01109             def application(environ, start_response):
01110                 request = Request(environ)
01111                 urls = url_map.bind_to_environ(environ)
01112                 return urls.dispatch(lambda e, v: views[e](request, **v),
01113                                      catch_http_exceptions=True)
01114 
01115         Keep in mind that this method might return exception objects, too, so
01116         use :class:`Response.force_type` to get a response object.
01117 
01118         :param view_func: a function that is called with the endpoint as
01119                           first argument and the value dict as second.  Has
01120                           to dispatch to the actual view function with this
01121                           information.  (see above)
01122         :param path_info: the path info to use for matching.  Overrides the
01123                           path info specified on binding.
01124         :param method: the HTTP method used for matching.  Overrides the
01125                        method specified on binding.
01126         :param catch_http_exceptions: set to `True` to catch any of the
01127                                       werkzeug :class:`HTTPException`\s.
01128         """
01129         try:
01130             try:
01131                 endpoint, args = self.match(path_info, method)
01132             except RequestRedirect, e:
01133                 return e
01134             return view_func(endpoint, args)
01135         except HTTPException, e:
01136             if catch_http_exceptions:
01137                 return e
01138             raise
01139 
01140     def match(self, path_info=None, method=None):
01141         """The usage is simple: you just pass the match method the current
01142         path info as well as the method (which defaults to `GET`).  The
01143         following things can then happen:
01144 
01145         - you receive a `NotFound` exception that indicates that no URL is
01146           matching.  A `NotFound` exception is also a WSGI application you
01147           can call to get a default page not found page (happens to be the
01148           same object as `werkzeug.exceptions.NotFound`)
01149 
01150         - you receive a `MethodNotAllowed` exception that indicates that there
01151           is a match for this URL but non for the current request method.
01152           This is useful for RESTful applications.
01153 
01154         - you receive a `RequestRedirect` exception with a `new_url`
01155           attribute.  This exception is used to notify you about a request
01156           Werkzeug requests by your WSGI application.  This is for example the
01157           case if you request ``/foo`` although the correct URL is ``/foo/``
01158           You can use the `RequestRedirect` instance as response-like object
01159           similar to all other subclasses of `HTTPException`.
01160 
01161         - you get a tuple in the form ``(endpoint, arguments)`` when there is
01162           a match.
01163 
01164         If the path info is not passed to the match method the default path
01165         info of the map is used (defaults to the root URL if not defined
01166         explicitly).
01167 
01168         All of the exceptions raised are subclasses of `HTTPException` so they
01169         can be used as WSGI responses.  The will all render generic error or
01170         redirect pages.
01171 
01172         Here is a small example for matching:
01173 
01174         >>> m = Map([
01175         ...     Rule('/', endpoint='index'),
01176         ...     Rule('/downloads/', endpoint='downloads/index'), 
01177         ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
01178         ... ])
01179         >>> urls = m.bind("example.com", "/")
01180         >>> urls.match("/", "GET")
01181         ('index', {})
01182         >>> urls.match("/downloads/42")
01183         ('downloads/show', {'id': 42})
01184 
01185         And here is what happens on redirect and missing URLs:
01186 
01187         >>> urls.match("/downloads")
01188         Traceback (most recent call last):
01189           ...
01190         RequestRedirect: http://example.com/downloads/
01191         >>> urls.match("/missing")
01192         Traceback (most recent call last):
01193           ...
01194         NotFound: 404 Not Found
01195 
01196         :param path_info: the path info to use for matching.  Overrides the
01197                           path info specified on binding.
01198         :param method: the HTTP method used for matching.  Overrides the
01199                        method specified on binding.
01200         """
01201         self.map.update()
01202         if path_info is None:
01203             path_info = self.path_info
01204         if not isinstance(path_info, unicode):
01205             path_info = path_info.decode(self.map.charset, 'ignore')
01206         method = (method or self.default_method).upper()
01207         path = u'%s|/%s' % (self.subdomain, path_info.lstrip('/'))
01208         have_match_for = set()
01209         for rule in self.map._rules:
01210             try:
01211                 rv = rule.match(path)
01212             except RequestSlash:
01213                 raise RequestRedirect(str('%s://%s%s%s/%s/' % (
01214                     self.url_scheme,
01215                     self.subdomain and self.subdomain + '.' or '',
01216                     self.server_name,
01217                     self.script_name[:-1],
01218                     url_quote(path_info.lstrip('/'), self.map.charset)
01219                 )))
01220             if rv is None:
01221                 continue
01222             if rule.methods is not None and method not in rule.methods:
01223                 have_match_for.update(rule.methods)
01224                 continue
01225             if self.map.redirect_defaults:
01226                 for r in self.map._rules_by_endpoint[rule.endpoint]:
01227                     if r.provides_defaults_for(rule) and \
01228                        r.suitable_for(rv, method):
01229                         rv.update(r.defaults)
01230                         subdomain, path = r.build(rv)
01231                         raise RequestRedirect(str('%s://%s%s%s/%s' % (
01232                             self.url_scheme,
01233                             subdomain and subdomain + '.' or '',
01234                             self.server_name,
01235                             self.script_name[:-1],
01236                             url_quote(path.lstrip('/'), self.map.charset)
01237                         )))
01238             if rule.redirect_to is not None:
01239                 if isinstance(rule.redirect_to, basestring):
01240                     def _handle_match(match):
01241                         value = rv[match.group(1)]
01242                         return rule._converters[match.group(1)].to_url(value)
01243                     redirect_url = _simple_rule_re.sub(_handle_match,
01244                                                        rule.redirect_to)
01245                 else:
01246                     redirect_url = rule.redirect_to(self, **rv)
01247                 raise RequestRedirect(str(urljoin('%s://%s%s%s' % (
01248                     self.url_scheme,
01249                     self.subdomain and self.subdomain + '.' or '',
01250                     self.server_name,
01251                     self.script_name
01252                 ), redirect_url)))
01253             return rule.endpoint, rv
01254         if have_match_for:
01255             raise MethodNotAllowed(valid_methods=list(have_match_for))
01256         raise NotFound()
01257 
01258     def test(self, path_info=None, method=None):
01259         """Test if a rule would match.  Works like `match` but returns `True`
01260         if the URL matches, or `False` if it does not exist.
01261 
01262         :param path_info: the path info to use for matching.  Overrides the
01263                           path info specified on binding.
01264         :param method: the HTTP method used for matching.  Overrides the
01265                        method specified on binding.
01266         """
01267         try:
01268             self.match(path_info, method)
01269         except RequestRedirect:
01270             pass
01271         except NotFound:
01272             return False
01273         return True
01274 
01275     def build(self, endpoint, values=None, method=None, force_external=False):
01276         """Building URLs works pretty much the other way round.  Instead of
01277         `match` you call `build` and pass it the endpoint and a dict of
01278         arguments for the placeholders.
01279 
01280         The `build` function also accepts an argument called `force_external`
01281         which, if you set it to `True` will force external URLs. Per default
01282         external URLs (include the server name) will only be used if the
01283         target URL is on a different subdomain.
01284 
01285         >>> m = Map([
01286         ...     Rule('/', endpoint='index'),
01287         ...     Rule('/downloads/', endpoint='downloads/index'), 
01288         ...     Rule('/downloads/<int:id>', endpoint='downloads/show')
01289         ... ])
01290         >>> urls = m.bind("example.com", "/")
01291         >>> urls.build("index", {})
01292         '/'
01293         >>> urls.build("downloads/show", {'id': 42})
01294         '/downloads/42'
01295         >>> urls.build("downloads/show", {'id': 42}, force_external=True)
01296         'http://example.com/downloads/42'
01297 
01298         Because URLs cannot contain non ASCII data you will always get
01299         bytestrings back.  Non ASCII characters are urlencoded with the
01300         charset defined on the map instance.
01301 
01302         Additional values are converted to unicode and appended to the URL as
01303         URL querystring parameters:
01304 
01305         >>> urls.build("index", {'q': 'My Searchstring'})
01306         '/?q=My+Searchstring'
01307 
01308         If a rule does not exist when building a `BuildError` exception is
01309         raised.
01310 
01311         The build method accepts an argument called `method` which allows you
01312         to specify the method you want to have an URL built for if you have
01313         different methods for the same endpoint specified.
01314 
01315         :param endpoint: the endpoint of the URL to build.
01316         :param values: the values for the URL to build.  Unhandled values are
01317                        appended to the URL as query parameters.
01318         :param method: the HTTP method for the rule if there are different
01319                        URLs for different methods on the same endpoint.
01320         :param force_external: enforce full canonical external URLs.
01321         """
01322         self.map.update()
01323         method = method or self.default_method
01324         if values:
01325             values = dict([(k, v) for k, v in values.items() if v is not None])
01326         else:
01327             values = {}
01328 
01329         for rule in self.map._rules_by_endpoint.get(endpoint, ()):
01330             if rule.suitable_for(values, method):
01331                 rv = rule.build(values)
01332                 if rv is not None:
01333                     break
01334         else:
01335             raise BuildError(endpoint, values, method)
01336         subdomain, path = rv
01337         if not force_external and subdomain == self.subdomain:
01338             return str(urljoin(self.script_name, path.lstrip('/')))
01339         return str('%s://%s%s%s/%s' % (
01340             self.url_scheme,
01341             subdomain and subdomain + '.' or '',
01342             self.server_name,
01343             self.script_name[:-1],
01344             path.lstrip('/')
01345         ))
01346 
01347 
01348 #: the default converter mapping for the map.
01349 DEFAULT_CONVERTERS = {
01350     'default':          UnicodeConverter,
01351     'string':           UnicodeConverter,
01352     'any':              AnyConverter,
01353     'path':             PathConverter,
01354     'int':              IntegerConverter,
01355     'float':            FloatConverter
01356 }