1. Andriy Kornatskyy
  2. wheezy.routing


wheezy.routing / src / wheezy / routing / curly.py

""" ``curly`` module.

import re
from wheezy.routing.utils import outer_split

RE_SPLIT = re.compile('(?P<n>\{[\w:]+.*?\})')

patterns = {
    # one or more digits
    'i': r'\d+',
    'int': r'\d+',
    'number': r'\d+',
    'digits': r'\d+',
    # one or more word characters
    'w': r'\w+',
    'word': r'\w+',
    # everything until ``/``
    's': r'[^/]+',
    'segment': r'[^/]+',
    'part': r'[^/]+',
    # any
    '*': r'.+',
    'a': r'.+',
    'any': r'.+',
    'rest': r'.+'

default_pattern = 's'

def convert(s):
    """ Convert curly expression into regex with
        named groups.

        >>> convert(r'abc/{id}')

        >>> convert(r'abc/{id:i}')

        >>> convert(r'abc/{n}/{x:w}')

        >>> convert(r'{locale:(en|ru)}/home')

        >>> convert(r'{locale:(en|ru)}/home')

        Operates with optional values in square brackets

        >>> convert(r'[{locale:(en|ru)}/]home')

        >>> convert(r'item[/{id:i}]')

        >>> convert(r'{controller:w}[/{action:w}[/{id:i}]]')
    parts = outer_split(s, sep='[]')
    parts[1::2] = ['(%s)?' % p for p in map(convert, parts[1::2])]
    parts[::2] = map(convert_single, parts[::2])
    return ''.join(parts)

def convert_single(s):
    """ Convert curly expression into regex with
        named groups.
    parts = RE_SPLIT.split(s)
    return ''.join(map(replace, parts))

def replace(val):
    """ Replace ``{group_name:pattern_name}`` by regex with
        named groups.

        If the ``val`` is not an expression in curly brackets
        simply return it.

        >>> replace('abc')

        If the ``pattern_name`` is not specified, use
        default one.

        >>> replace('{abc}')

        Replace the ``pattern_name`` with regex from
        ``patterns`` dict.

        >>> replace('{abc:i}')

        The ``pattern_name`` not found use it as pattern.

        >>> replace('{locale:(en|ru)}')
    if val.startswith('{') and val.endswith('}'):
        group_name, pattern_name = parse(val[1:-1])
        pattern = patterns.get(pattern_name, pattern_name)
        return '(?P<%s>%s)' % (group_name, pattern)
    return val

def parse(s):
    """ Parse ``s`` according to ``group_name:pattern_name``.

        There is just ``group_name``, return default

        >>> parse('abc')
        ('abc', 's')

        Otherwise both.
        >>> parse('abc:i')
        ('abc', 'i')
    if ':' in s:
        return tuple(s.split(':', 1))
    return s, default_pattern