Source code for lighty.wsgi.urls

'''Package contains URL patterns management functions
'''
import copy
import re
from functools import partial, reduce

from ..monads import NoneMonad
from . import decorators


PATH_PATTERN = re.compile(
        '^(?P<module>(([\\w\\d]+\\.)*[\\w\\d]+))\\.(?P<function>([\\w\\d]+))$')
VARIABLE_PATTERN = re.compile('(<[\w_]+[:]?[\w]{0,5}>)')
TYPE_PATTERNS = {
        'int': '[\\d]+',
        'float': '([\\d]+(\\.[\\d]*)?)',
        'char': '[\\w\\d]',
        'str': '[\\w\\d_\\-\\.\\,]+',
        'slug': '[\\w\\d_\\-]+',
        'path': '[\\w\\d_\\-\\.\\,/]+',
}
TO_ESCAPE = ('.', '\\', '[', ']', '(', ')', '+', '*', '?', '{', '}', '-', '|')
HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE']


[docs]def load_urls(name): '''Load urls file. This file requires to contains urlpatterns variable. It could be list or tuple of results of url() function calling. from app_name.views import hello_view urlpatterns = ( url('/test', 'app_name.views.test_view'), url('/hello/(?P<name>[\w]+)', hello_view), ) ''' module = __import__(name, globals(), locals(), 'urlpatterns') return module.urlpatterns
[docs]def load_view(view): '''Load view for name if needed ''' if callable(view): return view if hasattr(view, 'is_view') else decorators.view(view) elif not type(view) is str: return TypeError('Error url creation') explain = PATH_PATTERN.match(view) if not explain: return ImportError('%s could not be loaded' % view) func_name = explain.group('function') pack_name = explain.group('module') module = __import__(pack_name, globals(), locals(), func_name) func = getattr(module, explain.group('function')) if not callable(func): return TypeError('%s.%s is not callable' % (pack_name, func_name)) return func if hasattr(func, 'is_view') else decorators.view(func)
[docs]def escape_url(url): pattern = url for symbol in TO_ESCAPE: pattern = pattern.replace(symbol, '\\' + symbol) return pattern
[docs]def get_arg(pattern, constr): '''Get argument types ''' actual = pattern[1:-1] name, type = ':' in actual and actual.split(':') or (actual, 'str') return (pattern, name, constr[name] if name in constr else TYPE_PATTERNS[type])
[docs]def replace_pattern(url, arg): pattern, name, regexp = arg return url.replace(pattern, '(?P<%s>%s)' % (name, regexp))
[docs]def replace_arg(url, arg): pattern, value = arg return url.replace(pattern, str(value))
[docs]def url(pattern, view, name='', constraints={}, methods=HTTP_METHODS, defaults={}): '''Prepare url for application ''' vars = [get_arg(var, constraints) for var in VARIABLE_PATTERN.findall(pattern)] constrs = dict([(var[1], var[2]) for var in vars]) args = dict([(var[1], var[0]) for var in vars]) url_expr = re.compile(reduce(replace_pattern, [pattern + '$'] + vars)) view_func = load_view(view) url_name = name is not '' and name or view_func return (url_expr, pattern, view_func, url_name, defaults, args, constrs, methods)
[docs]def resolve(urls, path, method=None): '''Resolve url ''' for expr, url, view, name, defaults, _, _, methods in urls: if not method or method in methods: match = expr.match(path) if match: call_args = copy.copy(defaults) call_args.update(match.groupdict()) return partial(view, **call_args) return NoneMonad(LookupError('There is no pattern matching path %s' % path))
[docs]def reverse(urls, lookup_name, lookup_args={}): '''Get url for name with specified args number ''' lookup_keys = sorted(lookup_args.keys()) for _, url, _, name, _, args, _, _ in urls: if name == lookup_name and lookup_keys == sorted(args.keys()): replacement = [(args[var], lookup_args[var]) for var in lookup_keys] return reduce(replace_arg, [url] + replacement) return NoneMonad(LookupError( 'Url for name "%s" with args "%s" was not found' % (lookup_name, ','.join(lookup_keys))))