Skip to content

Commit

Permalink
Add a base_url option to ClientSession (#6129)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Svetlov <andrew.svetlov@gmail.com>
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
4 people committed Oct 31, 2021
1 parent 0854cd7 commit 59bbcac
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES/6013.feature
@@ -0,0 +1 @@
Added ``base_url`` parameter to the initializer of :class:`~aiohttp.ClientSession`.
21 changes: 19 additions & 2 deletions aiohttp/client.py
Expand Up @@ -166,6 +166,7 @@ class ClientSession:

ATTRS = frozenset(
[
"_base_url",
"_source_traceback",
"_connector",
"requote_redirect_url",
Expand Down Expand Up @@ -194,6 +195,7 @@ class ClientSession:

def __init__(
self,
base_url: Optional[StrOrURL] = None,
*,
connector: Optional[BaseConnector] = None,
loop: Optional[asyncio.AbstractEventLoop] = None,
Expand All @@ -218,13 +220,20 @@ def __init__(
trace_configs: Optional[List[TraceConfig]] = None,
read_bufsize: int = 2 ** 16,
) -> None:

if loop is None:
if connector is not None:
loop = connector._loop

loop = get_running_loop(loop)

if base_url is None or isinstance(base_url, URL):
self._base_url: Optional[URL] = base_url
else:
self._base_url = URL(base_url)
assert (
self._base_url.origin() == self._base_url
), "Only absolute URLs without path part are supported"

if connector is None:
connector = TCPConnector(loop=loop)

Expand Down Expand Up @@ -343,6 +352,14 @@ def request(
"""Perform HTTP request."""
return _RequestContextManager(self._request(method, url, **kwargs))

def _build_url(self, str_or_url: StrOrURL) -> URL:
url = URL(str_or_url)
if self._base_url is None:
return url
else:
assert not url.is_absolute() and url.path.startswith("/")
return self._base_url.join(url)

async def _request(
self,
method: str,
Expand Down Expand Up @@ -402,7 +419,7 @@ async def _request(
proxy_headers = self._prepare_headers(proxy_headers)

try:
url = URL(str_or_url)
url = self._build_url(str_or_url)
except ValueError as e:
raise InvalidURL(str_or_url) from e

Expand Down
12 changes: 12 additions & 0 deletions docs/client_quickstart.rst
Expand Up @@ -55,6 +55,18 @@ Other HTTP methods are available as well::
session.options('http://httpbin.org/get')
session.patch('http://httpbin.org/patch', data=b'data')

To make several requests to the same site more simple, the parameter ``base_url``
of :class:`ClientSession` constructor can be used. For example to request different
endpoints of ``http://httpbin.org`` can be used the following code::

async with aiohttp.ClientSession('http://httpbin.org') as session:
async with session.get('/get'):
pass
async with session.post('/post', data=b'data'):
pass
async with session.put('/put', data=b'data'):
pass

.. note::

Don't create a session per request. Most likely you need a session
Expand Down
8 changes: 7 additions & 1 deletion docs/client_reference.rst
Expand Up @@ -38,7 +38,8 @@ Usage example::

The client session supports the context manager protocol for self closing.

.. class:: ClientSession(*, connector=None, loop=None, cookies=None, \
.. class:: ClientSession(base_url=None, *, \
connector=None, cookies=None, \
headers=None, skip_auto_headers=None, \
auth=None, json_serialize=json.dumps, \
version=aiohttp.HttpVersion11, \
Expand All @@ -56,6 +57,11 @@ The client session supports the context manager protocol for self closing.
The class for creating client sessions and making requests.


:param base_url: Base part of the URL (optional)
If set it allows to skip the base part in request calls.

.. versionadded:: 3.8

:param aiohttp.BaseConnector connector: BaseConnector
sub-class instance to support connection pooling.

Expand Down
36 changes: 36 additions & 0 deletions tests/test_client_session.py
Expand Up @@ -862,3 +862,39 @@ async def test_requote_redirect_setter() -> None:
session.requote_redirect_url = False
assert not session.requote_redirect_url
await session.close()


@pytest.mark.parametrize(
("base_url", "url", "expected_url"),
[
pytest.param(
None,
"http://example.com/test",
URL("http://example.com/test"),
id="base_url=None url='http://example.com/test'",
),
pytest.param(
None,
URL("http://example.com/test"),
URL("http://example.com/test"),
id="base_url=None url=URL('http://example.com/test')",
),
pytest.param(
"http://example.com",
"/test",
URL("http://example.com/test"),
id="base_url='http://example.com' url='/test'",
),
pytest.param(
URL("http://example.com"),
"/test",
URL("http://example.com/test"),
id="base_url=URL('http://example.com') url='/test'",
),
],
)
async def test_build_url_returns_expected_url(
create_session, base_url, url, expected_url
) -> None:
session = await create_session(base_url)
assert session._build_url(url) == expected_url

0 comments on commit 59bbcac

Please sign in to comment.