Repository: aio-libs/aiohttp

Issue:
ImportError: cannot import name 'unquote'
## Long story short

As of yarl 1.0.0 (available since one hour ago), yarl.unquote is no longer available, thus causing an import error at web_urldispatcher.py.

## Expected behaviour

Been able to run: `from aiohttp import web`

## Actual behaviour

`from aiohttp import web` raises the following error:
```Traceback (most recent call last):
  File "web.py", line 1, in <module>
    from aiohttp import web
  File "/home/ruggero/Development/mqtt/.venv/lib/python3.6/site-packages/aiohttp/web.py", line 15, in <module>
    from . import (hdrs, web_exceptions, web_fileresponse, web_middlewares,
  File "/home/ruggero/Development/mqtt/.venv/lib/python3.6/site-packages/aiohttp/web_middlewares.py", line 5, in <module>
    from aiohttp.web_urldispatcher import SystemRoute
  File "/home/ruggero/Development/mqtt/.venv/lib/python3.6/site-packages/aiohttp/web_urldispatcher.py", line 21, in <module>
    from yarl import URL, unquote
ImportError: cannot import name 'unquote'
```

## Steps to reproduce

Simply: `from aiohttp import web`

## Your environment

aiohttp 2.3.7 (both)
Ubuntu Linux 16.04
Python 3.6.3

## Workaround

Downgrade to yarl 0.18.0



Discussion:
Same problem since yarl 1.0.0, downgrading to `yarl==0.18.0` helps as workaround.

```
13:36:06   File "/var/jenkins/workspace/sometest-01-test/test/__init__.py", line 19, in <module>
13:36:06     from aiohttp import web
13:36:06   File "/var/jenkins/shiningpanda/jobs/bd59e0bb/virtualenvs/d41d8cd9/lib/python3.6/site-packages/aiohttp/web.py", line 15, in <module>
13:36:06     from . import (hdrs, web_exceptions, web_fileresponse, web_middlewares,
13:36:06   File "/var/jenkins/shiningpanda/jobs/bd59e0bb/virtualenvs/d41d8cd9/lib/python3.6/site-packages/aiohttp/web_middlewares.py", line 5, in <module>
13:36:06     from aiohttp.web_urldispatcher import SystemRoute
13:36:06   File "/var/jenkins/shiningpanda/jobs/bd59e0bb/virtualenvs/d41d8cd9/lib/python3.6/site-packages/aiohttp/web_urldispatcher.py", line 21, in <module>
13:36:06     from yarl import URL, unquote
13:36:06 ImportError: cannot import name 'unquote'
```
Same here, opened [a ticket to yarl](https://github.com/aio-libs/yarl/issues/161).
Confirmed @ilmaruk , you were faster 👍 


> CHANGES
> 1.0.0 (2018-01-15)
> Drop yarl.quote and yarl.unquote public functions (#155)

from: https://pypi.python.org/pypi/yarl



Can we get someone to pin `yarl==0.18.0` in deps until the problem is sorted out?
For reference, #2664 
@asvetlov ^^
Just in case: I'm working on moving aiohttp internals towards public yarl interfaces.


Resolving patch:
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -15,10 +15,10 @@
 from pathlib import Path
 from types import MappingProxyType
 
-# do not use yarl.quote directly,
+# do not use yarl.quote/unquote directly,
 # use `URL(path).raw_path` instead of `quote(path)`
 # Escaping of the URLs need to be consitent with the escaping done by yarl
-from yarl import URL, unquote
+from yarl import URL
 
 from . import hdrs, helpers
 from .abc import AbstractMatchInfo, AbstractRouter, AbstractView
@@ -427,7 +427,7 @@ def _match(self, path):
         if match is None:
             return None
         else:
-            return {key: unquote(value, unsafe='+') for key, value in
+            return {key: URL(value, encoded=True).path for key, value in
                     match.groupdict().items()}
 
     def get_info(self):
@@ -559,7 +559,8 @@ def resolve(self, request):
         if method not in allowed_methods:
             return None, allowed_methods
 
-        match_dict = {'filename': unquote(path[len(self._prefix)+1:])}
+        match_dict = {'filename': URL(path[len(self._prefix)+1:],
+                                      encoded=True).path}
         return (UrlMappingMatchInfo(match_dict, self._routes[method]),
                 allowed_methods)
         yield  # pragma: no cover
@@ -572,7 +573,7 @@ def __iter__(self):
 
     @asyncio.coroutine
     def _handle(self, request):
-        filename = unquote(request.match_info['filename'])
+        filename = request.match_info['filename']
         try:
             filepath = self._directory.joinpath(filename).resolve()
             if not self._follow_symlinks:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -62,8 +62,8 @@ def build_extension(self, ext):
         raise RuntimeError('Unable to determine version.')
 
 
-install_requires = ['chardet', 'multidict>=3.0.0',
-                    'async_timeout>=1.2.0', 'yarl>=0.11']
+install_requires = ['chardet', 'multidict>=4.0.0',
+                    'async_timeout>=1.2.0', 'yarl>=1.0.0']
 
 
 def read(f):


Test patch: