Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
nitely committed Jul 17, 2017
1 parent cbf3ff3 commit fc60589
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 30 deletions.
73 changes: 43 additions & 30 deletions kua/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
Any,
Dict,
Union,
Callable)
Callable,
Iterator)


__all__ = [
Expand All @@ -19,7 +20,9 @@
'RouteResolved']

# This is a nested structure similar to a linked-list
VariablePartsType = Tuple[tuple, Tuple[str, str]]
WrappedVariablePartsType = Tuple[tuple, Tuple[str, str]]
VariablePartsType = Tuple[Union[str, Tuple[str]]]
VariablePartsIterType = Iterator[Union[str, Tuple[str]]]
ValidateType = Dict[str, Callable[[str], bool]]


Expand Down Expand Up @@ -75,7 +78,7 @@ def decode_parts(parts):
raise DecodeRouteError('Can\'t decode the URL')


def _unwrap(variable_parts: VariablePartsType):
def _unwrap(variable_parts: WrappedVariablePartsType) -> VariablePartsIterType:
"""
Yield URL parts. The given parts are usually in reverse order.
"""
Expand Down Expand Up @@ -109,44 +112,45 @@ def _unwrap(variable_parts: VariablePartsType):
yield tuple(reversed(var_any))


def unwrap(variable_parts: WrappedVariablePartsType) -> VariablePartsType:
return tuple(reversed(tuple(_unwrap(variable_parts))))


def make_params(
key_parts: Sequence[str],
variable_parts: VariablePartsType) -> Dict[str, Union[str, Tuple[str]]]:
variable_parts: VariablePartsIterType) -> Dict[str, Union[str, Tuple[str]]]:
"""
Map keys to variables. This map\
URL-pattern variables to\
a URL related parts
:param key_parts: A list of URL parts
:param variable_parts: A linked-list\
(ala nested tuples) of URL parts
:param variable_parts: A list of URL parts
:return: The param dict with the values\
assigned to the keys
:private:
"""
# The unwrapped variable parts are in reverse order.
# Instead of reversing those we reverse the key parts
# and avoid the O(n) space required for reversing the vars
return dict(zip(reversed(key_parts), _unwrap(variable_parts)))
return dict(zip(key_parts, variable_parts))


_SAFE_COMPONENT = re.compile(r'[ \w\-_.]+')


def _is_safe(part):
def _is_safe(part: str) -> bool:
if isinstance(part, tuple): # /:*parts/
return all(
_SAFE_COMPONENT.fullmatch(v)
for v in part)
return all(_is_safe(p) for p in part)

return _SAFE_COMPONENT.fullmatch(part) is not None


def validate(params: dict, params_validate: dict):
def validate(
key_parts: Sequence[str],
variable_parts: VariablePartsIterType,
params_validate: dict) -> bool:
return all(
params_validate.get(param, _is_safe)(value)
for param, value in params.items())
for param, value in zip(key_parts, variable_parts))


_Route = collections.namedtuple(
Expand Down Expand Up @@ -194,6 +198,23 @@ def _route(key_parts: Sequence, anything: Any, validate: ValidateType) -> _Route
""")


def _resolve(
variable_parts: VariablePartsType,
routes: Sequence[_Route]) -> Union[RouteResolved, None]:
for route in routes:
if validate(
route.key_parts,
variable_parts,
route.validate):
return RouteResolved(
params=make_params(
key_parts=route.key_parts,
variable_parts=variable_parts),
anything=route.anything)

return None


class Routes:
"""
Route URLs to registered URL patterns.
Expand Down Expand Up @@ -328,23 +349,15 @@ def _match(self, parts: Sequence[str]) -> RouteResolved:
try:
part = parts[depth]
except IndexError:
if self._ROUTE_NODE not in curr:
continue

for route in curr[self._ROUTE_NODE]: # todo: move elsewhere
params = make_params(
key_parts=route.key_parts,
variable_parts=curr_variable_parts)
route_resolved = _resolve(
variable_parts=unwrap(curr_variable_parts),
routes=curr.get(self._ROUTE_NODE, ()))

if not validate(params, route.validate):
continue

return RouteResolved(
params=params,
anything=route.anything)
else: # no-break
if not route_resolved:
continue

return route_resolved

if self._VAR_ANY_NODE in curr:
to_visit.append((
{self._VAR_ANY_NODE: curr[self._VAR_ANY_NODE]},
Expand Down
2 changes: 2 additions & 0 deletions tests/tests_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ def test_var_validate_clashing(self):
self.routes.add(':var', 'bar', {'var': is_alphanum})

self.assertEqual(self.routes.match('123').anything, 'foo')
self.assertEqual(self.routes.match('123').params, {'var': '123'})
self.assertEqual(self.routes.match('foo123').anything, 'bar')
self.assertEqual(self.routes.match('foo123').params, {'var': 'foo123'})

def test_any_var_validate(self):
"""
Expand Down

0 comments on commit fc60589

Please sign in to comment.