diff --git a/setup.py b/setup.py index 4e777f3..511ae0d 100644 --- a/setup.py +++ b/setup.py @@ -40,9 +40,7 @@ def _read(name): long_description=readme + "\n" + changes, test_suite="webdispatch", license="MIT", - install_requires=[ - "mypy_extensions", - ], + install_requires=[], tests_require=tests_require, extras_require={ "testing": tests_require, diff --git a/webdispatch/tests/test_uritemplate.py b/webdispatch/tests/test_uritemplate.py index f327d3c..59f4381 100644 --- a/webdispatch/tests/test_uritemplate.py +++ b/webdispatch/tests/test_uritemplate.py @@ -127,8 +127,8 @@ def test_match_empty(self): result = target.match(path) - compare(result["matchdict"], dict()) - compare(result["matchlength"], 0) + compare(result.matchdict, dict()) + compare(result.matchlength, 0) def test_wildcard(self): """ test matching pattern including wildcard""" @@ -136,8 +136,8 @@ def test_wildcard(self): target = self._make_one(path) result = target.match("hoge/egg/bacon") - compare(result["matchdict"], dict(var1="egg")) - compare(result["matchlength"], 9) + compare(result.matchdict, dict(var1="egg")) + compare(result.matchlength, 9) def test_match_no_match(self): """ test no mathing""" @@ -153,8 +153,8 @@ def test_match_match_one(self): target = self._make_one(path) result = target.match("a") - compare(result["matchdict"], dict(var1="a")) - compare(result["matchlength"], 1) + compare(result.matchdict, dict(var1="a")) + compare(result.matchlength, 1) def test_match_match_complex_word(self): """ test matching a string""" @@ -162,7 +162,7 @@ def test_match_match_complex_word(self): target = self._make_one(path) result = target.match("abc") - compare(result["matchdict"], dict(var1="abc")) + compare(result.matchdict, dict(var1="abc")) def test_match_match_many(self): """ test matching pattern including two vars """ @@ -170,7 +170,7 @@ def test_match_match_many(self): target = self._make_one(path) result = target.match("a/users/egg") - compare(result["matchdict"], dict(var1="a", var2="egg")) + compare(result.matchdict, dict(var1="a", var2="egg")) def test_match_conveter(self): """ test matching pattern including specified converter """ @@ -178,7 +178,7 @@ def test_match_conveter(self): target = self._make_one(path) result = target.match("1/users/egg") - compare(result["matchdict"], dict(var1=1, var2="egg")) + compare(result.matchdict, dict(var1=1, var2="egg")) def test_match_conveter_error(self): """ test matching pattern including specified converter """ @@ -198,7 +198,7 @@ def test_match_custom_conveter(self): target = self._make_one(path, converters=converters) result = target.match("1/users/20140420") - compare(result["matchdict"], + compare(result.matchdict, dict(var1="1", var2=datetime(2014, 4, 20))) def test_substitue(self): diff --git a/webdispatch/tests/test_urldispatcher.py b/webdispatch/tests/test_urldispatcher.py index 1b48322..5eac2be 100644 --- a/webdispatch/tests/test_urldispatcher.py +++ b/webdispatch/tests/test_urldispatcher.py @@ -60,12 +60,14 @@ def test_lookup_none(self): def test_lookup(self): """ test looking up basic usage """ + from webdispatch.uritemplate import MatchResult target = self._make_one() target.add('testing-route', 'a') result = target.lookup('a') - compare(result, {'name': 'testing-route', - 'matchdict': {}, - 'matchlength': 1}) + compare(result, C(MatchResult, + name='testing-route', + matchdict={}, + matchlength=1)) def test_generate(self): """ test generating url """ diff --git a/webdispatch/uritemplate.py b/webdispatch/uritemplate.py index 7218588..1f1c3bf 100644 --- a/webdispatch/uritemplate.py +++ b/webdispatch/uritemplate.py @@ -11,7 +11,6 @@ Callable, Tuple, ) -from mypy_extensions import TypedDict VARS_PT = re.compile(r"{(?P[a-zA-Z0-9_]+)" r"(:(?P[a-zA-Z0-9_]+))?}", @@ -84,9 +83,22 @@ class URITemplateFormatException(Exception): """ raised when uri template format error duaring""" -MATCH_RESULT_TYPE = TypedDict( - "MATCH_RESULT_TYPE", - {"matchdict": Dict[str, Any], "matchlength": int}) +class MatchResult: + """ result of parsing url """ + def __init__(self, matchdict: Dict[str, Any], matchlength: int) -> None: + self.name = None # type: str + self.matchdict = matchdict + self.matchlength = matchlength + + def new_named_args(self, cur_named_args: Dict[str, Any]) -> Dict[str, Any]: + """ create new named args updating current name args""" + named_args = cur_named_args.copy() + named_args.update(self.matchdict) + return named_args + + def split_path_info(self, path_info: str) -> Tuple[str, str]: + """ split path_info to new script_name and new path_info""" + return path_info[:self.matchlength], path_info[self.matchlength:] class URITemplate(object): @@ -105,7 +117,7 @@ def __init__(self, tmpl_pattern: str, self.converters = detect_converters( tmpl_pattern, converters) - def match(self, path_info: str) -> MATCH_RESULT_TYPE: + def match(self, path_info: str) -> MatchResult: """ parse path_info and detect urlvars of url pattern """ matched = self.regex.match(path_info) if matched is None: @@ -118,8 +130,8 @@ def match(self, path_info: str) -> MATCH_RESULT_TYPE: except ValueError: return None - return {"matchdict": matchdict, - "matchlength": matchlength} + return MatchResult(matchdict, + matchlength) def convert_values(self, matchdict: Dict[str, str]) -> Dict[str, Any]: """ convert values of ``matchdict`` diff --git a/webdispatch/urldispatcher.py b/webdispatch/urldispatcher.py index 8eb8fc5..c4c03d5 100644 --- a/webdispatch/urldispatcher.py +++ b/webdispatch/urldispatcher.py @@ -11,7 +11,7 @@ Tuple, Iterable, ) -from .uritemplate import URITemplate +from .uritemplate import URITemplate, MatchResult from .base import DispatchBase @@ -29,14 +29,14 @@ def add(self, name: str, pattern: str) -> None: self.patterns[name] = URITemplate( pattern, converters=self.converters) - def lookup(self, path_info: str) -> Dict[str, Any]: + def lookup(self, path_info: str) -> MatchResult: """ lookup url match for path_info """ for name, pattern in self.patterns.items(): match = pattern.match(path_info) if match is None: continue - match["name"] = name + match.name = name return match return None @@ -106,23 +106,22 @@ def detect_view_name(self, environ: Dict[str, Any]) -> str: if match is None: return None - extra_path_info = path_info[match["matchlength"]:] + splited = match.split_path_info(path_info) + extra_path_info = splited[1] pos_args = [] # type: List[str] - named_args = match["matchdict"] routing_args = environ.get('wsgiorg.routing_args', ((), {})) (cur_pos, cur_named) = routing_args new_pos = list(cur_pos) + list(pos_args) - new_named = cur_named.copy() - new_named.update(named_args) + new_named = match.new_named_args(cur_named) environ['wsgiorg.routing_args'] = (new_pos, new_named) environ['webdispatch.urlmapper'] = self.urlmapper urlgenerator = URLGenerator(environ, self.urlmapper) environ['webdispatch.urlgenerator'] = urlgenerator - environ['SCRIPT_NAME'] = script_name + path_info[:match["matchlength"]] + environ['SCRIPT_NAME'] = script_name + splited[0] environ['PATH_INFO'] = extra_path_info - return match["name"] + return match.name def on_view_not_found( self,