improve url_for to support multi values for one arg, add _anchor/_external/_scheme options
This commit is contained in:
		| @@ -230,6 +230,16 @@ class Sanic: | ||||
|         matched_params = re.findall( | ||||
|             self.router.parameter_pattern, uri) | ||||
|  | ||||
|         # _method is only a placeholder now, don't know how to support it | ||||
|         kwargs.pop('_method', None) | ||||
|         anchor = kwargs.pop('_anchor', '') | ||||
|         # _external need SERVER_NAME in config or pass _server arg | ||||
|         external = kwargs.pop('_external', False) | ||||
|         scheme = kwargs.pop('_scheme', '') | ||||
|         if scheme and not external: | ||||
|             raise ValueError('When specifying _scheme, _external must be True') | ||||
|  | ||||
|         netloc = kwargs.pop('_server', self.config.get('SERVER_NAME', '')) | ||||
|         for match in matched_params: | ||||
|             name, _type, pattern = self.router.parse_parameter_string( | ||||
|                 match) | ||||
| @@ -271,11 +281,9 @@ class Sanic: | ||||
|  | ||||
|         # parse the remainder of the keyword arguments into a querystring | ||||
|         if kwargs: | ||||
|             query_string = urlencode(kwargs) | ||||
|             out = urlunparse(( | ||||
|                 '', '', out, | ||||
|                 '', query_string, '' | ||||
|             )) | ||||
|             query_string = urlencode(kwargs, doseq=True) | ||||
|             # scheme://netloc/path;parameters?query#fragment | ||||
|             out = urlunparse((scheme, netloc, out, '', query_string, anchor)) | ||||
|  | ||||
|         return out | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,11 @@ PORT = 42101 | ||||
|  | ||||
|  | ||||
| async def local_request(method, uri, cookies=None, *args, **kwargs): | ||||
|     url = 'http://{host}:{port}{uri}'.format(host=HOST, port=PORT, uri=uri) | ||||
|     if uri.startswith(('http:', 'https:', 'ftp:', 'ftps://' '//')): | ||||
|         url = uri | ||||
|     else: | ||||
|         url = 'http://{host}:{port}{uri}'.format(host=HOST, port=PORT, uri=uri) | ||||
|  | ||||
|     log.info(url) | ||||
|     async with aiohttp.ClientSession(cookies=cookies) as session: | ||||
|         async with getattr( | ||||
|   | ||||
| @@ -5,7 +5,7 @@ from sanic import Sanic | ||||
| from sanic.response import text | ||||
| from sanic.views import HTTPMethodView | ||||
| from sanic.blueprints import Blueprint | ||||
| from sanic.utils import sanic_endpoint_test | ||||
| from sanic.utils import sanic_endpoint_test, PORT as test_port | ||||
| from sanic.exceptions import URLBuildError | ||||
|  | ||||
| import string | ||||
| @@ -39,6 +39,30 @@ def test_simple_url_for_getting(simple_app): | ||||
|         assert response.text == letter | ||||
|  | ||||
|  | ||||
| def test_simple_url_for_getting_with_duplicate_params(simple_app): | ||||
|     kw = dict(arg1=['value1', 'value2'], _anchor='anchor') | ||||
|     for letter in string.ascii_letters: | ||||
|         url = simple_app.url_for(letter, **kw) | ||||
|  | ||||
|         assert url == '/{}?arg1=value1&arg1=value2#anchor'.format(letter) | ||||
|         request, response = sanic_endpoint_test(simple_app, uri=url) | ||||
|         assert response.status == 200 | ||||
|         assert response.text == letter | ||||
|  | ||||
|  | ||||
| def test_simple_url_for_getting_with_special_params(simple_app): | ||||
|     kw = dict(arg1='value1', _anchor='anchor', _scheme='http', | ||||
|               _server='localhost:{}'.format(test_port), _external=True) | ||||
|     url_fmt = 'http://localhost:{}/{}?arg1=value1#anchor' | ||||
|     for letter in string.ascii_letters: | ||||
|         url = simple_app.url_for(letter, **kw) | ||||
|  | ||||
|         assert url == url_fmt.format(test_port, letter) | ||||
|         request, response = sanic_endpoint_test(simple_app, uri=url) | ||||
|         assert response.status == 200 | ||||
|         assert response.text == letter | ||||
|  | ||||
|  | ||||
| def test_fails_if_endpoint_not_found(): | ||||
|     app = Sanic('fail_url_build') | ||||
|  | ||||
| @@ -75,6 +99,19 @@ def test_fails_url_build_if_param_not_passed(): | ||||
|     assert 'Required parameter `Z` was not passed to url_for' in str(e.value) | ||||
|  | ||||
|  | ||||
| def test_fails_url_build_if_params_not_passed(): | ||||
|     app = Sanic('fail_url_build') | ||||
|  | ||||
|     @app.route('/fail') | ||||
|     def fail(): | ||||
|         return text('this should fail') | ||||
|  | ||||
|     with pytest.raises(ValueError) as e: | ||||
|         app.url_for('fail', _scheme='http') | ||||
|  | ||||
|     assert str(e.value) == 'When specifying _scheme, _external must be True' | ||||
|  | ||||
|  | ||||
| COMPLEX_PARAM_URL = ( | ||||
|     '/<foo:int>/<four_letter_string:[A-z]{4}>/' | ||||
|     '<two_letter_string:[A-z]{2}>/<normal_string>/<some_number:number>') | ||||
| @@ -179,11 +216,11 @@ def blueprint_app(): | ||||
|         return text( | ||||
|             'foo from first : {}'.format(param)) | ||||
|  | ||||
|     @second_print.route('/foo') # noqa | ||||
|     @second_print.route('/foo')  # noqa | ||||
|     def foo(): | ||||
|         return text('foo from second') | ||||
|  | ||||
|     @second_print.route('/foo/<param>') # noqa | ||||
|     @second_print.route('/foo/<param>')  # noqa | ||||
|     def foo_with_param(request, param): | ||||
|         return text( | ||||
|             'foo from second : {}'.format(param)) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 lixxu
					lixxu