Repository: aio-libs/aiohttp

Issue:
Discourage ClientSession.requote_redirect_url changing
The property setting affects all upcoming requests, which looks dangerous.

Better to make it read-only property at least with setting by client session constructor only.
Dropping the property in aiohttp 4.0 is even better: user always can pass `yarl.URL()` instance with `encoded=True`.

On transition period we should convert the `client.requote_redirect_url` into property.
Next step is varies upon our decision.
If we want to drop it: raise the warning and wait for aiohttp 4.0.
If we will decide to keep the functionality: 
- add `requote_redirect_url` parameters to both `ClientSession` constructor and `client.request` and family.
- raise a warning on modification of `requote_redirect_url` property.
- Finally disable the property modification in aiohttp 4.0

@fafhrd91 you are the author of these lines. What do you prefer?


Discussion:
I would remove this property, but problem is you can not use "encoded=True" during redirect.
I am fine with property
Aha, got you.
If I understood correctly aiohttp internals should use `encoded=True` for redirection URLs.
This change will close all redirection problems, we don't need the property anymore (but will support it with a deprecation warning up to aiohttp 4.0) 
Do you remember, re-quoting for redirect was the reason for yarl in first place. In most cases we need re-quote, but sometimes we don’t 
If we need it *sometimes* maybe better to add `requote_redirect_url=False` argument to `session.request`?

Resolving patch:
diff --git a/aiohttp/client.py b/aiohttp/client.py
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -98,8 +98,6 @@ class ClientSession:
     _source_traceback = None
     _connector = None
 
-    requote_redirect_url = True
-
     def __init__(self, *, connector: Optional[BaseConnector]=None,
                  loop: Optional[asyncio.AbstractEventLoop]=None,
                  cookies: Optional[LooseCookies]=None,
@@ -119,6 +117,7 @@ def __init__(self, *, connector: Optional[BaseConnector]=None,
                  timeout: Union[object, ClientTimeout]=sentinel,
                  auto_decompress: bool=True,
                  trust_env: bool=False,
+                 requote_redirect_url: bool=True,
                  trace_configs: Optional[List[TraceConfig]]=None) -> None:
 
         if loop is None:
@@ -171,6 +170,7 @@ def __init__(self, *, connector: Optional[BaseConnector]=None,
         self._raise_for_status = raise_for_status
         self._auto_decompress = auto_decompress
         self._trust_env = trust_env
+        self._requote_redirect_url = requote_redirect_url
 
         # Convert to list of tuples
         if headers:
@@ -468,7 +468,7 @@ async def _request(
 
                         try:
                             r_url = URL(
-                                r_url, encoded=not self.requote_redirect_url)
+                                r_url, encoded=not self._requote_redirect_url)
 
                         except ValueError:
                             raise InvalidURL(r_url)
@@ -841,6 +841,20 @@ def version(self) -> Tuple[int, int]:
         """The session HTTP protocol version."""
         return self._version
 
+    @property
+    def requote_redirect_url(self) -> bool:
+        """Do URL requoting on redirection handling."""
+        return self._requote_redirect_url
+
+    @requote_redirect_url.setter
+    def requote_redirect_url(self, val: bool) -> None:
+        """Do URL requoting on redirection handling."""
+        warnings.warn("session.requote_redirect_url modification "
+                      "is deprecated #2778",
+                      DeprecationWarning,
+                      stacklevel=2)
+        self._requote_redirect_url = val
+
     @property
     def loop(self) -> asyncio.AbstractEventLoop:
         """Session's loop."""


Test patch:
diff --git a/tests/test_client_session.py b/tests/test_client_session.py
--- a/tests/test_client_session.py
+++ b/tests/test_client_session.py
@@ -687,3 +687,21 @@ async def test_client_session_timeout_args(loop) -> None:
         ClientSession(loop=loop,
                       timeout=client.ClientTimeout(total=10 * 60),
                       conn_timeout=30 * 60)
+
+
+async def test_requote_redirect_url_default() -> None:
+    session = ClientSession()
+    assert session.requote_redirect_url
+
+
+async def test_requote_redirect_url_default_disable() -> None:
+    session = ClientSession(requote_redirect_url=False)
+    assert not session.requote_redirect_url
+
+
+async def test_requote_redirect_setter() -> None:
+    session = ClientSession()
+    assert session.requote_redirect_url
+    with pytest.warns(DeprecationWarning):
+        session.requote_redirect_url = False
+    assert not session.requote_redirect_url