def generate_non_minimized(self, kargs):
        """Generate a non-minimal version of the URL"""
        # Iterate through the keys that are defaults, and NOT in the route
        # path. If its not in kargs, or doesn't match, or is None, this
        # route won't work
        for k in self.maxkeys - self.minkeys:
            if k not in kargs:
                return False
            elif self.make_unicode(kargs[k]) != \
                self.make_unicode(self.defaults[k]):
                return False

        # Ensure that all the args in the route path are present and not None
        for arg in self.minkeys:
            if arg not in kargs or kargs[arg] is None:
                if arg in self.dotkeys:
                    kargs[arg] = ''
                else:
                    return False

        # Encode all the argument that the regpath can use
        for k in kargs:
            if k in self.maxkeys:
                if k in self.dotkeys:
                    if kargs[k]:
                        kargs[k] = url_quote(
                            '.' + as_unicode(kargs[k], self.encoding),
                            self.encoding)
                else:
                    kargs[k] = url_quote(as_unicode(kargs[k], self.encoding),
                                         self.encoding)

        return self.regpath % kargs
Beispiel #2
0
    def generate(self, _ignore_req_list=False, _append_slash=False, **kargs):
        """Generate a URL from ourself given a set of keyword arguments

        Toss an exception if this
        set of keywords would cause a gap in the url.

        """
        # Verify that our args pass any regexp requirements
        if not _ignore_req_list:
            for key in self.reqs.keys():
                val = kargs.get(key)
                if val and not self.req_regs[key].match(self.make_unicode(val)):
                    return False

        # Verify that if we have a method arg, its in the method accept list.
        # Also, method will be changed to _method for route generation
        meth = as_unicode(kargs.get('method'), self.encoding)
        if meth:
            if self.conditions and 'method' in self.conditions \
                    and meth.upper() not in self.conditions['method']:
                return False
            kargs.pop('method')

        if self.minimization:
            url = self.generate_minimized(kargs)
        else:
            url = self.generate_non_minimized(kargs)

        if url is False:
            return url

        if not url.startswith('/') and not self.static:
            url = '/' + url
        extras = frozenset(kargs.keys()) - self.maxkeys
        if extras:
            if _append_slash and not url.endswith('/'):
                url += '/'
            fragments = []
            # don't assume the 'extras' set preserves order: iterate
            # through the ordered kargs instead
            for key in kargs:
                if key not in extras:
                    continue
                if key == 'action' or key == 'controller':
                    continue
                val = kargs[key]
                if isinstance(val, (tuple, list)):
                    for value in val:
                        value = as_unicode(value, self.encoding)
                        fragments.append((key, _str_encode(value,
                                                           self.encoding)))
                else:
                    val = as_unicode(val, self.encoding)
                    fragments.append((key, _str_encode(val, self.encoding)))
            if fragments:
                url += '?'
                url += urlparse.urlencode(fragments)
        elif _append_slash and not url.endswith('/'):
            url += '/'
        return url
Beispiel #3
0
    def generate_non_minimized(self, kargs):
        """Generate a non-minimal version of the URL"""
        # Iterate through the keys that are defaults, and NOT in the route
        # path. If its not in kargs, or doesn't match, or is None, this
        # route won't work
        for k in self.maxkeys - self.minkeys:
            if k not in kargs:
                return False
            elif self.make_unicode(kargs[k]) != \
                    self.make_unicode(self.defaults[k]):
                return False

        # Ensure that all the args in the route path are present and not None
        for arg in self.minkeys:
            if arg not in kargs or kargs[arg] is None:
                if arg in self.dotkeys:
                    kargs[arg] = ''
                else:
                    return False

        # Encode all the argument that the regpath can use
        for k in kargs:
            if k in self.maxkeys:
                if k in self.dotkeys:
                    if kargs[k]:
                        kargs[k] = url_quote('.' + as_unicode(kargs[k],
                                             self.encoding), self.encoding)
                else:
                    kargs[k] = url_quote(as_unicode(kargs[k], self.encoding),
                                         self.encoding)

        return self.regpath % kargs
Beispiel #4
0
    def generate(self, *args, **kargs):
        """Generate a route from a set of keywords

        Returns the url text, or None if no URL could be generated.

        .. code-block:: python

            m.generate(controller='content',action='view',id=10)

        """
        # Generate ourself if we haven't already
        if not self._created_gens:
            self._create_gens()

        if self.append_slash:
            kargs['_append_slash'] = True

        if not self.explicit:
            if 'controller' not in kargs:
                kargs['controller'] = 'content'
            if 'action' not in kargs:
                kargs['action'] = 'index'

        environ = kargs.pop('_environ', self.environ) or {}
        if 'SCRIPT_NAME' in environ:
            script_name = environ['SCRIPT_NAME']
        elif self.environ and 'SCRIPT_NAME' in self.environ:
            script_name = self.environ['SCRIPT_NAME']
        else:
            script_name = ""
        controller = kargs.get('controller', None)
        action = kargs.get('action', None)

        # If the URL didn't depend on the SCRIPT_NAME, we'll cache it
        # keyed by just by kargs; otherwise we need to cache it with
        # both SCRIPT_NAME and kargs:
        cache_key = six.text_type(args).encode('utf8') + \
            six.text_type(kargs).encode('utf8')

        if self.urlcache is not None:
            if six.PY3:
                cache_key_script_name = b':'.join((script_name.encode('utf-8'),
                                                   cache_key))
            else:
                cache_key_script_name = '%s:%s' % (script_name, cache_key)

            # Check the url cache to see if it exists, use it if it does
            val = self.urlcache.get(cache_key_script_name, self)
            if val != self:
                return val

        controller = as_unicode(controller, self.encoding)
        action = as_unicode(action, self.encoding)

        actionlist = self._gendict.get(controller) or self._gendict.get('*', {})
        if not actionlist and not args:
            return None
        (keylist, sortcache) = actionlist.get(action) or \
            actionlist.get('*', (None, {}))
        if not keylist and not args:
            return None

        keys = frozenset(kargs.keys())
        cacheset = False
        cachekey = six.text_type(keys)
        cachelist = sortcache.get(cachekey)
        if args:
            keylist = args
        elif cachelist:
            keylist = cachelist
        else:
            cacheset = True
            newlist = []
            for route in keylist:
                if len(route.minkeys - route.dotkeys - keys) == 0:
                    newlist.append(route)
            keylist = newlist

            class KeySorter:

                def __init__(self, obj, *args):
                    self.obj = obj

                def __lt__(self, other):
                    return self._keysort(self.obj, other.obj) < 0

                def _keysort(self, a, b):
                    """Sorts two sets of sets, to order them ideally for
                    matching."""
                    a = a.maxkeys
                    b = b.maxkeys

                    lendiffa = len(keys ^ a)
                    lendiffb = len(keys ^ b)
                    # If they both match, don't switch them
                    if lendiffa == 0 and lendiffb == 0:
                        return 0

                    # First, if a matches exactly, use it
                    if lendiffa == 0:
                        return -1

                    # Or b matches exactly, use it
                    if lendiffb == 0:
                        return 1

                    # Neither matches exactly, return the one with the most in
                    # common
                    if self._compare(lendiffa, lendiffb) != 0:
                        return self._compare(lendiffa, lendiffb)

                    # Neither matches exactly, but if they both have just as
                    # much in common
                    if len(keys & b) == len(keys & a):
                        # Then we return the shortest of the two
                        return self._compare(len(a), len(b))

                    # Otherwise, we return the one that has the most in common
                    else:
                        return self._compare(len(keys & b), len(keys & a))

                def _compare(self, obj1, obj2):
                    if obj1 < obj2:
                        return -1
                    elif obj1 < obj2:
                        return 1
                    else:
                        return 0

            keylist.sort(key=KeySorter)
            if cacheset:
                sortcache[cachekey] = keylist

        # Iterate through the keylist of sorted routes (or a single route if
        # it was passed in explicitly for hardcoded named routes)
        for route in keylist:
            fail = False
            for key in route.hardcoded:
                kval = kargs.get(key)
                if not kval:
                    continue
                kval = as_unicode(kval, self.encoding)
                if kval != route.defaults[key] and \
                        not callable(route.defaults[key]):
                    fail = True
                    break
            if fail:
                continue
            path = route.generate(**kargs)
            if path:
                if self.prefix:
                    path = self.prefix + path
                external_static = route.static and route.external
                if not route.absolute and not external_static:
                    path = script_name + path
                    key = cache_key_script_name
                else:
                    key = cache_key
                if self.urlcache is not None:
                    self.urlcache.put(key, str(path))
                return str(path)
            else:
                continue
        return None
Beispiel #5
0
    def generate(self, *args, **kargs):
        """Generate a route from a set of keywords

        Returns the url text, or None if no URL could be generated.

        .. code-block:: python

            m.generate(controller='content',action='view',id=10)

        """
        # Generate ourself if we haven't already
        if not self._created_gens:
            self._create_gens()

        if self.append_slash:
            kargs['_append_slash'] = True

        if not self.explicit:
            if 'controller' not in kargs:
                kargs['controller'] = 'content'
            if 'action' not in kargs:
                kargs['action'] = 'index'

        environ = kargs.pop('_environ', self.environ) or {}
        if 'SCRIPT_NAME' in environ:
            script_name = environ['SCRIPT_NAME']
        elif self.environ and 'SCRIPT_NAME' in self.environ:
            script_name = self.environ['SCRIPT_NAME']
        else:
            script_name = ""
        controller = kargs.get('controller', None)
        action = kargs.get('action', None)

        # If the URL didn't depend on the SCRIPT_NAME, we'll cache it
        # keyed by just by kargs; otherwise we need to cache it with
        # both SCRIPT_NAME and kargs:
        cache_key = six.text_type(args).encode('utf8') + \
            six.text_type(kargs).encode('utf8')

        if self.urlcache is not None:
            if six.PY3:
                cache_key_script_name = b':'.join(
                    (script_name.encode('utf-8'), cache_key))
            else:
                cache_key_script_name = '%s:%s' % (script_name, cache_key)

            # Check the url cache to see if it exists, use it if it does
            val = self.urlcache.get(cache_key_script_name, self)
            if val != self:
                return val

        controller = as_unicode(controller, self.encoding)
        action = as_unicode(action, self.encoding)

        actionlist = self._gendict.get(controller) or self._gendict.get(
            '*', {})
        if not actionlist and not args:
            return None
        (keylist, sortcache) = actionlist.get(action) or \
            actionlist.get('*', (None, {}))
        if not keylist and not args:
            return None

        keys = frozenset(kargs.keys())
        cacheset = False
        cachekey = six.text_type(keys)
        cachelist = sortcache.get(cachekey)
        if args:
            keylist = args
        elif cachelist:
            keylist = cachelist
        else:
            cacheset = True
            newlist = []
            for route in keylist:
                if len(route.minkeys - route.dotkeys - keys) == 0:
                    newlist.append(route)
            keylist = newlist

            class KeySorter:
                def __init__(self, obj, *args):
                    self.obj = obj

                def __lt__(self, other):
                    return self._keysort(self.obj, other.obj) < 0

                def _keysort(self, a, b):
                    """Sorts two sets of sets, to order them ideally for
                    matching."""
                    a = a.maxkeys
                    b = b.maxkeys

                    lendiffa = len(keys ^ a)
                    lendiffb = len(keys ^ b)
                    # If they both match, don't switch them
                    if lendiffa == 0 and lendiffb == 0:
                        return 0

                    # First, if a matches exactly, use it
                    if lendiffa == 0:
                        return -1

                    # Or b matches exactly, use it
                    if lendiffb == 0:
                        return 1

                    # Neither matches exactly, return the one with the most in
                    # common
                    if self._compare(lendiffa, lendiffb) != 0:
                        return self._compare(lendiffa, lendiffb)

                    # Neither matches exactly, but if they both have just as
                    # much in common
                    if len(keys & b) == len(keys & a):
                        # Then we return the shortest of the two
                        return self._compare(len(a), len(b))

                    # Otherwise, we return the one that has the most in common
                    else:
                        return self._compare(len(keys & b), len(keys & a))

                def _compare(self, obj1, obj2):
                    if obj1 < obj2:
                        return -1
                    elif obj1 < obj2:
                        return 1
                    else:
                        return 0

            keylist.sort(key=KeySorter)
            if cacheset:
                sortcache[cachekey] = keylist

        # Iterate through the keylist of sorted routes (or a single route if
        # it was passed in explicitly for hardcoded named routes)
        for route in keylist:
            fail = False
            for key in route.hardcoded:
                kval = kargs.get(key)
                if not kval:
                    continue
                kval = as_unicode(kval, self.encoding)
                if kval != route.defaults[key] and \
                        not callable(route.defaults[key]):
                    fail = True
                    break
            if fail:
                continue
            path = route.generate(**kargs)
            if path:
                if self.prefix:
                    path = self.prefix + path
                external_static = route.static and route.external
                if not route.absolute and not external_static:
                    path = script_name + path
                    key = cache_key_script_name
                else:
                    key = cache_key
                if self.urlcache is not None:
                    self.urlcache.put(key, str(path))
                return str(path)
            else:
                continue
        return None
    def generate(self, _ignore_req_list=False, _append_slash=False, **kargs):
        """Generate a URL from ourself given a set of keyword arguments
        
        Toss an exception if this
        set of keywords would cause a gap in the url.
        
        """
        # Verify that our args pass any regexp requirements
        if not _ignore_req_list:
            for key in self.reqs.keys():
                val = kargs.get(key)
                if val and not self.req_regs[key].match(
                        self.make_unicode(val)):
                    return False

        # Verify that if we have a method arg, its in the method accept list.
        # Also, method will be changed to _method for route generation
        meth = as_unicode(kargs.get('method'), self.encoding)
        if meth:
            if self.conditions and 'method' in self.conditions \
                and meth.upper() not in self.conditions['method']:
                return False
            kargs.pop('method')

        if self.minimization:
            url = self.generate_minimized(kargs)
        else:
            url = self.generate_non_minimized(kargs)

        if url is False:
            return url

        if not url.startswith('/') and not self.static:
            url = '/' + url
        extras = frozenset(kargs.keys()) - self.maxkeys
        if extras:
            if _append_slash and not url.endswith('/'):
                url += '/'
            fragments = []
            # don't assume the 'extras' set preserves order: iterate
            # through the ordered kargs instead
            for key in kargs:
                if key not in extras:
                    continue
                if key == 'action' or key == 'controller':
                    continue
                val = kargs[key]
                if isinstance(val, (tuple, list)):
                    for value in val:
                        value = as_unicode(value, self.encoding)
                        fragments.append(
                            (key, _str_encode(value, self.encoding)))
                else:
                    val = as_unicode(val, self.encoding)
                    fragments.append((key, _str_encode(val, self.encoding)))
            if fragments:
                url += '?'
                url += urllib.urlencode(fragments)
        elif _append_slash and not url.endswith('/'):
            url += '/'
        return url
    def generate_minimized(self, kargs):
        """Generate a minimized version of the URL"""
        routelist = self.routebackwards
        urllist = []
        gaps = False
        for part in routelist:
            if isinstance(part, dict) and part['type'] in (':', '.'):
                arg = part['name']

                # For efficiency, check these just once
                has_arg = kargs.has_key(arg)
                has_default = self.defaults.has_key(arg)

                # Determine if we can leave this part off
                # First check if the default exists and wasn't provided in the
                # call (also no gaps)
                if has_default and not has_arg and not gaps:
                    continue

                # Now check to see if there's a default and it matches the
                # incoming call arg
                if (has_default and has_arg) and self.make_unicode(kargs[arg]) == \
                    self.make_unicode(self.defaults[arg]) and not gaps:
                    continue

                # We need to pull the value to append, if the arg is None and
                # we have a default, use that
                if has_arg and kargs[arg] is None and has_default and not gaps:
                    continue

                # Otherwise if we do have an arg, use that
                elif has_arg:
                    val = kargs[arg]

                elif has_default and self.defaults[arg] is not None:
                    val = self.defaults[arg]
                # Optional format parameter?
                elif part['type'] == '.':
                    continue
                # No arg at all? This won't work
                else:
                    return False

                val = as_unicode(val, self.encoding)
                urllist.append(url_quote(val, self.encoding))
                if part['type'] == '.':
                    urllist.append('.')

                if has_arg:
                    del kargs[arg]
                gaps = True
            elif isinstance(part, dict) and part['type'] == '*':
                arg = part['name']
                kar = kargs.get(arg)
                if kar is not None:
                    urllist.append(url_quote(kar, self.encoding))
                    gaps = True
            elif part and part[-1] in self.done_chars:
                if not gaps and part in self.done_chars:
                    continue
                elif not gaps:
                    urllist.append(part[:-1])
                    gaps = True
                else:
                    gaps = True
                    urllist.append(part)
            else:
                gaps = True
                urllist.append(part)
        urllist.reverse()
        url = ''.join(urllist)
        return url
    def match(self,
              url,
              environ=None,
              sub_domains=False,
              sub_domains_ignore=None,
              domain_match=''):
        """Match a url to our regexp. 
        
        While the regexp might match, this operation isn't
        guaranteed as there's other factors that can cause a match to
        fail even though the regexp succeeds (Default that was relied
        on wasn't given, requirement regexp doesn't pass, etc.).
        
        Therefore the calling function shouldn't assume this will
        return a valid dict, the other possible return is False if a
        match doesn't work out.
        
        """
        # Static routes don't match, they generate only
        if self.static:
            return False

        match = self.regmatch.match(url)

        if not match:
            return False

        sub_domain = None

        if sub_domains and environ and 'HTTP_HOST' in environ:
            host = environ['HTTP_HOST'].split(':')[0]
            sub_match = re.compile('^(.+?)\.%s$' % domain_match)
            subdomain = re.sub(sub_match, r'\1', host)
            if subdomain not in sub_domains_ignore and host != subdomain:
                sub_domain = subdomain

        if self.conditions:
            if 'method' in self.conditions and environ and \
                environ['REQUEST_METHOD'] not in self.conditions['method']:
                return False

            # Check sub-domains?
            use_sd = self.conditions.get('sub_domain')
            if use_sd and not sub_domain:
                return False
            elif not use_sd and 'sub_domain' in self.conditions and sub_domain:
                return False
            if isinstance(use_sd, list) and sub_domain not in use_sd:
                return False

        matchdict = match.groupdict()
        result = {}
        extras = self._default_keys - frozenset(matchdict.keys())
        for key, val in matchdict.iteritems():
            if key != 'path_info' and self.encoding:
                # change back into python unicode objects from the URL
                # representation
                try:
                    val = as_unicode(val, self.encoding, self.decode_errors)
                except UnicodeDecodeError:
                    return False

            if not val and key in self.defaults and self.defaults[key]:
                result[key] = self.defaults[key]
            else:
                result[key] = val
        for key in extras:
            result[key] = self.defaults[key]

        # Add the sub-domain if there is one
        if sub_domains:
            result['sub_domain'] = sub_domain

        # If there's a function, call it with environ and expire if it
        # returns False
        if self.conditions and 'function' in self.conditions and \
            not self.conditions['function'](environ, result):
            return False

        return result
Beispiel #9
0
    def generate_minimized(self, kargs):
        """Generate a minimized version of the URL"""
        routelist = self.routebackwards
        urllist = []
        gaps = False
        for part in routelist:
            if isinstance(part, dict) and part['type'] in (':', '.'):
                arg = part['name']

                # For efficiency, check these just once
                has_arg = arg in kargs
                has_default = arg in self.defaults

                # Determine if we can leave this part off
                # First check if the default exists and wasn't provided in the
                # call (also no gaps)
                if has_default and not has_arg and not gaps:
                    continue

                # Now check to see if there's a default and it matches the
                # incoming call arg
                if (has_default and has_arg) and \
                    self.make_unicode(kargs[arg]) == \
                        self.make_unicode(self.defaults[arg]) and not gaps:
                    continue

                # We need to pull the value to append, if the arg is None and
                # we have a default, use that
                if has_arg and kargs[arg] is None and has_default and not gaps:
                    continue

                # Otherwise if we do have an arg, use that
                elif has_arg:
                    val = kargs[arg]

                elif has_default and self.defaults[arg] is not None:
                    val = self.defaults[arg]
                # Optional format parameter?
                elif part['type'] == '.':
                    continue
                # No arg at all? This won't work
                else:
                    return False

                val = as_unicode(val, self.encoding)
                urllist.append(url_quote(val, self.encoding))
                if part['type'] == '.':
                    urllist.append('.')

                if has_arg:
                    del kargs[arg]
                gaps = True
            elif isinstance(part, dict) and part['type'] == '*':
                arg = part['name']
                kar = kargs.get(arg)
                if kar is not None:
                    urllist.append(url_quote(kar, self.encoding))
                    gaps = True
            elif part and part[-1] in self.done_chars:
                if not gaps and part in self.done_chars:
                    continue
                elif not gaps:
                    urllist.append(part[:-1])
                    gaps = True
                else:
                    gaps = True
                    urllist.append(part)
            else:
                gaps = True
                urllist.append(part)
        urllist.reverse()
        url = ''.join(urllist)
        return url
Beispiel #10
0
    def match(self, url, environ=None, sub_domains=False,
              sub_domains_ignore=None, domain_match=''):
        """Match a url to our regexp.

        While the regexp might match, this operation isn't
        guaranteed as there's other factors that can cause a match to
        fail even though the regexp succeeds (Default that was relied
        on wasn't given, requirement regexp doesn't pass, etc.).

        Therefore the calling function shouldn't assume this will
        return a valid dict, the other possible return is False if a
        match doesn't work out.

        """
        # Static routes don't match, they generate only
        if self.static:
            return False

        match = self.regmatch.match(url)

        if not match:
            return False

        sub_domain = None

        if sub_domains and environ and 'HTTP_HOST' in environ:
            host = environ['HTTP_HOST'].split(':')[0]
            sub_match = re.compile('^(.+?)\.%s$' % domain_match)
            subdomain = re.sub(sub_match, r'\1', host)
            if subdomain not in sub_domains_ignore and host != subdomain:
                sub_domain = subdomain

        if self.conditions:
            if 'method' in self.conditions and environ and \
                    environ['REQUEST_METHOD'] not in self.conditions['method']:
                return False

            # Check sub-domains?
            use_sd = self.conditions.get('sub_domain')
            if use_sd and not sub_domain:
                return False
            elif not use_sd and 'sub_domain' in self.conditions and sub_domain:
                return False
            if isinstance(use_sd, list) and sub_domain not in use_sd:
                return False

        matchdict = match.groupdict()
        result = {}
        extras = self._default_keys - frozenset(matchdict.keys())
        for key, val in six.iteritems(matchdict):
            if key != 'path_info' and self.encoding:
                # change back into python unicode objects from the URL
                # representation
                try:
                    val = as_unicode(val, self.encoding, self.decode_errors)
                except UnicodeDecodeError:
                    return False

            if not val and key in self.defaults and self.defaults[key]:
                result[key] = self.defaults[key]
            else:
                result[key] = val
        for key in extras:
            result[key] = self.defaults[key]

        # Add the sub-domain if there is one
        if sub_domains:
            result['sub_domain'] = sub_domain

        # If there's a function, call it with environ and expire if it
        # returns False
        if self.conditions and 'function' in self.conditions and \
                not self.conditions['function'](environ, result):
            return False

        return result