|
1 | | -"""This module has a full support for :mod:`asyncio` that enables developers to |
2 | | -perform asynchronous additional requests inside of Page Objects. |
3 | | -
|
4 | | -Note that the implementation to fully execute any :class:`~.Request` is not |
5 | | -handled in this module. With that, the framework using **web-poet** must supply |
6 | | -the implementation. |
7 | | -
|
8 | | -You can read more about this in the :ref:`advanced-downloader-impl` documentation. |
9 | | -""" |
10 | | - |
11 | | -import asyncio |
12 | 1 | import logging |
13 | 2 | from contextvars import ContextVar |
14 | | -from typing import Optional, List, Callable, Union, Dict |
15 | | - |
16 | | -import attrs |
17 | 3 |
|
18 | | -from web_poet.page_inputs import ( |
19 | | - HttpResponse, |
| 4 | +from web_poet.exceptions import RequestBackendError |
| 5 | +from web_poet.page_inputs.http import ( |
20 | 6 | HttpRequest, |
21 | | - HttpRequestHeaders, |
22 | | - HttpRequestBody, |
| 7 | + HttpResponse, |
23 | 8 | ) |
24 | | -from web_poet.exceptions import RequestBackendError |
25 | 9 |
|
26 | 10 | logger = logging.getLogger(__name__) |
27 | 11 |
|
28 | | -_StrMapping = Dict[str, str] |
29 | | -_Headers = Union[_StrMapping, HttpRequestHeaders] |
30 | | -_Body = Union[bytes, HttpRequestBody] |
31 | | - |
32 | | - |
33 | 12 | # Frameworks that wants to support additional requests in ``web-poet`` should |
34 | 13 | # set the appropriate implementation for requesting data. |
35 | 14 | request_backend_var: ContextVar = ContextVar("request_backend") |
@@ -60,99 +39,3 @@ async def _perform_request(request: HttpRequest) -> HttpResponse: |
60 | 39 |
|
61 | 40 | response_data: HttpResponse = await request_backend(request) |
62 | 41 | return response_data |
63 | | - |
64 | | - |
65 | | -class HttpClient: |
66 | | - """A convenient client to easily execute requests. |
67 | | -
|
68 | | - By default, it uses the request implementation assigned in the |
69 | | - ``web_poet.request_backend_var`` which is a :mod:`contextvars` instance to |
70 | | - download the actual requests. However, it can easily be overridable by |
71 | | - providing an optional ``request_downloader`` callable. |
72 | | -
|
73 | | - Providing the request implementation by dependency injection would be a good |
74 | | - alternative solution when you want to avoid setting up :mod:`contextvars` |
75 | | - like ``web_poet.request_backend_var``. |
76 | | -
|
77 | | - In any case, this doesn't contain any implementation about how to execute |
78 | | - any requests fed into it. When setting that up, make sure that the downloader |
79 | | - implementation returns a :class:`~.HttpResponse` instance. |
80 | | - """ |
81 | | - |
82 | | - def __init__(self, request_downloader: Callable = None): |
83 | | - self._request_downloader = request_downloader or _perform_request |
84 | | - |
85 | | - async def request( |
86 | | - self, |
87 | | - url: str, |
88 | | - *, |
89 | | - method: str = "GET", |
90 | | - headers: Optional[_Headers] = None, |
91 | | - body: Optional[_Body] = None, |
92 | | - ) -> HttpResponse: |
93 | | - """This is a shortcut for creating a :class:`~.HttpRequest` instance and executing |
94 | | - that request. |
95 | | -
|
96 | | - A :class:`~.HttpResponse` instance should then be returned. |
97 | | -
|
98 | | - .. warning:: |
99 | | - By convention, the request implementation supplied optionally to |
100 | | - :class:`~.HttpClient` should return a :class:`~.HttpResponse` instance. |
101 | | - However, the underlying implementation supplied might change that, |
102 | | - depending on how the framework using **web-poet** implements it. |
103 | | - """ |
104 | | - headers = headers or {} |
105 | | - body = body or b"" |
106 | | - req = HttpRequest(url=url, method=method, headers=headers, body=body) |
107 | | - return await self.execute(req) |
108 | | - |
109 | | - async def get(self, url: str, *, headers: Optional[_Headers] = None) -> HttpResponse: |
110 | | - """Similar to :meth:`~.HttpClient.request` but peforming a ``GET`` |
111 | | - request. |
112 | | - """ |
113 | | - return await self.request(url=url, method="GET", headers=headers) |
114 | | - |
115 | | - async def post( |
116 | | - self, |
117 | | - url: str, |
118 | | - *, |
119 | | - headers: Optional[_Headers] = None, |
120 | | - body: Optional[_Body] = None, |
121 | | - ) -> HttpResponse: |
122 | | - """Similar to :meth:`~.HttpClient.request` but performing a ``POST`` |
123 | | - request. |
124 | | - """ |
125 | | - return await self.request(url=url, method="POST", headers=headers, body=body) |
126 | | - |
127 | | - async def execute(self, request: HttpRequest) -> HttpResponse: |
128 | | - """Accepts a single instance of :class:`~.HttpRequest` and executes it |
129 | | - using the request implementation configured in the :class:`~.HttpClient` |
130 | | - instance. |
131 | | -
|
132 | | - This returns a single :class:`~.HttpResponse`. |
133 | | - """ |
134 | | - return await self._request_downloader(request) |
135 | | - |
136 | | - async def batch_execute( |
137 | | - self, *requests: HttpRequest, return_exceptions: bool = False |
138 | | - ) -> List[Union[HttpResponse, Exception]]: |
139 | | - """Similar to :meth:`~.HttpClient.execute` but accepts a collection of |
140 | | - :class:`~.HttpRequest` instances that would be batch executed. |
141 | | -
|
142 | | - The order of the :class:`~.HttpResponses` would correspond to the order |
143 | | - of :class:`~.HttpRequest` passed. |
144 | | -
|
145 | | - If any of the :class:`~.HttpRequest` raises an exception upon execution, |
146 | | - the exception is raised. |
147 | | -
|
148 | | - To prevent this, the actual exception can be returned alongside any |
149 | | - successful :class:`~.HttpResponse`. This enables salvaging any usable |
150 | | - responses despite any possible failures. This can be done by setting |
151 | | - ``True`` to the ``return_exceptions`` parameter. |
152 | | - """ |
153 | | - |
154 | | - coroutines = [self._request_downloader(r) for r in requests] |
155 | | - responses = await asyncio.gather( |
156 | | - *coroutines, return_exceptions=return_exceptions |
157 | | - ) |
158 | | - return responses |
0 commit comments